├── .gitattributes ├── .gitignore ├── .idea ├── .gitignore ├── YeIM-Uni-SDK.iml ├── modules.xml └── vcs.xml ├── LICENSE ├── README.md ├── babel.config.js ├── dist └── yeim-uni-sdk.min.js ├── package-lock.json ├── package.json ├── rollup.config.js └── src ├── const ├── yeim-defines.js └── yeim-status-code.js ├── func ├── callback.js ├── common.js ├── event.js ├── formatMessage.js ├── log.js ├── request.js ├── storage.js └── websocket.js ├── service ├── conversationService.js ├── friendService.js ├── groupService.js ├── messageService.js ├── pushService.js ├── uploadService.js └── userService.js ├── utils ├── CryptoJS.js ├── fetch.js ├── md5.js ├── mitt.umd.js └── queryParams.js ├── yeim-uni-sdk.js └── yeim-uni-sdk.min.d.ts /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .idea 3 | node_modules -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | # Editor-based HTTP Client requests 5 | /httpRequests/ 6 | -------------------------------------------------------------------------------- /.idea/YeIM-Uni-SDK.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![微信公众号.png](https://s2.loli.net/2024/03/31/MHE1CD3WtP2nUOQ.jpg) 2 | 3 |

4 | YeIM-Uni-SDK 5 |
6 |
7 | 即时通讯JSSDK 8 |
9 |
10 | 查看文档 11 | | 备用文档 12 |
13 | 14 |

15 | 16 | `此为社区开源项目,用于学习交流使用,禁止用于非法途径。如作他用所造成的一切法律责任均不由作者承担。` 17 | 18 | - 注:不是聊天项目!不是聊天项目!不是聊天项目!只是SDK,通过使用SDK里的一系列接口可实现聊天,跟环信、融云、腾讯云即时通信等等类似的一种可实现聊天的IMSDK。 19 | 20 | `YeIM-Uni-SDK`是可以`私有化部署`的`全开源`即时通讯`js-sdk`,仅需集成 SDK 即可轻松实现聊天能力,支持Web、[uni-app](https://uniapp.dcloud.net.cn/)接入使用,满足通信需要。 21 | 22 | 支持私聊和群聊,支持发送的消息类型:文字消息、图片消息、语音消息、视频消息、位置消息、自定义消息。 23 | 24 | 必须搭配[YeIM-Uni-Server](https://github.com/wzJun1/YeIM-Uni-Server)服务端,开箱即用。 25 | 26 | ## 支持哪些端? 27 | 28 | `YeIM-Uni-SDK`由`JavaScript`构建,支持Web端、uni-app端的项目接入使用。 29 | 30 | 当项目在uni环境打包,SDK将使用`uni-app API`,否则使用JS相关API,详情可查看源码。 31 | 32 | 作为通用JSSDK,`YeIM-Uni-SDK`支持包括不限于H5(uni和非uni环境均可)、Android APP、iOS APP、微信小程序、字节小程序、支付宝小程序、百度小程序(仅限uni环境)等平台项目。 33 | 34 | ## 使用文档 35 | 36 | 查看文档 37 | | 备用文档 38 | 39 | ## 反馈与共建 40 | 41 | - 普通交流QQ群:[391276294](https://qm.qq.com/cgi-bin/qm/qr?k=hEQnVRj3c1B0gDpD2QJrD7UIfWMzCUuM&jump_from=webapi&authKey=kbrD7NHXGIPaiVb2puw+vJeRCIQSXVhIci7eFvFLBH/UjGt+hrdOk4upK731S+1+) 42 | 43 | ## 源码编译 44 | 45 | ```shell 46 | npm i 47 | ``` 48 | 49 | ```shell 50 | npm run build 51 | ``` 52 | 53 | ## 基于YeIM-Uni-SDK的演示案例 54 | 55 | ### 代码 56 | 57 | [下载基于YeIM-Uni-SDK的演示案例源码](https://ext.dcloud.net.cn/plugin?id=10266) 58 | 59 | ### Android App Demo 60 | 61 | 62 | 63 | ### Electron 桌面端 Demo 64 | 65 | [https://github.com/wzJun1/YeIM-Uni-SDK-Electron-Demo](https://github.com/wzJun1/YeIM-Uni-SDK-Electron-Demo) 66 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "presets": [ 3 | [ 4 | "@babel/preset-env", 5 | { 6 | "modules": false 7 | } 8 | ] 9 | ], 10 | "plugins": [ 11 | [ 12 | "@babel/plugin-transform-runtime" 13 | ] 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "yeim-uni-sdk", 3 | "version": "1.2.83", 4 | "description": "可私有化部署的全开源即时通讯uni-app js-sdk,仅需植入 sdk 即可轻松集成聊天能力,支持Web、uni-app项目接入使用,满足通信需要。", 5 | "scripts": { 6 | "dev": "rollup -w -c --bundleConfigAsCjs", 7 | "build": "rollup -c --bundleConfigAsCjs", 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "author": "wzjun1", 11 | "license": "Apache-2.0", 12 | "types": "src/yeim-uni-sdk.min.d.ts", 13 | "main": "dist/yeim-uni-sdk.min.js", 14 | "module": "dist/yeim-uni-sdk.min.js", 15 | "devDependencies": { 16 | "@babel/core": "^7.21.3", 17 | "@babel/plugin-transform-runtime": "^7.21.0", 18 | "@babel/preset-env": "^7.20.2", 19 | "@rollup/plugin-babel": "^6.0.3", 20 | "@rollup/plugin-commonjs": "^24.0.1", 21 | "@rollup/plugin-json": "^6.0.0", 22 | "@rollup/plugin-node-resolve": "^15.0.1", 23 | "@rollup/plugin-terser": "^0.4.0" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | import resolve from '@rollup/plugin-node-resolve' 3 | import commonjs from '@rollup/plugin-commonjs' 4 | import terser from "@rollup/plugin-terser"; 5 | import babel from "@rollup/plugin-babel"; 6 | import json from "@rollup/plugin-json"; 7 | const env = process.env.NODE_ENV 8 | const config = { 9 | input: path.resolve(__dirname, 'src/yeim-uni-sdk.js'), 10 | output: { 11 | file: './dist/yeim-uni-sdk.min.js', 12 | format: 'es', 13 | name: 'YeIMUniSDK', 14 | }, 15 | plugins: [ 16 | resolve(), 17 | commonjs(), 18 | json(), 19 | babel({ babelHelpers: 'runtime' }) 20 | ] 21 | } 22 | 23 | config.plugins.push(terser({ 24 | compress: { 25 | pure_getters: true, unsafe: true, unsafe_comps: true, warnings: false 26 | } 27 | })) 28 | 29 | if (env === 'production') { 30 | config.plugins.push(terser({ 31 | compress: { 32 | pure_getters: true, unsafe: true, unsafe_comps: true, warnings: false 33 | } 34 | })) 35 | } 36 | 37 | export default config 38 | -------------------------------------------------------------------------------- /src/const/yeim-defines.js: -------------------------------------------------------------------------------- 1 | //SDK常量 2 | 3 | var YeIMUniSDKDefines = {}; 4 | 5 | //事件类型 6 | YeIMUniSDKDefines.EVENT = {}; 7 | YeIMUniSDKDefines.EVENT.NET_CHANGED = "net_changed"; //网络状态变化 8 | YeIMUniSDKDefines.EVENT.CONVERSATION_LIST_CHANGED = "conversation_list_changed"; // 会话列表变化 9 | YeIMUniSDKDefines.EVENT.MESSAGE_RECEIVED = "message_received"; // 收到消息 10 | YeIMUniSDKDefines.EVENT.MESSAGE_REVOKED = "message_revoked"; // 撤回消息 11 | YeIMUniSDKDefines.EVENT.PRIVATE_READ_RECEIPT = "private_read_receipt"; //私聊会话已读回执 12 | YeIMUniSDKDefines.EVENT.FRIEND_LIST_CHANGED = "friend_list_changed"; //好友列表变化 13 | YeIMUniSDKDefines.EVENT.FRIEND_APPLY_LIST_CHANGED = "friend_apply_list_changed"; //好友申请列表变化 14 | YeIMUniSDKDefines.EVENT.FRIEND_APPLY_REFUSE = "friend_apply_refuse"; //好友申请被拒 15 | YeIMUniSDKDefines.EVENT.KICKED_OUT = "kicked_out"; //用户被踢下线 16 | 17 | //会话类型 18 | YeIMUniSDKDefines.CONVERSATION_TYPE = {}; 19 | YeIMUniSDKDefines.CONVERSATION_TYPE.PRIVATE = 'private'; //私聊 20 | YeIMUniSDKDefines.CONVERSATION_TYPE.GROUP = 'group'; //群聊 21 | 22 | //消息类型 23 | YeIMUniSDKDefines.MESSAGE_TYPE = {}; 24 | YeIMUniSDKDefines.MESSAGE_TYPE.TEXT = 'text'; //文本消息 25 | YeIMUniSDKDefines.MESSAGE_TYPE.TEXT_AT = 'text_at'; //文本@消息 26 | YeIMUniSDKDefines.MESSAGE_TYPE.IMAGE = 'image'; //图片消息 27 | YeIMUniSDKDefines.MESSAGE_TYPE.AUDIO = 'audio'; //语音消息 28 | YeIMUniSDKDefines.MESSAGE_TYPE.VIDEO = 'video'; //小视频消息 29 | YeIMUniSDKDefines.MESSAGE_TYPE.LOCATION = 'location'; //位置消息 30 | YeIMUniSDKDefines.MESSAGE_TYPE.CUSTOM = 'custom'; //自定义消息 31 | YeIMUniSDKDefines.MESSAGE_TYPE.MERGER = 'merger'; //合并消息 32 | YeIMUniSDKDefines.MESSAGE_TYPE.FORWARD = 'forward'; //转发消息 33 | YeIMUniSDKDefines.MESSAGE_TYPE.SYS_NOTICE = 'sys_notice'; //私聊系统通知 34 | YeIMUniSDKDefines.MESSAGE_TYPE.GROUP_SYS_NOTICE = 'group_sys_notice'; //群聊系统通知 35 | 36 | 37 | //用户 38 | YeIMUniSDKDefines.USER = {}; 39 | 40 | //加好友验证方式 41 | YeIMUniSDKDefines.USER.ADDFRIEND = {}; 42 | YeIMUniSDKDefines.USER.ADDFRIEND.ALLOW = 1; //允许任何人添加自己为好友 43 | YeIMUniSDKDefines.USER.ADDFRIEND.CONFIRM = 2; //需要经过自己确认才能添加自己为好友 44 | YeIMUniSDKDefines.USER.ADDFRIEND.DENY = 3; //拒绝加好友 45 | 46 | //群组 47 | YeIMUniSDKDefines.GROUP = {}; 48 | 49 | //群申请处理方式 50 | YeIMUniSDKDefines.GROUP.JOINMODE = {}; 51 | YeIMUniSDKDefines.GROUP.JOINMODE.FREE = 1; //自有加入,不需要申请和审核,不需要被邀请人允许。 52 | YeIMUniSDKDefines.GROUP.JOINMODE.CHECK = 2; //验证加入,需要申请,以及群主或管理员的同意才能入群 53 | YeIMUniSDKDefines.GROUP.JOINMODE.FORBIDDEN = 3; //禁止加入 54 | 55 | //入群申请处理结果 56 | YeIMUniSDKDefines.GROUP.APPLYSTATUS = {}; 57 | YeIMUniSDKDefines.GROUP.APPLYSTATUS.PENDING = 1; //待处理 58 | YeIMUniSDKDefines.GROUP.APPLYSTATUS.AGREE = 2; //同意 59 | YeIMUniSDKDefines.GROUP.APPLYSTATUS.REFUSE = 3; //拒绝 60 | 61 | export { 62 | YeIMUniSDKDefines 63 | }; 64 | -------------------------------------------------------------------------------- /src/const/yeim-status-code.js: -------------------------------------------------------------------------------- 1 | //客户端状态码 2 | 3 | var YeIMUniSDKStatusCode = { 4 | NORMAL_SUCCESS: { 5 | code: 200, 6 | describe: '接口调用成功' //接口调用成功的统一状态码 7 | }, 8 | APPLY_NEED: { 9 | code: 20020, 10 | describe: '好友申请已发送,请等待对方处理' 11 | }, 12 | NORMAL_ERROR: { 13 | code: 500, 14 | describe: '未知错误' //未知错误的统一状态码 15 | }, 16 | SDK_PARAMS_ERROR: { 17 | code: 9001, 18 | describe: 'YeIMUniSDK 初始化失败,请检查参数是否存在异常' 19 | }, 20 | CONNECT_ERROR: { 21 | code: 10001, 22 | describe: '聊天服务网络连接错误' 23 | }, 24 | NO_USERID: { 25 | code: 10002, 26 | describe: '用户验证失败,请检查userId、token' 27 | }, 28 | LOGIN_EXPIRE: { 29 | code: 10003, 30 | describe: '用户登录状态过期,请重新登录' 31 | }, 32 | NO_CONVERSATION: { 33 | code: 10004, 34 | describe: '未找到此会话' 35 | }, 36 | PARAMS_ERROR: { 37 | code: 10008, 38 | describe: '请求参数错误' 39 | }, 40 | LOGIN_ERROR: { 41 | code: 10103, 42 | describe: '用户登录失败,请稍后再试' 43 | }, 44 | GROUP_APPLY_REPEAT: { 45 | code: 10251, 46 | describe: '此用户已在当前群组,请勿重复加入' 47 | }, 48 | GROUP_APPLY_ERROR: { 49 | code: 10252, 50 | describe: '申请入群异常,请稍后重试' 51 | }, 52 | GROUP_APPLY_WAIT: { 53 | code: 10253, 54 | describe: '已发送入群申请,请等待审核' 55 | }, 56 | UPLOAD_ERROR: { 57 | code: 10300, 58 | describe: '本地上传失败,请检查配置' 59 | }, 60 | DOWNLOAD_ERROR: { 61 | code: 10301, 62 | describe: '文件下载失败' 63 | }, 64 | COS_UPLOAD_ERROR: { 65 | code: 10302, 66 | describe: '腾讯云COS上传文件失败,请检查配置是否填写正确' 67 | }, 68 | COS_DOWNLOAD_ERROR_1: { 69 | code: 10303, 70 | describe: '腾讯云媒体截图接口:下载视频缩略图失败,请检查网络或开启万象功能' 71 | }, 72 | OSS_UPLOAD_ERROR: { 73 | code: 10304, 74 | describe: '阿里云OSS上传文件失败,请检查配置是否填写正确' 75 | }, 76 | }; 77 | 78 | export { 79 | YeIMUniSDKStatusCode 80 | }; 81 | -------------------------------------------------------------------------------- /src/func/callback.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 格式化成功对象 3 | */ 4 | function buildSuccessObject(code = 200, message, data = null) { 5 | return { 6 | code: code, 7 | message: message, 8 | data: data 9 | } 10 | } 11 | 12 | /** 13 | * 格式化失败对象 14 | */ 15 | function buildErrObject(code = 500, message, data = null) { 16 | return { 17 | code: code, 18 | message: message, 19 | data: data 20 | } 21 | } 22 | 23 | /** 24 | * 参数函数成功回调 25 | * 26 | * @param {Object} options 包含成功回调函数参数的对象 27 | * @param {Function} [options.success] - 成功回调 28 | * @param {String} message 消息提示 29 | * @param {Object} data 回调参数 30 | * @param {Number} code 状态码 31 | */ 32 | function successHandle(options, message, data = null, code = 200) { 33 | try { 34 | if (options != null && options.success !== undefined && typeof options.success === "function") { 35 | options.success(buildSuccessObject(code, message, data)); 36 | } 37 | } catch (e) { 38 | console.error(e) 39 | } 40 | } 41 | 42 | /** 43 | * 参数函数失败回调 44 | * 45 | * @param {Object} options 包含失败回调函数参数的对象 46 | * @param {Function} [options.fail] - 失败回调 47 | * @param {Number} code 状态码 48 | * @param {String} message 消息提示 49 | * @param {Object} data 回调参数 50 | */ 51 | function errHandle(options, code = 500, message, data = null) { 52 | try { 53 | if (options != null && options.fail !== undefined && typeof options.fail === "function") { 54 | options.fail(buildErrObject(code, message, data)); 55 | } 56 | } catch (e) { 57 | console.error(e) 58 | } 59 | } 60 | 61 | export { 62 | buildSuccessObject, 63 | buildErrObject, 64 | successHandle, 65 | errHandle 66 | } 67 | -------------------------------------------------------------------------------- /src/func/common.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * 判断链接是否为h5 object url 4 | * 5 | * @param {*} url 6 | * @returns 7 | */ 8 | function isBlobURL(url) { 9 | return url.startsWith('blob:'); 10 | } 11 | 12 | /** 13 | * 14 | * 判断是否为网络链接 15 | * 16 | * @param {*} url 17 | * @returns 18 | */ 19 | function isHttpURL(url) { 20 | // 先判断是否是微信的临时链接 21 | if (url.indexOf('http://tmp/') === 0) { 22 | return false; 23 | } 24 | return url.startsWith('http:') || url.startsWith('https:') || url.startsWith('ftp:'); 25 | } 26 | 27 | export { 28 | isBlobURL, 29 | isHttpURL 30 | } 31 | -------------------------------------------------------------------------------- /src/func/event.js: -------------------------------------------------------------------------------- 1 | import { 2 | instance 3 | } from "../yeim-uni-sdk"; 4 | 5 | /** 6 | * 触发事件 7 | * @param {Object} event 8 | * @param {Object} data 9 | */ 10 | function emit(event, data) { 11 | instance.emitter.emit(instance.defaults.eventPrefix + event, data) 12 | } 13 | 14 | /** 15 | * 添加事件监听器 16 | * @param {Object} event 17 | * @param {Object} callback 18 | */ 19 | function addEventListener(event, callback) { 20 | if (event) { 21 | instance.emitter.on(instance.defaults.eventPrefix + event, callback); 22 | } 23 | } 24 | 25 | /** 26 | * 移除事件监听器 27 | * @param {Object} event 28 | * @param {Object} callback 29 | */ 30 | function removeEventListener(event, callback) { 31 | if (event) { 32 | instance.emitter.off(instance.defaults.eventPrefix + event, callback); 33 | } 34 | } 35 | 36 | export { 37 | emit, 38 | addEventListener, 39 | removeEventListener 40 | }; 41 | -------------------------------------------------------------------------------- /src/func/formatMessage.js: -------------------------------------------------------------------------------- 1 | import { 2 | instance 3 | } from "../yeim-uni-sdk"; 4 | 5 | /** 6 | * 生成消息ID 7 | */ 8 | // function createMessageId() { 9 | // if (!instance.nextIdGen) { 10 | // log(1, "分布式消息ID异常,请检查后端代码"); 11 | // return null; 12 | // } 13 | // return instance.nextIdGen + "-" + (new Date()).getTime(); 14 | // } 15 | 16 | /** 17 | * 统一消息格式 18 | * @param {Object} params 19 | */ 20 | function formatMessage(params) { 21 | return { 22 | // 取消本地生成messageId,改为服务端统一调配 23 | // messageId: createMessageId(), 24 | conversationId: params.to, //会话ID 25 | conversationType: params.conversationType, //会话类型 26 | fromUserInfo: { 27 | nickname: instance.user.nickname, 28 | avatarUrl: instance.user.avatarUrl 29 | }, // 发送者用户信息 30 | from: instance.userId, //发送者ID 31 | to: params.to, //接收者ID 32 | type: params.type, //消息内容类型 33 | isRead: 0, //是否已读 34 | isRevoke: 0, //是否撤回 35 | isDeleted: 0, //是否删除 36 | status: 'unSend', //消息状态 37 | time: (new Date()).getTime(), //发送时间戳 38 | body: params.body, //消息体 39 | extra: params.extra ? params.extra : "" //自定义扩展数据 40 | } 41 | } 42 | 43 | export default formatMessage 44 | -------------------------------------------------------------------------------- /src/func/log.js: -------------------------------------------------------------------------------- 1 | import { 2 | instance 3 | } from "../yeim-uni-sdk"; 4 | /** 5 | * 日志输出 6 | * logLevel 7 | * 0 普通日志,日志量较多,接入时建议使用 8 | * 1 关键性日志,日志量较少,生产环境时建议使用 9 | * 2 无日志级别,SDK 将不打印任何日志 10 | * @param {Object} level 11 | * @param {Object} msg 12 | * @param {Boolean} error 13 | */ 14 | function log(level, msg, error = false) { 15 | if (instance.defaults.logLevel == 0) { 16 | error ? console.error("【YeIMUniSDK Log】", msg) : console.log("【YeIMUniSDK Log】", msg) 17 | } else if (instance.defaults.logLevel == 2) { 18 | return; 19 | } else { 20 | if (level == 1) { 21 | error ? console.error("【YeIMUniSDK Log】", msg) : console.log("【YeIMUniSDK Log】", msg) 22 | } 23 | } 24 | } 25 | 26 | export default log 27 | -------------------------------------------------------------------------------- /src/func/request.js: -------------------------------------------------------------------------------- 1 | /*eslint-disable*/ 2 | 3 | import { 4 | YeIMUniSDKStatusCode 5 | } from '../const/yeim-status-code'; 6 | import { 7 | fetch 8 | } from '../utils/fetch'; 9 | import queryParams from '../utils/queryParams'; 10 | import { 11 | isBlobURL 12 | } from '../func/common.js'; 13 | import { 14 | instance 15 | } from '../yeim-uni-sdk'; 16 | import { 17 | errHandle, 18 | successHandle 19 | } from './callback'; 20 | import log from './log'; 21 | 22 | /** 23 | * APIURL 24 | */ 25 | let Api = { 26 | //用户相关接口 27 | User: { 28 | //根据userId查询用户资料 29 | fetchUserInfoById: '/user/info', 30 | //用户更新资料 31 | updateUserInfo: '/user/update', 32 | //黑名单列表 33 | getBlackUserList: '/user/black/list', 34 | //添加到黑名单 35 | addToBlackUserList: '/user/black/add', 36 | //移出黑名单 37 | removeFromBlacklist: '/user/black/remove' 38 | }, 39 | //群组相关接口 40 | Group: { 41 | //创建群组 42 | create: '/group/create', 43 | //更新群组 44 | update: '/group/edit', 45 | //解散群组 46 | dissolve: '/group/dissolve', 47 | //通过群ID获取群组资料 48 | fetchGroupInfoById: '/group/get', 49 | //获取当前登录用户群组列表 50 | list: '/group/list', 51 | //转让群组 52 | transferLeader: '/group/transferLeader', 53 | //添加用户入群 54 | addUser: '/group/user/add', 55 | //退出群组 56 | leave: '/group/user/leave', 57 | //移除群成员 58 | remove: '/group/user/delete', 59 | //获取群成员列表 60 | memberList: '/group/user/list', 61 | //设置群管理员 62 | adminstrator: '/group/user/set/adminstrator', 63 | //获取入群申请请求列表 64 | applyList: '/group/user/apply/list', 65 | //处理入群申请 66 | handleApply: '/group/user/apply/change', 67 | //禁言群成员 68 | setMute: '/group/user/set/mute' 69 | }, 70 | //会话相关接口 71 | Conversation: { 72 | //查询用户会话列表 73 | fetchConversationList: '/conversation/list', 74 | //清除私聊会话未读数 75 | clearConversationUnread: '/conversation/update/unread', 76 | //删除会话,将同时删除聊天记录 77 | deleteConversation: '/conversation/delete' 78 | }, 79 | //消息相关接口 80 | Message: { 81 | //通过http发送消息 82 | sendMessage: '/message/save', 83 | //获取历史消息记录 84 | fetchHistoryMessageList: '/v117/message/list', 85 | //删除消息 86 | deleteMessage: '/message/delete', 87 | //撤回消息 88 | revokeMessage: '/message/revoke' 89 | }, 90 | //好友相关接口 91 | Friend: { 92 | //获取好友申请列表 93 | fetchList: '/friend/list', 94 | //获取好友申请列表 95 | fetchApplyList: '/friend/apply/list', 96 | //将全部好友申请设置为已读状态 97 | setRead: '/friend/apply/set/read', 98 | //同意好友申请 99 | acceptApply: '/friend/apply/accept', 100 | //拒绝好友申请 101 | refuseApply: '/friend/apply/refuse', 102 | //添加好友 103 | addFriend: '/friend/add', 104 | //删除好友 105 | deleteFriend: '/friend/delete', 106 | //更新好友资料 107 | updateFriend: '/friend/update' 108 | }, 109 | //推送相关 110 | Push: { 111 | //APP开启离线推送后,绑定个推clientID 112 | bindClientId: '/user/bind/push/id' 113 | }, 114 | //上传相关 115 | Upload: { 116 | //获取上传配置信息 117 | sign: '/upload/sign', 118 | //通用上传 119 | normal: '/upload', 120 | //图片上传 121 | image: '/upload/image', 122 | //视频上传 123 | video: '/upload/video' 124 | } 125 | } 126 | 127 | /** 128 | * 公共请求 129 | * 130 | * 131 | * @param {String} url - 请求路由 132 | * @param {String} method - 请求方法 133 | * @param {Object} data - 请求参数 134 | * 135 | * @return uni.request Promise 136 | * 137 | */ 138 | function request(url, method = 'GET', data = null) { 139 | 140 | return new Promise((resolve, reject) => { 141 | 142 | if (instance.uni) { 143 | uni.request({ 144 | url: instance.defaults.baseURL + url, 145 | data: data, 146 | method: method, 147 | header: { 148 | 'content-type': 'application/json', 149 | 'token': instance.token != null ? instance.token : '' 150 | }, 151 | success: (result) => { 152 | if (result.data == null) { 153 | return reject({ 154 | code: YeIMUniSDKStatusCode.NORMAL_ERROR.code, 155 | message: result.message ? result.message : YeIMUniSDKStatusCode 156 | .NORMAL_ERROR.describe, 157 | data: null 158 | }); 159 | } 160 | result = result.data; 161 | let code = result.code; 162 | if (code === YeIMUniSDKStatusCode.NORMAL_SUCCESS.code) { 163 | resolve(result.data ? result.data : null); 164 | } else { 165 | reject(result); 166 | } 167 | }, 168 | fail: (fail) => { 169 | log(1, fail); 170 | reject({ 171 | code: YeIMUniSDKStatusCode.NORMAL_ERROR.code, 172 | message: JSON.stringify(fail), 173 | data: null 174 | }); 175 | } 176 | }); 177 | } else { 178 | 179 | let options = { 180 | method: method, 181 | body: JSON.stringify(data), 182 | headers: { 183 | "Content-Type": "application/json", 184 | 'token': instance.token != null ? instance.token : '' 185 | }, 186 | }; 187 | 188 | if (method == "GET") { 189 | url = url + queryParams(data, true); 190 | delete options.body; 191 | } 192 | 193 | fetch(instance.defaults.baseURL + url, options).then(async (response) => { 194 | let result = await response.json(); 195 | if (result == null) { 196 | return reject({ 197 | code: YeIMUniSDKStatusCode.NORMAL_ERROR.code, 198 | message: result.message ? result.message : YeIMUniSDKStatusCode 199 | .NORMAL_ERROR.describe, 200 | data: null 201 | }); 202 | } 203 | let code = result.code; 204 | if (code === YeIMUniSDKStatusCode.NORMAL_SUCCESS.code) { 205 | resolve(result.data ? result.data : null); 206 | } else { 207 | reject(result); 208 | } 209 | }, (error) => { 210 | log(1, error); 211 | reject({ 212 | code: YeIMUniSDKStatusCode.NORMAL_ERROR.code, 213 | message: JSON.stringify(error.message), 214 | data: null 215 | }); 216 | }) 217 | } 218 | }); 219 | } 220 | 221 | 222 | /** 223 | * 公共上传 224 | * 225 | * @param {Object} options - 请求参数 226 | * @param {String} options.url - 请求地址 227 | * @param {String} options.name - 文件上传的标识符 228 | * @param {Object} options.header - 请求头 229 | * @param {Object} options.data - 请求参数 230 | * @param {String} options.filePath - 上传文件资源的路径 231 | * @param {Boolean} options.ignoreResult - 是否忽略结果 232 | * @param {(result)=>{}} [options.success] - 成功回调 233 | * @param {(error)=>{}} [options.fail] - 失败回调 234 | * 235 | * @return uploadTask 236 | * 237 | */ 238 | function upload(options) { 239 | if (instance.uni) { 240 | return uni.uploadFile({ 241 | url: options.url, 242 | name: options.name, 243 | formData: options.data, 244 | header: options.header, 245 | filePath: options.filePath, 246 | success: (result) => { 247 | //忽略结果 248 | if (options.ignoreResult) { 249 | successHandle(options, YeIMUniSDKStatusCode.NORMAL_SUCCESS.describe) 250 | } else { 251 | if (result.data == null) reject(null); 252 | result = JSON.parse(result.data); 253 | let code = result.code; 254 | if (code === YeIMUniSDKStatusCode.NORMAL_SUCCESS.code) { 255 | successHandle(options, YeIMUniSDKStatusCode.NORMAL_SUCCESS.describe, result.data ? 256 | result.data : null) 257 | } else { 258 | errHandle(options, code, result.message); 259 | } 260 | } 261 | }, 262 | fail: (fail) => { 263 | log(1, fail); 264 | errHandle(options, YeIMUniSDKStatusCode.NORMAL_ERROR.code, fail); 265 | } 266 | }); 267 | } else { 268 | return new Promise(async (resolve, reject) => { 269 | const formdata = new FormData() 270 | for (let key in options.data) { 271 | formdata.append(key, options.data[key]) 272 | } 273 | if (isBlobURL(options.filePath)) { 274 | const blobResp = await fetch(blobUrl); 275 | const blob = await blobResp.blob(); 276 | formData.append('file', blob); 277 | } else { 278 | formdata.append('file', options.filePath) 279 | } 280 | 281 | fetch(options.url, { 282 | method: 'POST', 283 | body: formdata, 284 | headers: options.header 285 | }).then(async res => { 286 | if (res.ok) { 287 | let json = await res.json(); 288 | if (json.code == YeIMUniSDKStatusCode.NORMAL_SUCCESS.code) { 289 | successHandle(options, YeIMUniSDKStatusCode.NORMAL_SUCCESS.describe, 290 | json.data ? 291 | json.data : null) 292 | } else { 293 | log(1, json.message); 294 | errHandle(options, YeIMUniSDKStatusCode.NORMAL_ERROR.code, json 295 | .message); 296 | } 297 | } else { 298 | log(1, 'network error'); 299 | errHandle(options, YeIMUniSDKStatusCode.NORMAL_ERROR.code, 'network error'); 300 | } 301 | }).then(fail => { 302 | log(1, fail); 303 | errHandle(options, YeIMUniSDKStatusCode.NORMAL_ERROR.code, fail); 304 | }) 305 | }); 306 | } 307 | } 308 | 309 | /** 310 | * 公共下载 311 | * 312 | * @param {Object} options - 请求参数 313 | * @param {String} options.url - 请求的下载地址 314 | * @param {Object} options.header - 请求头 315 | * @param {(result)=>{}} [options.success] - 成功回调 316 | * @param {(error)=>{}} [options.fail] - 失败回调 317 | * 318 | * @return downloadTask 319 | * 320 | */ 321 | function download(options) { 322 | if (instance.uni) { 323 | return uni.downloadFile({ 324 | url: options.url, 325 | header: options.header, 326 | success: (downloadRes) => { 327 | if (downloadRes.statusCode === 200) { 328 | successHandle(options, YeIMUniSDKStatusCode 329 | .NORMAL_SUCCESS.describe, downloadRes.tempFilePath) 330 | } else { 331 | errHandle(options, YeIMUniSDKStatusCode.DOWNLOAD_ERROR 332 | .code, YeIMUniSDKStatusCode.DOWNLOAD_ERROR 333 | .code.describe); 334 | } 335 | }, 336 | fail: (fail) => { 337 | log(1, fail); 338 | errHandle(options, YeIMUniSDKStatusCode.DOWNLOAD_ERROR 339 | .code, YeIMUniSDKStatusCode.DOWNLOAD_ERROR 340 | .code.describe); 341 | } 342 | }); 343 | } else { 344 | return fetch(options.url, { 345 | header: options.header, 346 | }) 347 | .then(response => response.blob()) 348 | .then(blob => { 349 | var file = new File([blob], options.filename ? options.filename : '1.jpg'); 350 | successHandle(options, YeIMUniSDKStatusCode 351 | .NORMAL_SUCCESS.describe, file) 352 | }).catch(error => { 353 | log(1, fail); 354 | errHandle(options, YeIMUniSDKStatusCode.DOWNLOAD_ERROR 355 | .code, YeIMUniSDKStatusCode.DOWNLOAD_ERROR 356 | .code.describe); 357 | }); 358 | } 359 | } 360 | 361 | export { 362 | Api, 363 | request, 364 | upload, 365 | download 366 | } -------------------------------------------------------------------------------- /src/func/storage.js: -------------------------------------------------------------------------------- 1 | /*eslint-disable*/ 2 | 3 | import { instance } from "../yeim-uni-sdk" 4 | 5 | 6 | /** 7 | * 从本地缓存中同步获取指定 key 对应的内容 8 | * 9 | * @param {String} key - 缓存名称 10 | * @returns {Object} 11 | */ 12 | function getCache(key) { 13 | if (instance.uni) { 14 | return uni.getStorageSync(key); 15 | } else { 16 | return localStorage.getItem(key) ? JSON.parse(localStorage.getItem(key)) : null; 17 | } 18 | } 19 | 20 | /** 21 | * 将 value 存储在本地缓存中指定的 key 中 22 | * 23 | * @param {String} key - 缓存名称 24 | * @param {Object} value - 缓存值 25 | * @returns {void} 26 | */ 27 | function setCache(key, value) { 28 | if (instance.uni) { 29 | uni.setStorageSync(key, value); 30 | } else { 31 | localStorage.setItem(key, JSON.stringify(value)); 32 | } 33 | } 34 | 35 | /** 36 | * 从本地缓存中同步移除指定 key 37 | * 38 | * @param {String} key - 缓存名称 39 | * @returns {void} 40 | */ 41 | function removeCache(key) { 42 | if (instance.uni) { 43 | uni.removeStorageSync(key); 44 | } else { 45 | localStorage.removeItem(key); 46 | } 47 | } 48 | 49 | export { 50 | getCache, 51 | setCache, 52 | removeCache 53 | } -------------------------------------------------------------------------------- /src/func/websocket.js: -------------------------------------------------------------------------------- 1 | /*eslint-disable*/ 2 | 3 | import { instance } from "../yeim-uni-sdk"; 4 | import log from "./log"; 5 | 6 | export default class WebSocketUtil { 7 | 8 | socketTask = undefined; 9 | 10 | onMessageCallBack = []; 11 | 12 | constructor(url) { 13 | if (instance.uni) { 14 | this.socketTask = uni.connectSocket({ 15 | url: url, 16 | success: () => { }, 17 | fail: () => { }, 18 | complete: () => { } 19 | }); 20 | } else { 21 | this.socketTask = new WebSocket(url); 22 | this.socketTask.onmessage = (res) => { 23 | if (this.onMessageCallBack) { 24 | for (let index = 0; index < this.onMessageCallBack.length; index++) { 25 | this.onMessageCallBack[index](res); 26 | } 27 | } 28 | }; 29 | } 30 | } 31 | onOpen(callback) { 32 | try { 33 | if (instance.uni) { 34 | this.socketTask.onOpen(callback); 35 | } else { 36 | this.socketTask.onopen = callback; 37 | } 38 | } catch (e) { 39 | log(0, e, true); 40 | } 41 | } 42 | onClose(callback) { 43 | try { 44 | if (instance.uni) { 45 | this.socketTask.onClose(callback); 46 | } else { 47 | this.socketTask.onclose = callback; 48 | } 49 | } catch (e) { 50 | log(0, e, true); 51 | } 52 | } 53 | onError(callback) { 54 | try { 55 | if (instance.uni) { 56 | this.socketTask.onError(callback); 57 | } else { 58 | this.socketTask.onerror = callback; 59 | } 60 | } catch (e) { 61 | log(0, e, true); 62 | } 63 | } 64 | onMessage(callback) { 65 | try { 66 | if (instance.uni) { 67 | this.socketTask.onMessage(callback); 68 | } else { 69 | this.onMessageCallBack.push(callback); 70 | } 71 | } catch (e) { 72 | log(0, e, true); 73 | } 74 | } 75 | send(str) { 76 | if (this.socketTask) { 77 | try { 78 | if (instance.uni) { 79 | this.socketTask.send({ 80 | data: JSON.stringify(str) 81 | }); 82 | } else { 83 | this.socketTask.send(JSON.stringify(str)); 84 | } 85 | } catch (e) { 86 | log(0, e, true); 87 | } 88 | } 89 | } 90 | close() { 91 | try { 92 | this.socketTask.close(); 93 | } catch (e) { 94 | log(0, e, true); 95 | } 96 | } 97 | destroy() { 98 | this.socketTask = undefined; 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/service/conversationService.js: -------------------------------------------------------------------------------- 1 | import { 2 | instance 3 | } from '../yeim-uni-sdk'; 4 | import { 5 | YeIMUniSDKDefines 6 | } from '../const/yeim-defines'; 7 | import { 8 | emit 9 | } from '../func/event'; 10 | import log from '../func/log'; 11 | import md5 from '../utils/md5'; 12 | import { 13 | buildSuccessObject, 14 | buildErrObject, 15 | successHandle, 16 | errHandle 17 | } from '../func/callback'; 18 | import { 19 | YeIMUniSDKStatusCode 20 | } from '../const/yeim-status-code'; 21 | import { 22 | Api, 23 | request 24 | } from '../func/request'; 25 | import { 26 | getCache, 27 | removeCache, 28 | setCache 29 | } from '../func/storage'; 30 | 31 | 32 | /** 33 | * 更新、保存单个会话到本地会话列表、发送会话列表更新事件 34 | * 35 | * @param {Conversation} conversation - 会话对象 36 | * @return {void} 37 | */ 38 | function saveAndUpdateConversation(conversation) { 39 | 40 | let key = `yeim:conversationList:${md5(instance.userId)}`; 41 | let list = getCache(key); 42 | list = list ? list : []; 43 | let index = list.findIndex(item => { 44 | return item.conversationId === conversation.conversationId 45 | }); 46 | if (index === -1) { 47 | list.unshift(conversation); 48 | } else { 49 | list.splice(index, 1); 50 | list.unshift(conversation); 51 | } 52 | saveConversationList(list); 53 | 54 | } 55 | 56 | /** 57 | * 根据会话ID获取本地会话详情 58 | * 59 | * @param {String} conversationId - 会话ID 60 | * @return {Conversation} conversation 61 | */ 62 | function getConversation(conversationId) { 63 | 64 | if (!instance.checkLogged()) { 65 | return buildErrObject(YeIMUniSDKStatusCode.LOGIN_EXPIRE.code, YeIMUniSDKStatusCode.LOGIN_EXPIRE.describe); 66 | } 67 | if (!conversationId) { 68 | return buildErrObject(YeIMUniSDKStatusCode.PARAMS_ERROR.code, 'conversationId 不能为空'); 69 | } 70 | let key = `yeim:conversationList:${md5(instance.userId)}`; 71 | let result = getCache(key); 72 | result = result ? result : []; 73 | let index = result.findIndex(item => { 74 | return item.conversationId === conversationId; 75 | }); 76 | if (index !== -1) { 77 | return buildSuccessObject(YeIMUniSDKStatusCode.NORMAL_SUCCESS.describe, result[index]); 78 | } else { 79 | return buildErrObject(YeIMUniSDKStatusCode.NO_CONVERSATION.code, YeIMUniSDKStatusCode.NO_CONVERSATION.describe); 80 | } 81 | 82 | } 83 | 84 | /** 85 | * 获取本地会话列表 86 | * 87 | * @param {Object} options - 参数对象 88 | * 89 | * @param {String} options.page - 页码 90 | * @param {String} options.limit - 每页数量 91 | * @param {(result)=>{}} [options.success] - 成功回调 92 | * @param {(error)=>{}} [options.fail] - 失败回调 93 | */ 94 | function getConversationList(options) { 95 | if (!instance.checkLogged()) { 96 | return errHandle(options, YeIMUniSDKStatusCode.LOGIN_EXPIRE.code, YeIMUniSDKStatusCode.LOGIN_EXPIRE.describe); 97 | } 98 | let page = options.page; 99 | let limit = options.limit; 100 | let key = `yeim:conversationList:${md5(instance.userId)}`; 101 | let result = getCache(key); 102 | result = result ? result : []; 103 | let skipNum = (page - 1) * limit; 104 | let list = (skipNum + limit >= result.length) ? result.slice(skipNum, result.length) : result.slice(skipNum, 105 | skipNum + limit); 106 | successHandle(options, YeIMUniSDKStatusCode.NORMAL_SUCCESS.describe, list); 107 | } 108 | 109 | /** 110 | * 从云端获取会话列表保存到本地、并发送会话列表更新事件 111 | * 112 | * @return {void} 113 | */ 114 | function saveCloudConversationListToLocal() { 115 | request(Api.Conversation.fetchConversationList, 'GET', { 116 | page: 1, 117 | limit: 999999, 118 | }).then((result) => { 119 | //查询结果保存到本地 120 | saveConversationList(result.records); 121 | }).catch((fail) => { 122 | log(1, fail); 123 | }); 124 | 125 | } 126 | 127 | /** 128 | * 保存会话列表到本地,并发送会话更新事件 129 | * 130 | * @param {Array} list - 会话对象数组 131 | * @return {void} 132 | */ 133 | function saveConversationList(list) { 134 | let key = `yeim:conversationList:${md5(instance.userId)}`; 135 | setCache(key, list); 136 | emit(YeIMUniSDKDefines.EVENT.CONVERSATION_LIST_CHANGED, list); 137 | } 138 | 139 | /** 140 | * 清除指定会话未读数 141 | * 142 | * 云端同时发送给对端已读事件(私聊) 143 | * 144 | * @param {String} conversationId - 会话ID 145 | * @return {void} 146 | */ 147 | function clearConversationUnread(conversationId) { 148 | 149 | if (!instance.checkLogged()) { 150 | return buildErrObject(YeIMUniSDKStatusCode.LOGIN_EXPIRE.code, YeIMUniSDKStatusCode.LOGIN_EXPIRE.describe); 151 | } 152 | 153 | //本地清除会话未读数 154 | let key = `yeim:conversationList:${md5(instance.userId)}`; 155 | let result = getCache(key); 156 | result = result ? result : []; 157 | let index = result.findIndex(item => { 158 | return item.conversationId === conversationId; 159 | }); 160 | if (index !== -1) { 161 | result[index].unread = 0; 162 | setCache(key, result); 163 | } 164 | //云端清除会话未读数 165 | request(Api.Conversation.clearConversationUnread, "GET", { 166 | conversationId: conversationId 167 | }).then(() => { }).catch((fail) => { 168 | log(1, fail); 169 | }); 170 | //发送会话列表更新事件 171 | emit(YeIMUniSDKDefines.EVENT.CONVERSATION_LIST_CHANGED, result); 172 | 173 | } 174 | 175 | /** 176 | * 收到某对端会话消息已读事件,处理本地会话消息已读字段,并发送给当前用户对端会话已读事件 177 | * 178 | * @param {Object} conversationId 179 | * @return {void} 180 | */ 181 | function handlePrivateConversationReadReceipt(conversationId) { 182 | 183 | if (!conversationId) { 184 | return log(1, 'conversationId 为空,无法继续执行'); 185 | } 186 | //查出当前会话发出的消息 187 | let messageKey = `yeim:messageList:${md5(instance.userId)}:conversationId:${md5(conversationId)}`; 188 | let result = getCache(messageKey); 189 | result = result ? result : []; 190 | if (result) { 191 | let tempList = []; 192 | for (let i = 0; i < result.length; i++) { 193 | let message = result[i]; 194 | if (message.direction == 'out') { 195 | message.isRead = 1; 196 | result[i].isRead = 1; 197 | tempList.push(message); 198 | } 199 | } 200 | setCache(messageKey, result); 201 | //发送私聊会话已读事件 202 | emit(YeIMUniSDKDefines.EVENT.PRIVATE_READ_RECEIPT, { 203 | conversationId: conversationId, 204 | list: tempList 205 | }); 206 | } 207 | 208 | } 209 | 210 | /** 211 | * 根据会话ID删除会话和聊天记录(包括云端) 212 | * 213 | * @param {Object} conversationId 214 | * @return {void} 215 | */ 216 | function deleteConversation(conversationId) { 217 | 218 | if (!instance.checkLogged()) { 219 | return buildErrObject(YeIMUniSDKStatusCode.LOGIN_EXPIRE.code, YeIMUniSDKStatusCode.LOGIN_EXPIRE.describe); 220 | } 221 | 222 | //1.删除本地会话 223 | let key = `yeim:conversationList:${md5(instance.userId)}`; 224 | let result = getCache(key); 225 | result = result ? result : []; 226 | let index = result.findIndex(item => { 227 | return item.conversationId === conversationId; 228 | }); 229 | if (index !== -1) { 230 | result.splice(index, 1); 231 | setCache(key, result); 232 | } 233 | //2.删除本地会话内的聊天记录 234 | let messageKey = `yeim:messageList:${md5(instance.userId)}:conversationId:${md5(conversationId)}`; 235 | removeCache(messageKey); 236 | 237 | //3.删除云端会话和云端聊天记录 238 | request(Api.Conversation.deleteConversation, 'GET', { 239 | conversationId: conversationId 240 | }).then(() => { }).catch((fail) => { 241 | log(1, fail); 242 | }); 243 | 244 | //4.发出会话列表更新事件 245 | emit(YeIMUniSDKDefines.EVENT.CONVERSATION_LIST_CHANGED, result); 246 | 247 | } 248 | 249 | export { 250 | saveAndUpdateConversation, 251 | clearConversationUnread, 252 | deleteConversation, 253 | getConversation, 254 | getConversationList, 255 | saveCloudConversationListToLocal, 256 | handlePrivateConversationReadReceipt 257 | } 258 | -------------------------------------------------------------------------------- /src/service/friendService.js: -------------------------------------------------------------------------------- 1 | import { 2 | YeIMUniSDKDefines, 3 | instance 4 | } from '../yeim-uni-sdk'; 5 | import { 6 | Api, 7 | request 8 | } from '../func/request'; 9 | import { 10 | successHandle, 11 | errHandle 12 | } from '../func/callback'; 13 | import { 14 | YeIMUniSDKStatusCode 15 | } from '../const/yeim-status-code'; 16 | import { 17 | emit 18 | } from '../func/event'; 19 | import log from '../func/log'; 20 | import { 21 | getCache, 22 | setCache 23 | } from '../func/storage'; 24 | import md5 from '../utils/md5'; 25 | 26 | /** 27 | * 28 | * 从云端获取好友列表 29 | * 30 | * @param {Object} options - 参数对象 31 | * @param {Boolean} options.cloud - 是否从云端拉取 32 | * @param {Number} options.profile - 资料类型,0=简略资料,1=详细资料 (云端拉取有效) 33 | * @param {Number} options.page - 页码 34 | * @param {Number} options.limit - 每页数量 35 | * 36 | * @param {(result)=>{}} [options.success] - 成功回调 37 | * @param {(error)=>{}} [options.fail] - 失败回调 38 | */ 39 | function getFriendList(options) { 40 | 41 | let key = `yeim:friendList:${md5(instance.userId)}`; 42 | let page = options.page ? options.page : 1; 43 | let limit = options.limit ? options.limit : 20; 44 | if (options.cloud) { 45 | //从云端获取好友列表 46 | request(Api.Friend.fetchList, 'GET', { 47 | profile: options.profile ? 0 : 1, 48 | page: page, 49 | limit: limit, 50 | }).then((result) => { 51 | const { 52 | records 53 | } = result; 54 | successHandle(options, YeIMUniSDKStatusCode.NORMAL_SUCCESS.describe, records); 55 | }).catch((fail) => { 56 | errHandle(options, fail.code, fail.message); 57 | log(1, fail); 58 | }); 59 | } else { 60 | //从本地获取好友列表 61 | 62 | //查询本地缓存 63 | let list = getCache(key); 64 | list = list ? list : []; 65 | let skipNum = (page - 1) * limit; 66 | list = (skipNum + limit >= list.length) ? list.slice(skipNum, list.length) : list.slice(skipNum, 67 | skipNum + limit); 68 | successHandle(options, YeIMUniSDKStatusCode.NORMAL_SUCCESS.describe, list); 69 | } 70 | } 71 | 72 | /** 73 | * 74 | * 响应好友列表更新 75 | * 76 | */ 77 | function handleFriendListChanged() { 78 | //获取最新记录 79 | getFriendList({ 80 | cloud: true, 81 | page: 1, 82 | limit: 9999999, 83 | success: (result) => { 84 | let key = `yeim:friendList:${md5(instance.userId)}`; 85 | setCache(key, result.data); 86 | emit(YeIMUniSDKDefines.EVENT.FRIEND_LIST_CHANGED, result); 87 | }, 88 | fail: () => { 89 | emit(YeIMUniSDKDefines.EVENT.FRIEND_LIST_CHANGED, null); 90 | } 91 | }); 92 | } 93 | 94 | /** 95 | * 96 | * 响应好友申请列表更新 97 | * 98 | */ 99 | function handleFriendApplyListChanged(type = 0) { 100 | //获取最新记录 101 | getFriendApplyList({ 102 | cloud: true, 103 | type: type, 104 | page: 1, 105 | limit: 9999999, 106 | success: (result) => { 107 | const { 108 | data: { 109 | records, 110 | unread 111 | } 112 | } = result; 113 | let listKey = `yeim:friendApplyList:${md5(instance.userId)}`; 114 | let unreadKey = `yeim:friendApplyUnread:${md5(instance.userId)}`; 115 | setCache(listKey, records); 116 | setCache(unreadKey, unread); 117 | emit(YeIMUniSDKDefines.EVENT.FRIEND_APPLY_LIST_CHANGED, result); 118 | }, 119 | fail: () => { 120 | emit(YeIMUniSDKDefines.EVENT.FRIEND_APPLY_LIST_CHANGED, null); 121 | } 122 | }); 123 | } 124 | 125 | /** 126 | * 127 | * 从云端获取好友申请列表 128 | * 129 | * @param {Object} options - 参数对象 130 | * 131 | * @param {Boolean} options.cloud - 是否从云端拉取 132 | * @param {Number} options.type - 类型,0=发给我申请,1=我发出去的申请 (云端拉取有效) 133 | * @param {Number} options.page - 页码 134 | * @param {Number} options.limit - 每页数量 135 | * 136 | * @param {(result)=>{}} [options.success] - 成功回调 137 | * @param {(error)=>{}} [options.fail] - 失败回调 138 | */ 139 | function getFriendApplyList(options) { 140 | 141 | let listKey = `yeim:friendApplyList:${md5(instance.userId)}`; 142 | let unreadKey = `yeim:friendApplyUnread:${md5(instance.userId)}`; 143 | let page = options.page ? options.page : 1; 144 | let limit = options.limit ? options.limit : 20; 145 | if (options.cloud) { 146 | //从云端获取好友申请列表 147 | request(Api.Friend.fetchApplyList, 'GET', { 148 | type: options.type ? options.type : 0, 149 | page: page, 150 | limit: limit, 151 | }).then((result) => { 152 | const { 153 | apply: { 154 | records 155 | }, 156 | unread 157 | } = result; 158 | successHandle(options, YeIMUniSDKStatusCode.NORMAL_SUCCESS.describe, { 159 | records, 160 | unread 161 | }); 162 | }).catch((fail) => { 163 | errHandle(options, fail.code, fail.message); 164 | log(1, fail); 165 | }); 166 | } else { 167 | //从本地获取好友列表 168 | 169 | //查询本地缓存 170 | let list = getCache(listKey); 171 | list = list ? list : []; 172 | let skipNum = (page - 1) * limit; 173 | list = (skipNum + limit >= list.length) ? list.slice(skipNum, list.length) : list.slice(skipNum, 174 | skipNum + limit); 175 | 176 | successHandle(options, YeIMUniSDKStatusCode.NORMAL_SUCCESS.describe, { 177 | list, 178 | unread: getCache(unreadKey) ? getCache(unreadKey) : 0 179 | }); 180 | 181 | } 182 | } 183 | 184 | /** 185 | * 186 | * 将全部好友申请设置为已读状态 187 | * 188 | * @param {Object} options - 参数对象 189 | * 190 | * @param {(result)=>{}} [options.success] - 成功回调 191 | * @param {(error)=>{}} [options.fail] - 失败回调 192 | */ 193 | function setApplyListRead(options) { 194 | request(Api.Friend.setRead, 'GET', {}).then(() => { 195 | successHandle(options, YeIMUniSDKStatusCode.NORMAL_SUCCESS.describe, null); 196 | }).catch((fail) => { 197 | errHandle(options, fail.code, fail.message); 198 | log(1, fail); 199 | }); 200 | } 201 | 202 | 203 | /** 204 | * 205 | * 同意好友申请 206 | * 207 | * @param {Object} options - 参数对象 208 | * 209 | * @param {Number} options.id - 申请ID 210 | * @param {String} options.remark - 备注 211 | * 212 | * @param {(result)=>{}} [options.success] - 成功回调 213 | * @param {(error)=>{}} [options.fail] - 失败回调 214 | */ 215 | function acceptApply(options) { 216 | 217 | if (!instance.checkLogged()) { 218 | return errHandle(options, YeIMUniSDKStatusCode.LOGIN_EXPIRE.code, YeIMUniSDKStatusCode.LOGIN_EXPIRE.describe); 219 | } 220 | 221 | if (!options.id) { 222 | return errHandle(options, YeIMUniSDKStatusCode.PARAMS_ERROR.code, 'id 不能为空'); 223 | } 224 | 225 | request(Api.Friend.acceptApply, 'GET', { 226 | id: options.id, 227 | remark: options.remark ? options.remark : null 228 | }).then(() => { 229 | successHandle(options, YeIMUniSDKStatusCode.NORMAL_SUCCESS.describe, null); 230 | }).catch((fail) => { 231 | errHandle(options, fail.code, fail.message); 232 | log(1, fail); 233 | }); 234 | } 235 | 236 | /** 237 | * 238 | * 拒绝好友申请 239 | * 240 | * @param {Object} options - 参数对象 241 | * 242 | * @param {Number} options.id - 申请ID 243 | * 244 | * @param {(result)=>{}} [options.success] - 成功回调 245 | * @param {(error)=>{}} [options.fail] - 失败回调 246 | */ 247 | function refuseApply(options) { 248 | 249 | if (!instance.checkLogged()) { 250 | return errHandle(options, YeIMUniSDKStatusCode.LOGIN_EXPIRE.code, YeIMUniSDKStatusCode.LOGIN_EXPIRE.describe); 251 | } 252 | 253 | if (!options.id) { 254 | return errHandle(options, YeIMUniSDKStatusCode.PARAMS_ERROR.code, 'id 不能为空'); 255 | } 256 | 257 | request(Api.Friend.refuseApply, 'GET', { 258 | id: options.id 259 | }).then(() => { 260 | successHandle(options, YeIMUniSDKStatusCode.NORMAL_SUCCESS.describe, null); 261 | }).catch((fail) => { 262 | errHandle(options, fail.code, fail.message); 263 | log(1, fail); 264 | }); 265 | } 266 | 267 | /** 268 | * 269 | * 添加好友 270 | * 271 | * @param {Object} options - 参数对象 272 | * 273 | * @param {String} options.userId - 用户ID 274 | * @param {String} options.remark - 好友备注 275 | * @param {String} options.extraMessage - 附言 276 | * 277 | * @param {(result)=>{}} [options.success] - 成功回调 278 | * @param {(error)=>{}} [options.fail] - 失败回调 279 | */ 280 | function addFriend(options) { 281 | 282 | if (!instance.checkLogged()) { 283 | return errHandle(options, YeIMUniSDKStatusCode.LOGIN_EXPIRE.code, YeIMUniSDKStatusCode.LOGIN_EXPIRE.describe); 284 | } 285 | 286 | if (!options.userId) { 287 | return errHandle(options, YeIMUniSDKStatusCode.PARAMS_ERROR.code, 'userId 不能为空'); 288 | } 289 | 290 | request(Api.Friend.addFriend, 'POST', { 291 | userId: options.userId, 292 | remark: options.remark ? options.remark : null, 293 | extraMessage: options.extraMessage ? options.extraMessage : null 294 | }).then(() => { 295 | successHandle(options, YeIMUniSDKStatusCode.NORMAL_SUCCESS.describe, null); 296 | }).catch((fail) => { 297 | if (fail.code === YeIMUniSDKStatusCode.APPLY_NEED.code) { 298 | successHandle(options, YeIMUniSDKStatusCode.APPLY_NEED.describe, null, YeIMUniSDKStatusCode 299 | .APPLY_NEED.code); 300 | } else { 301 | errHandle(options, fail.code, fail.message); 302 | log(1, fail); 303 | } 304 | }); 305 | 306 | } 307 | 308 | /** 309 | * 310 | * 删除好友 311 | * 312 | * @param {Object} options - 参数对象 313 | * 314 | * @param {Array} options.members - 好友ID列表 315 | * 316 | * @param {(result)=>{}} [options.success] - 成功回调 317 | * @param {(error)=>{}} [options.fail] - 失败回调 318 | */ 319 | function deleteFriend(options) { 320 | 321 | if (!instance.checkLogged()) { 322 | return errHandle(options, YeIMUniSDKStatusCode.LOGIN_EXPIRE.code, YeIMUniSDKStatusCode.LOGIN_EXPIRE.describe); 323 | } 324 | 325 | if (!options.members) { 326 | return errHandle(options, YeIMUniSDKStatusCode.PARAMS_ERROR.code, 'members 不能为空'); 327 | } 328 | 329 | request(Api.Friend.deleteFriend, 'POST', { 330 | members: options.members 331 | }).then(() => { 332 | successHandle(options, YeIMUniSDKStatusCode.NORMAL_SUCCESS.describe, null); 333 | }).catch((fail) => { 334 | errHandle(options, fail.code, fail.message); 335 | log(1, fail); 336 | }); 337 | 338 | } 339 | 340 | /** 341 | * 342 | * 更新好友资料 343 | * 344 | * @param {Object} options - 参数对象 345 | * 346 | * @param {String} options.userId - 好友ID 347 | * @param {String} options.remark - 好友备注 348 | * @param {String} options.extend - 自定义扩展字段 349 | * 350 | * @param {(result)=>{}} [options.success] - 成功回调 351 | * @param {(error)=>{}} [options.fail] - 失败回调 352 | */ 353 | function updateFriend(options) { 354 | 355 | if (!instance.checkLogged()) { 356 | return errHandle(options, YeIMUniSDKStatusCode.LOGIN_EXPIRE.code, YeIMUniSDKStatusCode.LOGIN_EXPIRE.describe); 357 | } 358 | 359 | if (!options.userId) { 360 | return errHandle(options, YeIMUniSDKStatusCode.PARAMS_ERROR.code, 'userId 不能为空'); 361 | } 362 | 363 | if (!options.remark && !options.extend) { 364 | return errHandle(options, YeIMUniSDKStatusCode.PARAMS_ERROR.code, '请至少传入一个参数更新'); 365 | } 366 | 367 | request(Api.Friend.updateFriend, 'POST', { 368 | userId: options.userId, 369 | remark: options.remark ? options.remark : null, 370 | extend: options.extend ? options.extend : null, 371 | }).then(() => { 372 | successHandle(options, YeIMUniSDKStatusCode.NORMAL_SUCCESS.describe, null); 373 | }).catch((fail) => { 374 | errHandle(options, fail.code, fail.message); 375 | log(1, fail); 376 | }); 377 | 378 | } 379 | 380 | export { 381 | getFriendList, 382 | handleFriendListChanged, 383 | handleFriendApplyListChanged, 384 | getFriendApplyList, 385 | setApplyListRead, 386 | addFriend, 387 | deleteFriend, 388 | updateFriend, 389 | acceptApply, 390 | refuseApply 391 | } -------------------------------------------------------------------------------- /src/service/groupService.js: -------------------------------------------------------------------------------- 1 | import { 2 | instance 3 | } from '../yeim-uni-sdk'; 4 | import { 5 | YeIMUniSDKDefines 6 | } from '../const/yeim-defines'; 7 | import log from '../func/log'; 8 | import { 9 | successHandle, 10 | errHandle 11 | } from '../func/callback'; 12 | import { 13 | Api, 14 | request 15 | } from '../func/request'; 16 | import { 17 | YeIMUniSDKStatusCode 18 | } from '../const/yeim-status-code'; 19 | 20 | /** 21 | * 22 | * 创建群组 23 | * 24 | * @param {Object} options - 参数对象 25 | * 26 | * { "name":"群名称","avatarUrl":"群头像", success: (result) => {}, fail: (error) => {} } 27 | * 28 | * @param {String} options.name - 群名称 29 | * @param {String} options.avatarUrl - 群头像 30 | * @param {String} [options.groupId] - 群ID,未填写时系统自动生成 31 | * @param {YeIMUniSDKDefines.GROUP.JOINMODE} [options.joinMode] - 群申请处理方式 32 | * @param {String} [options.introduction] - 群简介 33 | * @param {String} [options.notification] - 群公告 34 | * @param {Array} [options.members] - 创建群聊初始化成员(用户ID数组) 35 | * @param {(result)=>{}} [options.success] - 成功回调 36 | * @param {(error)=>{}} [options.fail] - 失败回调 37 | * 38 | * @example 39 | * createGroup({ 40 | name: "", 41 | avatarUrl: "", 42 | success: (result) => {}, 43 | fail: (error) => {} 44 | }); 45 | */ 46 | 47 | function createGroup(options) { 48 | 49 | if (!instance.checkLogged()) { 50 | return errHandle(options, YeIMUniSDKStatusCode.LOGIN_EXPIRE.code, YeIMUniSDKStatusCode.LOGIN_EXPIRE.describe); 51 | } 52 | 53 | if (!options.name) { 54 | return errHandle(options, YeIMUniSDKStatusCode.PARAMS_ERROR.code, 'name 不能为空'); 55 | } 56 | 57 | if (!options.avatarUrl) { 58 | return errHandle(options, YeIMUniSDKStatusCode.PARAMS_ERROR.code, 'avatarUrl 不能为空'); 59 | } 60 | 61 | request(Api.Group.create, 'POST', options).then(() => { 62 | successHandle(options, YeIMUniSDKStatusCode.NORMAL_SUCCESS.describe); 63 | }).catch((fail) => { 64 | errHandle(options, fail.code, fail.message); 65 | log(1, fail); 66 | }); 67 | 68 | } 69 | 70 | /** 71 | * 72 | * 解散群组 73 | * 74 | * 仅群主可操作 75 | * 76 | * @param {Object} options - 参数对象 77 | * 78 | * { "groupId":"群ID", success: (result) => {}, fail: (error) => {} } 79 | * 80 | * @param {String} [options.groupId] - 群ID 81 | * @param {(result)=>{}} [options.success] - 成功回调 82 | * @param {(error)=>{}} [options.fail] - 失败回调 83 | * 84 | * @example 85 | * dissolveGroup({ 86 | groupId: "", 87 | success: (result) => {}, 88 | fail: (error) => {} 89 | }); 90 | */ 91 | function dissolveGroup(options) { 92 | 93 | if (!instance.checkLogged()) { 94 | return errHandle(options, YeIMUniSDKStatusCode.LOGIN_EXPIRE.code, YeIMUniSDKStatusCode.LOGIN_EXPIRE.describe); 95 | } 96 | 97 | if (!options.groupId) { 98 | return errHandle(options, YeIMUniSDKStatusCode.PARAMS_ERROR.code, 'groupId 不能为空'); 99 | } 100 | 101 | request(Api.Group.dissolve, 'GET', { 102 | groupId: options.groupId 103 | }).then(() => { 104 | successHandle(options, YeIMUniSDKStatusCode.NORMAL_SUCCESS.describe); 105 | }).catch((fail) => { 106 | errHandle(options, fail.code, fail.message); 107 | log(1, fail); 108 | }); 109 | 110 | } 111 | 112 | /** 113 | * 114 | * 转让群主 115 | * 116 | * 仅群主可操作 117 | * 118 | * @param {Object} options - 参数对象 119 | * 120 | * { "groupId":"群ID", "userId":"转让用户ID", success: (result) => {}, fail: (error) => {} } 121 | * 122 | * @param {String} [options.groupId] - 群ID 123 | * @param {String} [options.userId] - 转让用户ID 124 | * @param {(result)=>{}} [options.success] - 成功回调 125 | * @param {(error)=>{}} [options.fail] - 失败回调 126 | * 127 | * @example 128 | * transferLeader({ 129 | groupId: "", 130 | userId: "", 131 | success: (result) => {}, 132 | fail: (error) => {} 133 | }); 134 | */ 135 | function transferLeader(options) { 136 | 137 | if (!instance.checkLogged()) { 138 | return errHandle(options, YeIMUniSDKStatusCode.LOGIN_EXPIRE.code, YeIMUniSDKStatusCode.LOGIN_EXPIRE.describe); 139 | } 140 | 141 | if (!options.groupId) { 142 | return errHandle(options, YeIMUniSDKStatusCode.PARAMS_ERROR.code, 'groupId 不能为空'); 143 | } 144 | 145 | if (!options.userId) { 146 | return errHandle(options, YeIMUniSDKStatusCode.PARAMS_ERROR.code, 'userId 不能为空'); 147 | } 148 | 149 | request(Api.Group.transferLeader, 'GET', { 150 | groupId: options.groupId, 151 | userId: options.userId 152 | }).then(() => { 153 | successHandle(options, YeIMUniSDKStatusCode.NORMAL_SUCCESS.describe); 154 | }).catch((fail) => { 155 | errHandle(options, fail.code, fail.message); 156 | log(1, fail); 157 | }); 158 | } 159 | 160 | /** 161 | * 162 | * 更新群组资料 163 | * 164 | * @param {Object} options - 参数对象 165 | * 166 | * { "groupId":"群ID", "name":"群名称","avatarUrl":"群头像", success: (result) => {}, fail: (error) => {} } 167 | * 168 | * @param {String} [options.groupId] - 群ID 169 | * @param {String} [options.name] - 群名称 170 | * @param {String} [options.avatarUrl] - 群头像 171 | * @param {YeIMUniSDKDefines.GROUP.JOINMODE} [options.joinMode] - 群申请处理方式 172 | * @param {String} [options.introduction] - 群简介 173 | * @param {String} [options.notification] - 群公告 174 | * @param {Number} [options.isMute] - 全体禁言 0,1 175 | * @param {(result)=>{}} [options.success] - 成功回调 176 | * @param {(error)=>{}} [options.fail] - 失败回调 177 | * 178 | * @example 179 | * updateGroup({ 180 | groupId: "", 181 | name: "", 182 | avatarUrl: "", 183 | success: (result) => {}, 184 | fail: (error) => {} 185 | }); 186 | */ 187 | function updateGroup(options) { 188 | 189 | if (!instance.checkLogged()) { 190 | return errHandle(options, YeIMUniSDKStatusCode.LOGIN_EXPIRE.code, YeIMUniSDKStatusCode.LOGIN_EXPIRE.describe); 191 | } 192 | 193 | if (!options.groupId) { 194 | return errHandle(options, YeIMUniSDKStatusCode.PARAMS_ERROR.code, 'groupId 不能为空'); 195 | } 196 | 197 | request(Api.Group.update, 'POST', options).then(() => { 198 | successHandle(options, YeIMUniSDKStatusCode.NORMAL_SUCCESS.describe); 199 | }).catch((fail) => { 200 | errHandle(options, fail.code, fail.message); 201 | log(1, fail); 202 | }); 203 | 204 | } 205 | 206 | /** 207 | * 208 | * 通过群ID获取群组资料 209 | * 210 | * @param {Object} options - 参数对象 211 | * 212 | * { "groupId":"群ID", success: (result) => {}, fail: (error) => {} } 213 | * 214 | * @param {String} options.groupId - 群ID 215 | * @param {(result)=>{}} [options.success] - 成功回调 216 | * @param {(error)=>{}} [options.fail] - 失败回调 217 | * 218 | * @example 219 | * getGroup({ 220 | groupId: "", 221 | success: (result) => {}, 222 | fail: (error) => {} 223 | }); 224 | */ 225 | function getGroup(options) { 226 | 227 | if (!instance.checkLogged()) { 228 | return errHandle(options, YeIMUniSDKStatusCode.LOGIN_EXPIRE.code, YeIMUniSDKStatusCode.LOGIN_EXPIRE.describe); 229 | } 230 | 231 | if (!options.groupId) { 232 | return errHandle(options, YeIMUniSDKStatusCode.PARAMS_ERROR.code, 'groupId 不能为空'); 233 | } 234 | 235 | request(Api.Group.fetchGroupInfoById, 'GET', { 236 | groupId: options.groupId 237 | }).then((result) => { 238 | successHandle(options, YeIMUniSDKStatusCode.NORMAL_SUCCESS.describe, result); 239 | }).catch((fail) => { 240 | errHandle(options, fail.code, fail.message); 241 | log(1, fail); 242 | }); 243 | 244 | } 245 | 246 | /** 247 | * 248 | * 获取我的群组列表 249 | * 250 | * @param {Object} options - 参数对象 251 | * 252 | * @param {(result)=>{}} [options.success] - 成功回调 253 | * @param {(error)=>{}} [options.fail] - 失败回调 254 | * 255 | * @example 256 | * getGroupList({ 257 | success: (result) => {}, 258 | fail: (error) => {} 259 | }); 260 | */ 261 | function getGroupList(options) { 262 | 263 | if (!instance.checkLogged()) { 264 | return errHandle(options, YeIMUniSDKStatusCode.LOGIN_EXPIRE.code, YeIMUniSDKStatusCode.LOGIN_EXPIRE.describe); 265 | } 266 | 267 | request(Api.Group.list, 'GET', {}).then((result) => { 268 | successHandle(options, YeIMUniSDKStatusCode.NORMAL_SUCCESS.describe, result); 269 | }).catch((fail) => { 270 | errHandle(options, fail.code, fail.message); 271 | log(1, fail); 272 | }); 273 | 274 | } 275 | 276 | /** 277 | * 278 | * 用户申请入群 279 | * 280 | * IM内用户均可调用此接口申请进入传入的群组ID的群,根据群申请处理方式不同,可能直接进入,也可能等待审核 281 | * 282 | * @param {Object} options - 参数对象 283 | * 284 | * { "groupId": "群ID", success: (result) => {}, fail: (error) => {} } 285 | * 286 | * @param {String} options.groupId - 群ID 287 | * @param {(result)=>{}} [options.success] - 成功回调 288 | * @param {(error)=>{}} [options.fail] - 失败回调 289 | * 290 | * @example 291 | * joinGroup({ 292 | groupId: "", 293 | success: (result) => {}, 294 | fail: (error) => {} 295 | }); 296 | */ 297 | function joinGroup(options) { 298 | 299 | if (!instance.checkLogged()) { 300 | return errHandle(options, YeIMUniSDKStatusCode.LOGIN_EXPIRE.code, YeIMUniSDKStatusCode.LOGIN_EXPIRE.describe); 301 | } 302 | 303 | if (!options.groupId) { 304 | return errHandle(options, YeIMUniSDKStatusCode.PARAMS_ERROR.code, 'groupId 不能为空'); 305 | } 306 | 307 | request(Api.Group.addUser, 'POST', { 308 | groupId: options.groupId, 309 | members: [ 310 | instance.userId 311 | ] 312 | }).then((result) => { 313 | 314 | //群组信息 315 | let group = result.group; 316 | //操作成功的用户ID列表 317 | let successList = result.successList; 318 | //忽略了的用户ID列表 319 | let ignoreList = result.ignoreList; 320 | 321 | //添加的用户重复加入 322 | if (ignoreList.indexOf(instance.userId) != -1) { 323 | return errHandle(options, YeIMUniSDKStatusCode.GROUP_APPLY_REPEAT.code, YeIMUniSDKStatusCode 324 | .GROUP_APPLY_REPEAT.describe); 325 | } 326 | 327 | //判断成功列表中是否包含当前申请的用户 328 | let success = false; 329 | if (successList.indexOf(instance.userId) != -1) { 330 | success = true; 331 | } 332 | 333 | //申请入群的接口异常 334 | if (!success) { 335 | return errHandle(options, YeIMUniSDKStatusCode.GROUP_APPLY_ERROR.code, YeIMUniSDKStatusCode 336 | .GROUP_APPLY_ERROR.describe); 337 | } 338 | 339 | //自由加入,无需申请 340 | if (group.joinMode == YeIMUniSDKDefines.GROUP.JOINMODE.FREE) { 341 | successHandle(options, YeIMUniSDKStatusCode.NORMAL_SUCCESS.code, '成功加入群组'); 342 | } else if (group.joinMode == YeIMUniSDKDefines.GROUP.JOINMODE.CHECK) { 343 | successHandle(options, YeIMUniSDKStatusCode.GROUP_APPLY_WAIT.code, YeIMUniSDKStatusCode 344 | .GROUP_APPLY_WAIT.describe); 345 | } else { 346 | return errHandle(options, YeIMUniSDKStatusCode.NORMAL_ERROR.code, YeIMUniSDKStatusCode 347 | .NORMAL_ERROR.describe); 348 | } 349 | 350 | }).catch((fail) => { 351 | errHandle(options, fail.code, fail.message); 352 | log(1, fail); 353 | }); 354 | } 355 | 356 | /** 357 | * 358 | * 退出群组 359 | * 360 | * @param {Object} options - 参数对象 361 | * 362 | * { "groupId":"群ID", success: (result) => {}, fail: (error) => {} } 363 | * 364 | * @param {String} options.groupId - 群ID 365 | * @param {(result)=>{}} [options.success] - 成功回调 366 | * @param {(error)=>{}} [options.fail] - 失败回调 367 | * 368 | * @example 369 | * leaveGroup({ 370 | groupId: "", 371 | success: (result) => {}, 372 | fail: (error) => {} 373 | }); 374 | */ 375 | function leaveGroup(options) { 376 | 377 | if (!instance.checkLogged()) { 378 | return errHandle(options, YeIMUniSDKStatusCode.LOGIN_EXPIRE.code, YeIMUniSDKStatusCode.LOGIN_EXPIRE.describe); 379 | } 380 | 381 | if (!options.groupId) { 382 | return errHandle(options, YeIMUniSDKStatusCode.PARAMS_ERROR.code, 'groupId 不能为空'); 383 | } 384 | 385 | request(Api.Group.leave, 'GET', { 386 | groupId: options.groupId 387 | }).then(() => { 388 | successHandle(options, YeIMUniSDKStatusCode.NORMAL_SUCCESS.describe); 389 | }).catch((fail) => { 390 | errHandle(options, fail.code, fail.message); 391 | log(1, fail); 392 | }); 393 | 394 | } 395 | 396 | /** 397 | * 398 | * 添加群成员 399 | * 400 | * 不限权限,IM内用户均可调用此接口,但根据群申请处理方式不同,可能直接进入,也可能等待审核 401 | * 402 | * @param {Object} options - 参数对象 403 | * 404 | * { "groupId":"群ID", "members": ["user1", "user2"], success: (result) => {}, fail: (error) => {} } 405 | * 406 | * @param {String} options.groupId - 群ID 407 | * @param {Array} options.members - 用户ID列表 408 | * @param {(result)=>{}} [options.success] - 成功回调 409 | * @param {(error)=>{}} [options.fail] - 失败回调 410 | * 411 | * @example 412 | * addGroupUsers({ 413 | groupId: "", 414 | members: ["user1", "user2"], 415 | success: (result) => {}, 416 | fail: (error) => {} 417 | }); 418 | */ 419 | function addGroupUsers(options) { 420 | 421 | if (!instance.checkLogged()) { 422 | return errHandle(options, YeIMUniSDKStatusCode.LOGIN_EXPIRE.code, YeIMUniSDKStatusCode.LOGIN_EXPIRE.describe); 423 | } 424 | 425 | if (!options.groupId) { 426 | return errHandle(options, YeIMUniSDKStatusCode.PARAMS_ERROR.code, 'groupId 不能为空'); 427 | } 428 | 429 | if (!options.members || options.members.length <= 0) { 430 | return errHandle(options, YeIMUniSDKStatusCode.PARAMS_ERROR.code, 'members 不能为空'); 431 | } 432 | 433 | request(Api.Group.addUser, 'POST', { 434 | groupId: options.groupId, 435 | members: options.members 436 | }).then((result) => { 437 | successHandle(options, YeIMUniSDKStatusCode.NORMAL_SUCCESS.describe, result); 438 | }).catch((fail) => { 439 | errHandle(options, fail.code, fail.message); 440 | log(1, fail); 441 | }); 442 | 443 | } 444 | 445 | /** 446 | * 447 | * 移除群成员 448 | * 449 | * 仅群主可使用此接口 450 | * 451 | * @param {Object} options - 参数对象 452 | * 453 | * { "groupId":"群ID", "members": ["user1", "user2"], success: (result) => {}, fail: (error) => {} } 454 | * 455 | * @param {String} options.groupId - 群ID 456 | * @param {Array} options.members - 用户ID列表 457 | * @param {(result)=>{}} [options.success] - 成功回调 458 | * @param {(error)=>{}} [options.fail] - 失败回调 459 | * 460 | * @example 461 | * removeGroupUsers({ 462 | groupId: "", 463 | members: ["user1", "user2"], 464 | success: (result) => {}, 465 | fail: (error) => {} 466 | }); 467 | */ 468 | function removeGroupUsers(options) { 469 | 470 | if (!instance.checkLogged()) { 471 | return errHandle(options, YeIMUniSDKStatusCode.LOGIN_EXPIRE.code, YeIMUniSDKStatusCode.LOGIN_EXPIRE.describe); 472 | } 473 | 474 | if (!options.groupId) { 475 | return errHandle(options, YeIMUniSDKStatusCode.PARAMS_ERROR.code, 'groupId 不能为空'); 476 | } 477 | 478 | if (!options.members || options.members.length <= 0) { 479 | return errHandle(options, YeIMUniSDKStatusCode.PARAMS_ERROR.code, 'members 不能为空'); 480 | } 481 | 482 | request(Api.Group.remove, 'POST', { 483 | groupId: options.groupId, 484 | members: options.members 485 | }).then(() => { 486 | successHandle(options, YeIMUniSDKStatusCode.NORMAL_SUCCESS.describe); 487 | }).catch((fail) => { 488 | errHandle(options, fail.code, fail.message); 489 | log(1, fail); 490 | }); 491 | 492 | } 493 | 494 | /** 495 | * 496 | * 获取群成员列表 497 | * 498 | * @param {Object} options - 参数对象 499 | * 500 | * { "groupId":"群ID", success: (result) => {}, fail: (error) => {} } 501 | * 502 | * @param {String} options.groupId - 群ID 503 | * @param {(result)=>{}} [options.success] - 成功回调 504 | * @param {(error)=>{}} [options.fail] - 失败回调 505 | * 506 | * @example 507 | * getGroupUserList({ 508 | groupId: "group_1", 509 | success: (result) => {}, 510 | fail: (error) => {} 511 | }); 512 | */ 513 | function getGroupUserList(options) { 514 | 515 | if (!instance.checkLogged()) { 516 | return errHandle(options, YeIMUniSDKStatusCode.LOGIN_EXPIRE.code, YeIMUniSDKStatusCode.LOGIN_EXPIRE.describe); 517 | } 518 | 519 | if (!options.groupId) { 520 | return errHandle(options, YeIMUniSDKStatusCode.PARAMS_ERROR.code, 'groupId 不能为空'); 521 | } 522 | 523 | request(Api.Group.memberList, 'GET', { 524 | groupId: options.groupId 525 | }).then((result) => { 526 | successHandle(options, YeIMUniSDKStatusCode.NORMAL_SUCCESS.describe, result); 527 | }).catch((fail) => { 528 | errHandle(options, fail.code, fail.message); 529 | log(1, fail); 530 | }); 531 | 532 | } 533 | 534 | /** 535 | * 536 | * 设置群管理员 537 | * 538 | * 仅群主可操作此接口 539 | * 540 | * @param {Object} options - 参数对象 541 | * 542 | * { "groupId":"群ID","userId":"要设置的用户ID","isAdmin":"是否设置管理员,0=取消,1=设置", success: (result) => {}, fail: (error) => {} } 543 | * 544 | * @param {String} options.groupId - 群ID 545 | * @param {String} options.userId - 要设置的用户ID 546 | * @param {Number} options.isAdmin - 是否设置管理员,0=取消,1=设置 547 | * @param {(result)=>{}} [options.success] - 成功回调 548 | * @param {(error)=>{}} [options.fail] - 失败回调 549 | * 550 | * @example 551 | * setAdminstrator({ 552 | groupId: "", 553 | userId: "", 554 | isAdmin: 1, 555 | success: (result) => {}, 556 | fail: (error) => {} 557 | }); 558 | */ 559 | function setAdminstrator(options) { 560 | 561 | if (!instance.checkLogged()) { 562 | return errHandle(options, YeIMUniSDKStatusCode.LOGIN_EXPIRE.code, YeIMUniSDKStatusCode.LOGIN_EXPIRE.describe); 563 | } 564 | 565 | if (!options.groupId) { 566 | return errHandle(options, YeIMUniSDKStatusCode.PARAMS_ERROR.code, 'groupId 不能为空'); 567 | } 568 | 569 | request(Api.Group.adminstrator, 'GET', { 570 | groupId: options.groupId, 571 | userId: options.userId, 572 | isAdmin: isAdmin === 1 ? isAdmin : 0 573 | }).then(() => { 574 | successHandle(options, YeIMUniSDKStatusCode.NORMAL_SUCCESS.describe); 575 | }).catch((fail) => { 576 | errHandle(options, fail.code, fail.message); 577 | log(1, fail); 578 | }); 579 | 580 | } 581 | 582 | /** 583 | * 584 | * 获取群组用户入群申请列表 585 | * 586 | * 群主或管理员调用此接口可获取名下所有群组入群申请列表 587 | * 588 | * @param {Object} options - 参数对象 589 | * @param {(result)=>{}} [options.success] - 成功回调 590 | * @param {(error)=>{}} [options.fail] - 失败回调 591 | * 592 | * @example 593 | * getGroupApplyList({ 594 | success: (result) => {}, 595 | fail: (error) => {} 596 | }); 597 | */ 598 | function getGroupApplyList(options) { 599 | 600 | if (!instance.checkLogged()) { 601 | return errHandle(options, YeIMUniSDKStatusCode.LOGIN_EXPIRE.code, YeIMUniSDKStatusCode.LOGIN_EXPIRE.describe); 602 | } 603 | 604 | if (!options.groupId) { 605 | return errHandle(options, YeIMUniSDKStatusCode.PARAMS_ERROR.code, 'groupId 不能为空'); 606 | } 607 | 608 | request(Api.Group.applyList, 'GET', {}).then((result) => { 609 | successHandle(options, YeIMUniSDKStatusCode.NORMAL_SUCCESS.describe, result); 610 | }).catch((fail) => { 611 | errHandle(options, fail.code, fail.message); 612 | log(1, fail); 613 | }); 614 | 615 | } 616 | 617 | /** 618 | * 619 | * 处理群组用户入群申请 620 | * 621 | * 申请记录所在的群组中群主或管理员可调用此接口处理申请 622 | * 623 | * @param {Object} options - 参数对象 624 | * @param {Number} options.id - 申请记录的ID 625 | * @param {YeIMUniSDKDefines.GROUP.APPLYSTATUS} options.status - 处理结果 626 | * @param {(result)=>{}} [options.success] - 成功回调 627 | * @param {(error)=>{}} [options.fail] - 失败回调 628 | * 629 | * @example 630 | * handleApply({ 631 | id: 5, //申请记录的ID 632 | status: YeIMUniSDKDefines.GROUP.APPLYSTATUS.AGREE, //同意 633 | success: (result) => {}, 634 | fail: (error) => {} 635 | }); 636 | */ 637 | function handleApply(options) { 638 | 639 | if (!instance.checkLogged()) { 640 | return errHandle(options, YeIMUniSDKStatusCode.LOGIN_EXPIRE.code, YeIMUniSDKStatusCode.LOGIN_EXPIRE.describe); 641 | } 642 | 643 | if (!options.id) { 644 | return errHandle(options, YeIMUniSDKStatusCode.PARAMS_ERROR.code, 'id 不能为空'); 645 | } 646 | 647 | if (!options.status) { 648 | return errHandle(options, YeIMUniSDKStatusCode.PARAMS_ERROR.code, 'status 不能为空'); 649 | } 650 | 651 | request(Api.Group.handleApply, 'GET', { 652 | id: options.id, 653 | status: options.status 654 | }).then(() => { 655 | successHandle(options, YeIMUniSDKStatusCode.NORMAL_SUCCESS.describe); 656 | }).catch((fail) => { 657 | errHandle(options, fail.code, fail.message); 658 | log(1, fail); 659 | }); 660 | 661 | } 662 | 663 | /** 664 | * 665 | * 禁言群成员 666 | * 667 | * 群组中群主或管理员可调用此接口禁言群成员,每次设置的分钟数均从操作时间开始算起,设置0则取消禁言 668 | * 669 | * @param {Object} options - 参数对象 670 | * 671 | * { "groupId":"群ID","userId":"禁言用户ID","time":"禁言分钟数", success: (result) => {}, fail: (error) => {} } 672 | * 673 | * @param {String} options.groupId - 群ID 674 | * @param {String} options.userId - 禁言用户ID 675 | * @param {Number} options.time - 禁言分钟数 676 | * @param {(result)=>{}} [options.success] - 成功回调 677 | * @param {(error)=>{}} [options.fail] - 失败回调 678 | * 679 | * @example 680 | * setMute({ 681 | groupId: "group_1", 682 | userId: "user1", 683 | time: 10, 684 | success: (result) => {}, 685 | fail: (error) => {} 686 | }); 687 | */ 688 | function setMute(options) { 689 | 690 | if (!instance.checkLogged()) { 691 | return errHandle(options, YeIMUniSDKStatusCode.LOGIN_EXPIRE.code, YeIMUniSDKStatusCode.LOGIN_EXPIRE.describe); 692 | } 693 | 694 | if (!options.groupId) { 695 | return errHandle(options, YeIMUniSDKStatusCode.PARAMS_ERROR.code, 'groupId 不能为空'); 696 | } 697 | 698 | if (!options.userId) { 699 | return errHandle(options, YeIMUniSDKStatusCode.PARAMS_ERROR.code, 'userId 不能为空'); 700 | } 701 | 702 | if (!options.time) { 703 | options.time = 0; 704 | } 705 | 706 | request(Api.Group.setMute, 'GET', { 707 | groupId: options.groupId, 708 | userId: options.userId, 709 | time: options.time 710 | }).then(() => { 711 | successHandle(options, YeIMUniSDKStatusCode.NORMAL_SUCCESS.describe); 712 | }).catch((fail) => { 713 | errHandle(options, fail.code, fail.message); 714 | log(1, fail); 715 | }); 716 | 717 | } 718 | 719 | export { 720 | createGroup, 721 | dissolveGroup, 722 | getGroup, 723 | getGroupList, 724 | transferLeader, 725 | updateGroup, 726 | joinGroup, 727 | leaveGroup, 728 | addGroupUsers, 729 | getGroupUserList, 730 | removeGroupUsers, 731 | setAdminstrator, 732 | getGroupApplyList, 733 | handleApply, 734 | setMute 735 | } 736 | -------------------------------------------------------------------------------- /src/service/pushService.js: -------------------------------------------------------------------------------- 1 | /*eslint-disable*/ 2 | 3 | import { 4 | instance 5 | } from '../yeim-uni-sdk'; 6 | 7 | import log from '../func/log'; 8 | import { 9 | Api, 10 | request 11 | } from '../func/request'; 12 | 13 | /** 14 | * 检测设备通知权限及跳转系统设置通知页面 15 | */ 16 | function setPushPermissions() { 17 | let systemInfo = uni.getSystemInfoSync(); 18 | if (systemInfo.uniPlatform == 'app' && systemInfo.osName == 19 | 'android') { // 判断是Android 20 | var main = plus.android.runtimeMainActivity(); 21 | var pkName = main.getPackageName(); 22 | var uid = main.getApplicationInfo().plusGetAttribute('uid'); 23 | var NotificationManagerCompat = plus.android.importClass('android.support.v4.app.NotificationManagerCompat'); 24 | //android.support.v4升级为androidx 25 | if (NotificationManagerCompat == null) { 26 | NotificationManagerCompat = plus.android.importClass('androidx.core.app.NotificationManagerCompat'); 27 | } 28 | var areNotificationsEnabled = NotificationManagerCompat.from(main).areNotificationsEnabled(); 29 | 30 | // 未开通‘允许通知’权限,则弹窗提醒开通,并点击确认后,跳转到系统设置页面进行设置 31 | var firstFlag = uni.getStorageSync('first_flag') || false; 32 | if (!areNotificationsEnabled && !firstFlag) { 33 | uni.setStorageSync('first_flag', true); 34 | uni.showModal({ 35 | title: '通知权限开启提醒', 36 | content: '您还没有开启通知权限,无法接受到消息通知,请前往设置!', 37 | showCancel: false, 38 | confirmText: '去设置', 39 | success: function (res) { 40 | if (res.confirm) { 41 | var Intent = plus.android.importClass('android.content.Intent'); 42 | var Build = plus.android.importClass("android.os.Build"); 43 | //android 8.0引导 44 | if (Build.VERSION.SDK_INT >= 26) { 45 | var intent = new Intent('android.settings.APP_NOTIFICATION_SETTINGS'); 46 | intent.putExtra('android.provider.extra.APP_PACKAGE', pkName); 47 | } else if (Build.VERSION.SDK_INT >= 21) { //android 5.0-7.0 48 | var intent = new Intent('android.settings.APP_NOTIFICATION_SETTINGS'); 49 | intent.putExtra('app_package', pkName); 50 | intent.putExtra('app_uid', uid); 51 | } else { //(<21)其他--跳转到该应用管理的详情页 52 | intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS); 53 | var uri = Uri.fromParts('package', mainActivity.getPackageName(), null); 54 | intent.setData(uri); 55 | } 56 | // 跳转到该应用的系统通知设置页 57 | main.startActivity(intent); 58 | } 59 | } 60 | }); 61 | } 62 | } else if (systemInfo.uniPlatform == 'app' && systemInfo.osName == 63 | 'ios') { // 判断是IOS 64 | var isOn = undefined; 65 | var types = 0; 66 | var app = plus.ios.invoke('UIApplication', 'sharedApplication'); 67 | var settings = plus.ios.invoke(app, 'currentUserNotificationSettings'); 68 | if (settings) { 69 | types = settings.plusGetAttribute('types'); 70 | plus.ios.deleteObject(settings); 71 | } else { 72 | types = plus.ios.invoke(app, 'enabledRemoteNotificationTypes'); 73 | } 74 | plus.ios.deleteObject(app); 75 | isOn = (0 != types); 76 | if (isOn == false) { 77 | uni.showModal({ 78 | title: '通知权限开启提醒', 79 | content: '您还没有开启通知权限,无法接受到消息通知,请前往设置!', 80 | showCancel: false, 81 | confirmText: '去设置', 82 | success: function (res) { 83 | if (res.confirm) { 84 | var app = plus.ios.invoke('UIApplication', 'sharedApplication'); 85 | var setting = plus.ios.invoke('NSURL', 'URLWithString:', 'app-settings:'); 86 | plus.ios.invoke(app, 'openURL:', setting); 87 | plus.ios.deleteObject(setting); 88 | plus.ios.deleteObject(app); 89 | } 90 | } 91 | }); 92 | } 93 | } 94 | } 95 | 96 | /** 97 | * 98 | * Android8.0 99 | * 创建推送渠道 100 | * 101 | */ 102 | function createNotificationChannel() { 103 | let systemInfo = uni.getSystemInfoSync(); 104 | if (systemInfo.uniPlatform == 'app' && systemInfo.osName == 105 | 'android' && instance.defaults.notification && instance.defaults.notification.oppoChannelId) { 106 | var Build = plus.android.importClass('android.os.Build'); 107 | if (Build.VERSION.SDK_INT >= 26) { 108 | let oppoChannelId = instance.defaults.notification.oppoChannelId; 109 | let channelName = '聊天离线通知'; 110 | let main = plus.android.runtimeMainActivity(); 111 | let Context = plus.android.importClass('android.content.Context'); 112 | let NotificationManager = plus.android.importClass('android.app.NotificationManager'); 113 | let nManager = main.getSystemService(Context.NOTIFICATION_SERVICE); 114 | let channel = nManager.getNotificationChannel(oppoChannelId); 115 | let NotificationChannel = plus.android.importClass('android.app.NotificationChannel'); 116 | let Notification = plus.android.importClass('android.app.Notification'); 117 | if (channel) { 118 | nManager.deleteNotificationChannel(channel); 119 | } 120 | //OPPO创建本地通道 121 | if (!channel || channel == null || channel == 'null' || channel == undefined) { 122 | let channel = new NotificationChannel(oppoChannelId, channelName, NotificationManager 123 | .IMPORTANCE_HIGH); 124 | channel.setDescription('用于用户离线时推送消息通知'); 125 | channel.enableVibration(true); 126 | channel.enableLights(true); 127 | channel.setBypassDnd(true); 128 | channel.setLockscreenVisibility(Notification.VISIBILITY_SECRET); 129 | nManager.createNotificationChannel(channel); 130 | } 131 | } 132 | } 133 | } 134 | 135 | /** 136 | * 137 | * 注册APP用户推送标识符 138 | * 将个推cid绑定到后端,用于离线推送消息通知 139 | * 140 | */ 141 | function bindAppUserPushCID() { 142 | let systemInfo = uni.getSystemInfoSync(); 143 | if (!instance.checkLogged() || systemInfo.uniPlatform != 'app') { 144 | return; 145 | } 146 | plus.push.getClientInfoAsync((info) => { 147 | let clientId = info['clientid']; 148 | if (clientId) { 149 | request(Api.Push.bindClientId, 'GET', { 150 | clientId: clientId 151 | }).then(() => { 152 | log(0, "注册APP用户离线通知推送标识符成功,当前获取的cid为:" + clientId); 153 | }).catch((fail) => { 154 | log(1, "注册APP用户离线通知推送标识符异常:" + fail.message); 155 | }) 156 | } 157 | }); 158 | } 159 | 160 | 161 | export { 162 | setPushPermissions, 163 | createNotificationChannel, 164 | bindAppUserPushCID 165 | } 166 | -------------------------------------------------------------------------------- /src/service/uploadService.js: -------------------------------------------------------------------------------- 1 | import { 2 | instance 3 | } from '../yeim-uni-sdk'; 4 | 5 | import { 6 | successHandle, 7 | errHandle 8 | } from '../func/callback'; 9 | 10 | import md5 from '../utils/md5'; 11 | 12 | import log from '../func/log'; 13 | 14 | import CryptoJS from '../utils/CryptoJS'; 15 | 16 | import { 17 | YeIMUniSDKStatusCode 18 | } from '../const/yeim-status-code'; 19 | 20 | import { 21 | Api, 22 | download, 23 | request, 24 | upload as commonUpload 25 | } from '../func/request'; 26 | 27 | /** 28 | * 获取媒体上传参数 29 | */ 30 | async function getMediaUploadParams() { 31 | 32 | let response = await request(Api.Upload.sign, 'GET', {}); 33 | if (response.code) { 34 | log(1, '媒体上传参数获取失败', true); 35 | } else { 36 | instance.mediaUploadParams = response; 37 | } 38 | 39 | } 40 | 41 | /** 42 | * 获取上传URL 43 | */ 44 | function getUploadURL() { 45 | //默认本地上传URL 46 | let url = instance.defaults.baseURL; 47 | if (instance.mediaUploadParams.storage == "cos") { 48 | url = `https://${instance.mediaUploadParams.bucket}.cos.${instance.mediaUploadParams.region}.myqcloud.com`; 49 | } else if (instance.mediaUploadParams.storage == "oss") { 50 | url = `https://${instance.mediaUploadParams.bucket}.${instance.mediaUploadParams.region}.aliyuncs.com`; 51 | } 52 | return url; 53 | } 54 | 55 | /** 56 | * 获取访问URL 57 | */ 58 | function getVisitURL() { 59 | return (instance.mediaUploadParams.customDomain ? instance.mediaUploadParams.customDomain : getUploadURL()); 60 | } 61 | 62 | /** 63 | * 获取上传的文件名称带路径 64 | * 65 | * 拼接上传的文件保存目录,默认规则 baseDir/文件类型/日期/文件名称 66 | * 67 | * @param {String} filename 文件名称 68 | * @param {String} dir 文件类型,目前可选为image、video、audio、files 69 | */ 70 | function getUploadFilePath(filename, dir = 'files') { 71 | let key = filename; 72 | //完整文件名,如果设置了baseDir,则 = baseDir/filename,否则 = filename 73 | if (!instance.mediaUploadParams.baseDir && !instance.mediaUploadParams.storage === 'local') { 74 | return key; 75 | } else { 76 | //文件保存根目录 77 | let baseDir = instance.mediaUploadParams.baseDir; 78 | //日期目录 79 | let date = new Date(); 80 | //修复Android上日期显示异常问题 81 | let dateDir = date.getFullYear() + '-' + JSON.stringify(date.getMonth() + 1).padStart(2, 0) + '-' + JSON 82 | .stringify(date.getDate()).padStart(2, 0); 83 | //后半路径 84 | let suffix = dir + '/' + dateDir + '/' + filename; 85 | if (instance.mediaUploadParams.storage !== 'local') { 86 | //去除路径前的/ 87 | if (baseDir.substring(0, 1) == '/') { 88 | baseDir = baseDir.substring(1); 89 | } 90 | //去除路径后的/ 91 | if (baseDir.substring(baseDir.length - 1, baseDir.length) === '/') { 92 | baseDir = baseDir.substring(0, baseDir.length) + baseDir.substring(baseDir.length + 1); 93 | } 94 | //如果baseDir没了,返回不带baseDir的后半路径 95 | if (!baseDir) { 96 | return suffix; 97 | } 98 | //公有云对象存储最终保存路径 99 | key = baseDir + '/' + suffix; 100 | } else { 101 | //本地存储最终保存路径 102 | key = suffix; 103 | } 104 | } 105 | return key; 106 | } 107 | 108 | /** 109 | * 生成腾讯云COS 对象存储 请求签名 Authorization 110 | * @url https://cloud.tencent.com/document/product/436/7778 111 | * 112 | * @param {String} method @description 请求方法名称 113 | * @param {String} path @description url-param-list 114 | * @param {String} headers @description q-header-list 115 | */ 116 | async function buildCosAuthorization(method, path, headers) { 117 | let time = parseInt((new Date()).getTime() / 1000) - 1; 118 | if (!instance.mediaUploadParams || time > instance.mediaUploadParams.expireTime) { 119 | //过期,重新获取 120 | await getMediaUploadParams(); 121 | } 122 | let nowTime = instance.mediaUploadParams.nowTime; 123 | let expireTime = instance.mediaUploadParams.expireTime; 124 | let qSignAlgorithm = 'sha1'; 125 | let qAk = instance.mediaUploadParams.secretId; 126 | let qSignTime = nowTime + ';' + expireTime; 127 | let qKeyTime = nowTime + ';' + expireTime; 128 | let signKey = instance.mediaUploadParams.signKey; 129 | let httpString = method + '\n' + path + '\n\n' + headers + '\n'; 130 | let stringToSign = qSignAlgorithm + '\n' + qKeyTime + '\n' + CryptoJS.SHA1(httpString) + '\n'; 131 | let signature = CryptoJS.HmacSHA1(stringToSign, signKey); 132 | let authorization = 133 | `q-sign-algorithm=${qSignAlgorithm}&q-ak=${qAk}&q-sign-time=${qSignTime}&q-key-time=${qKeyTime}&q-header-list=&q-url-param-list=&q-signature=${signature}`; 134 | return authorization; 135 | } 136 | 137 | /** 138 | * 生成阿里云OSS 对象存储 请求签名 signature 139 | * @url https://help.aliyun.com/document_detail/31988.html 140 | */ 141 | async function buildOSSSignature() { 142 | let time = parseInt((new Date()).getTime() / 1000) - 1; 143 | if (!instance.mediaUploadParams || time > instance.mediaUploadParams.expireTime) { 144 | //过期,重新获取 145 | await getMediaUploadParams(); 146 | } 147 | return instance.mediaUploadParams; 148 | } 149 | 150 | /** 151 | * 152 | * 通用上传接口 153 | * 154 | * @param {Object} options 155 | * @param {String} options.filename - 文件名称(需带后缀) 156 | * @param {String} options.filepath - 本地文件临时路径 157 | * @param {(result)=>{}} [options.success] - 成功回调 158 | * @param {(error)=>{}} [options.fail] - 失败回调 159 | * @param {(progress)=>{}} [options.onProgress] - 上传进度回调 160 | * 161 | */ 162 | function upload(options) { 163 | 164 | if (!instance.checkLogged()) { 165 | return errHandle(options, YeIMUniSDKStatusCode.LOGIN_EXPIRE.code, YeIMUniSDKStatusCode.LOGIN_EXPIRE.describe); 166 | } 167 | 168 | if (!instance.mediaUploadParams) { 169 | return errHandle(options, YeIMUniSDKStatusCode.PARAMS_ERROR.code, '上传参数异常,暂无法使用此接口'); 170 | } 171 | 172 | let mediaUploadParams = instance.mediaUploadParams; 173 | let suffix = options.filename.substring(options.filename.lastIndexOf('.')); 174 | let filename = md5((new Date()).getTime() + '_' + options.filename) + '_other' + suffix; 175 | 176 | //上传URL 177 | let uploadUrl = getUploadURL(); 178 | 179 | //上传完成后的资源访问URL 180 | let resUrl = getVisitURL() + '/' + getUploadFilePath(filename); 181 | 182 | //腾讯云COS对象存储 183 | if (mediaUploadParams.storage === 'cos') { 184 | setTimeout(async () => { 185 | let authorization = await buildCosAuthorization('post', '/', ''); 186 | let uploadTask = commonUpload({ 187 | url: uploadUrl, 188 | name: 'file', 189 | data: { 190 | 'key': getUploadFilePath(filename), 191 | 'success_action_status': 200, 192 | 'Signature': authorization, 193 | 'Content-Type': '' 194 | }, 195 | header: { 196 | Authorization: authorization, 197 | }, 198 | filePath: options.filepath, 199 | ignoreResult: true, 200 | success: () => { 201 | successHandle(options, YeIMUniSDKStatusCode.NORMAL_SUCCESS.describe, { 202 | url: resUrl 203 | }) 204 | }, 205 | fail: () => { 206 | errHandle(options, YeIMUniSDKStatusCode.COS_UPLOAD_ERROR.code, 207 | YeIMUniSDKStatusCode.COS_UPLOAD_ERROR.describe); 208 | } 209 | }); 210 | //上传进度回调 211 | if (options.onProgress !== undefined && typeof options.onProgress === "function") { 212 | uploadTask.onProgressUpdate((res) => { 213 | options.onProgress(res); 214 | }); 215 | } 216 | }, 0); 217 | } else if (mediaUploadParams.storage === 'oss') { 218 | //阿里云对象存储 219 | setTimeout(async () => { 220 | await buildOSSSignature(); 221 | let uploadTask = commonUpload({ 222 | url: uploadUrl, 223 | name: 'file', 224 | data: { 225 | 'key': getUploadFilePath(filename), 226 | 'policy': mediaUploadParams.policyBase64, 227 | 'OSSAccessKeyId': mediaUploadParams.accessKeyId, 228 | 'success_action_status': 200, 229 | 'signature': mediaUploadParams.signature, 230 | }, 231 | filePath: options.filepath, 232 | ignoreResult: true, 233 | success: () => { 234 | successHandle(options, YeIMUniSDKStatusCode.NORMAL_SUCCESS.describe, { 235 | url: resUrl 236 | }) 237 | }, 238 | fail: () => { 239 | errHandle(options, YeIMUniSDKStatusCode.OSS_UPLOAD_ERROR.code, 240 | YeIMUniSDKStatusCode.OSS_UPLOAD_ERROR.describe); 241 | } 242 | }) 243 | //上传进度回调 244 | if (options.onProgress !== undefined && typeof options.onProgress === 'function') { 245 | uploadTask.onProgressUpdate((res) => { 246 | options.onProgress(res); 247 | }); 248 | } 249 | }); 250 | } else if (mediaUploadParams.storage === 'local') { 251 | //本地上传 252 | let uploadTask = commonUpload({ 253 | url: uploadUrl + Api.Upload.normal, 254 | name: 'file', 255 | data: { 256 | 'key': getUploadFilePath(filename) 257 | }, 258 | header: { 259 | 'token': instance.token 260 | }, 261 | filePath: options.filepath, 262 | ignoreResult: false, 263 | success: (result) => { 264 | successHandle(options, YeIMUniSDKStatusCode.NORMAL_SUCCESS.describe, { 265 | url: getVisitURL() + result.data.url, 266 | }) 267 | }, 268 | fail: () => { 269 | errHandle(options, YeIMUniSDKStatusCode.UPLOAD_ERROR.code, YeIMUniSDKStatusCode.UPLOAD_ERROR 270 | .describe); 271 | } 272 | }) 273 | //上传进度回调 274 | if (options.onProgress !== undefined && typeof options.onProgress === 'function') { 275 | uploadTask.onProgressUpdate((res) => { 276 | options.onProgress(res); 277 | }); 278 | } 279 | } 280 | 281 | } 282 | 283 | /** 284 | * 285 | * 上传图片 286 | * 外部暴露 287 | * 288 | * @param {Object} options 289 | * @param {String} options.filename @description 文件名称 290 | * @param {String} options.filepath @description 本地图片文件临时路径 291 | * @param {Number} options.width @description 图片宽度 292 | * @param {Number} options.height @description 图片高度 293 | * @param {(result)=>{}} [options.success] - 成功回调 294 | * @param {(error)=>{}} [options.fail] - 失败回调 295 | * @param {(progress)=>{}} options.onProgress @description 上传进度回调 296 | * 297 | */ 298 | function uploadImage(options) { 299 | 300 | if (!instance.mediaUploadParams) { 301 | return errHandle(options, YeIMUniSDKStatusCode.PARAMS_ERROR.code, '上传参数异常,暂无法使用此接口'); 302 | } 303 | 304 | let mediaUploadParams = instance.mediaUploadParams; 305 | let suffix = options.filename.substring(options.filename.lastIndexOf('.')); 306 | let filename = md5((new Date()).getTime() + '_' + options.filename) + '_image' + suffix; 307 | 308 | //上传URL 309 | let uploadUrl = getUploadURL(); 310 | //上传完成后的资源访问URL 311 | let resUrl = getVisitURL() + '/' + getUploadFilePath(filename, 'image'); 312 | 313 | //腾讯云COS对象存储 314 | if (mediaUploadParams.storage === 'cos') { 315 | 316 | setTimeout(async () => { 317 | let authorization = await buildCosAuthorization('post', '/', ''); 318 | 319 | let uploadTask = commonUpload({ 320 | url: uploadUrl, 321 | name: 'file', 322 | data: { 323 | 'key': getUploadFilePath(filename, 'image'), 324 | 'success_action_status': 200, 325 | 'Signature': authorization, 326 | 'Content-Type': '' 327 | }, 328 | header: { 329 | Authorization: authorization, 330 | }, 331 | filePath: options.filepath, 332 | ignoreResult: true, 333 | success: () => { 334 | let thumbnailHeight = 0; 335 | let thumbnailWidth = 0; 336 | 337 | if (options.height > 198) { 338 | let d = 198 / options.height; 339 | thumbnailHeight = parseInt(options.height * 340 | d); 341 | thumbnailWidth = parseInt(options.width * d); 342 | } else if (options.width > 198) { 343 | let d = 198 / options.width; 344 | thumbnailHeight = parseInt(options.height * 345 | d); 346 | thumbnailWidth = parseInt(options.width * d); 347 | } 348 | 349 | let thumbnailUrl = resUrl + '?imageMogr2/thumbnail/' + thumbnailWidth + 350 | 'x' + thumbnailHeight; 351 | 352 | successHandle(options, YeIMUniSDKStatusCode.NORMAL_SUCCESS.describe, { 353 | url: resUrl, 354 | thumbnailUrl: thumbnailUrl, 355 | thumbnailWidth: thumbnailWidth, 356 | thumbnailHeight: thumbnailHeight 357 | }) 358 | }, 359 | fail: () => { 360 | errHandle(options, YeIMUniSDKStatusCode.COS_UPLOAD_ERROR.code, 361 | YeIMUniSDKStatusCode.COS_UPLOAD_ERROR.describe); 362 | } 363 | }); 364 | if (options.onProgress !== undefined && typeof options.onProgress === 'function') { 365 | uploadTask.onProgressUpdate((res) => { 366 | options.onProgress(res); 367 | }); 368 | } 369 | }, 0); 370 | } else if (mediaUploadParams.storage === 'oss') { 371 | 372 | //阿里云对象存储 373 | setTimeout(async () => { 374 | await buildOSSSignature(); 375 | let uploadTask = commonUpload({ 376 | url: uploadUrl, 377 | name: 'file', 378 | data: { 379 | 'key': getUploadFilePath(filename, 'image'), 380 | 'policy': mediaUploadParams.policyBase64, 381 | 'OSSAccessKeyId': mediaUploadParams.accessKeyId, 382 | 'success_action_status': 200, 383 | 'signature': mediaUploadParams.signature, 384 | }, 385 | filePath: options.filepath, 386 | ignoreResult: true, 387 | success: () => { 388 | //阿里云图片缩放 389 | let thumbnailHeight = 0; 390 | let thumbnailWidth = 0; 391 | 392 | if (options.height > 198) { 393 | let d = 198 / options.height; 394 | thumbnailHeight = parseInt(options.height * 395 | d); 396 | thumbnailWidth = parseInt(options.width * d); 397 | } else if (options.width > 198) { 398 | let d = 198 / options.width; 399 | thumbnailHeight = parseInt(options.height * 400 | d); 401 | thumbnailWidth = parseInt(options.width * d); 402 | } 403 | 404 | let thumbnailUrl = resUrl + '?x-oss-process=image/resize,m_fixed,h_' + 405 | thumbnailHeight + ',w_' + thumbnailWidth; 406 | 407 | successHandle(options, YeIMUniSDKStatusCode.NORMAL_SUCCESS.describe, { 408 | url: resUrl, 409 | thumbnailUrl: thumbnailUrl, 410 | thumbnailWidth: thumbnailWidth, 411 | thumbnailHeight: thumbnailHeight 412 | }) 413 | }, 414 | fail: () => { 415 | errHandle(options, YeIMUniSDKStatusCode.OSS_UPLOAD_ERROR.code, 416 | YeIMUniSDKStatusCode.OSS_UPLOAD_ERROR.describe); 417 | } 418 | }); 419 | if (options.onProgress !== undefined && typeof options.onProgress === 'function') { 420 | uploadTask.onProgressUpdate((res) => { 421 | options.onProgress(res); 422 | }); 423 | } 424 | }); 425 | } else if (mediaUploadParams.storage === 'local') { 426 | //本地上传 427 | let uploadTask = commonUpload({ 428 | url: uploadUrl + Api.Upload.image, 429 | name: 'file', 430 | data: { 431 | 'key': getUploadFilePath(filename, 'image') 432 | }, 433 | header: { 434 | 'token': instance.token 435 | }, 436 | filePath: options.filepath, 437 | ignoreResult: false, 438 | success: (result) => { 439 | successHandle(options, YeIMUniSDKStatusCode.NORMAL_SUCCESS.describe, { 440 | url: getVisitURL() + result.data.url, 441 | thumbnailUrl: getVisitURL() + result.data.thumbnailUrl, 442 | thumbnailWidth: result.data.thumbnailWidth, 443 | thumbnailHeight: result.data.thumbnailHeight 444 | }) 445 | }, 446 | fail: () => { 447 | errHandle(options, YeIMUniSDKStatusCode.UPLOAD_ERROR.code, YeIMUniSDKStatusCode.UPLOAD_ERROR 448 | .describe); 449 | } 450 | }); 451 | if (options.onProgress !== undefined && typeof options.onProgress === 'function') { 452 | uploadTask.onProgressUpdate((res) => { 453 | options.onProgress(res); 454 | }); 455 | } 456 | } 457 | } 458 | 459 | /** 460 | * 上传音频 461 | * 462 | * @param {Object} options 463 | * @param {String} options.filename @description 文件名称 464 | * @param {String} options.filepath @description 本地文件临时路径 465 | * @param {Function} options.onProgress @description 上传进度回调 466 | */ 467 | function uploadAudio(options) { 468 | 469 | if (!instance.mediaUploadParams) { 470 | return errHandle(options, YeIMUniSDKStatusCode.PARAMS_ERROR.code, '上传参数异常,暂无法使用此接口'); 471 | } 472 | 473 | let mediaUploadParams = instance.mediaUploadParams; 474 | let suffix = options.filename.substring(options.filename.lastIndexOf('.')); 475 | let filename = md5((new Date()).getTime() + '_' + options.filename) + '_audio' + suffix; 476 | 477 | //上传URL 478 | let uploadUrl = getUploadURL(); 479 | //上传完成后的资源访问URL 480 | let resUrl = getVisitURL() + '/' + getUploadFilePath(filename, 'audio'); 481 | 482 | //腾讯云COS对象存储 483 | if (mediaUploadParams.storage === 'cos') { 484 | 485 | setTimeout(async () => { 486 | let authorization = await buildCosAuthorization('post', '/', ''); 487 | let uploadTask = commonUpload({ 488 | url: uploadUrl, 489 | name: 'file', 490 | data: { 491 | 'key': getUploadFilePath(filename, 'audio'), 492 | 'success_action_status': 200, 493 | 'Signature': authorization, 494 | 'Content-Type': '' 495 | }, 496 | header: { 497 | Authorization: authorization, 498 | }, 499 | filePath: options.filepath, 500 | ignoreResult: true, 501 | success: () => { 502 | successHandle(options, YeIMUniSDKStatusCode.NORMAL_SUCCESS.describe, { 503 | url: resUrl 504 | }) 505 | }, 506 | fail: () => { 507 | errHandle(options, YeIMUniSDKStatusCode.COS_UPLOAD_ERROR.code, 508 | YeIMUniSDKStatusCode.COS_UPLOAD_ERROR.describe); 509 | } 510 | }); 511 | if (options.onProgress !== undefined && typeof options.onProgress === 'function') { 512 | uploadTask.onProgressUpdate((res) => { 513 | options.onProgress(res); 514 | }); 515 | } 516 | }, 0); 517 | } else if (mediaUploadParams.storage === 'oss') { 518 | 519 | //阿里云对象存储 520 | setTimeout(async () => { 521 | await buildOSSSignature(); 522 | let uploadTask = commonUpload({ 523 | url: uploadUrl, 524 | name: 'file', 525 | data: { 526 | 'key': getUploadFilePath(filename, 'audio'), 527 | 'policy': mediaUploadParams.policyBase64, 528 | 'OSSAccessKeyId': mediaUploadParams.accessKeyId, 529 | 'success_action_status': 200, 530 | 'signature': mediaUploadParams.signature, 531 | }, 532 | filePath: options.filepath, 533 | ignoreResult: true, 534 | success: () => { 535 | successHandle(options, YeIMUniSDKStatusCode.NORMAL_SUCCESS.describe, { 536 | url: resUrl 537 | }) 538 | }, 539 | fail: () => { 540 | errHandle(options, YeIMUniSDKStatusCode.OSS_UPLOAD_ERROR.code, 541 | YeIMUniSDKStatusCode.OSS_UPLOAD_ERROR.describe); 542 | } 543 | }); 544 | if (options.onProgress !== undefined && typeof options.onProgress === 'function') { 545 | uploadTask.onProgressUpdate((res) => { 546 | options.onProgress(res); 547 | }); 548 | } 549 | }); 550 | } else if (mediaUploadParams.storage === 'local') { 551 | //本地上传 552 | let uploadTask = commonUpload({ 553 | url: uploadUrl + "/upload", 554 | name: 'file', 555 | data: { 556 | 'key': getUploadFilePath(filename, "audio") 557 | }, 558 | header: { 559 | 'token': instance.token 560 | }, 561 | filePath: options.filepath, 562 | ignoreResult: false, 563 | success: (result) => { 564 | successHandle(options, YeIMUniSDKStatusCode.NORMAL_SUCCESS.describe, { 565 | url: getVisitURL() + result.data.url, 566 | }) 567 | }, 568 | fail: () => { 569 | errHandle(options, YeIMUniSDKStatusCode.UPLOAD_ERROR.code, YeIMUniSDKStatusCode.UPLOAD_ERROR 570 | .describe); 571 | } 572 | }); 573 | if (options.onProgress !== undefined && typeof options.onProgress === 'function') { 574 | uploadTask.onProgressUpdate((res) => { 575 | options.onProgress(res); 576 | }); 577 | } 578 | } 579 | 580 | } 581 | 582 | 583 | /** 584 | * 上传视频 585 | * 586 | * @param {Object} options 587 | * @param {String} options.filename - 文件名称 588 | * @param {String} options.filepath - 本地文件临时路径 589 | * @param {(result)=>{}} options.onProgress - 上传进度回调 590 | */ 591 | function uploadVideo(options) { 592 | if (!instance.mediaUploadParams) { 593 | return log(1, '媒体上传参数获取失败', true); 594 | } 595 | let mediaUploadParams = instance.mediaUploadParams; 596 | let suffix = options.filename.substring(options.filename.lastIndexOf('.')); 597 | let filename = md5((new Date()).getTime() + '_' + options.filename) + '_video' + suffix; 598 | 599 | //上传URL 600 | let uploadUrl = getUploadURL(); 601 | //上传完成后的资源访问URL 602 | let resUrl = getVisitURL() + '/' + getUploadFilePath(filename, 'video'); 603 | 604 | //腾讯云COS对象存储 605 | if (mediaUploadParams.storage == "cos") { 606 | setTimeout(async () => { 607 | let postAuthorization = await buildCosAuthorization('post', '/', ''); 608 | let uploadTask = commonUpload({ 609 | url: uploadUrl, 610 | name: 'file', 611 | data: { 612 | 'key': getUploadFilePath(filename, 'video'), 613 | 'success_action_status': 200, 614 | 'Signature': postAuthorization, 615 | 'Content-Type': '' 616 | }, 617 | header: { 618 | Authorization: postAuthorization, 619 | }, 620 | filePath: options.filepath, 621 | ignoreResult: true, 622 | success: async () => { 623 | let getAuthorization = await buildCosAuthorization('get', '/' + 624 | getUploadFilePath(filename, 'video'), ''); 625 | //下载视频缩略图,腾讯云COS 媒体截图接口:https://cloud.tencent.com/document/product/436/55671 626 | download({ 627 | url: uploadUrl + '/' + getUploadFilePath(filename, 'video') + 628 | '?ci-process=snapshot&time=1', 629 | header: { 630 | 'Authorization': getAuthorization 631 | }, 632 | success: (result) => { 633 | uploadImage({ 634 | filename: md5(filename + result.data) + 635 | '_videoThumb.jpg', 636 | filepath: result.data, 637 | success: (thumb) => { 638 | successHandle(options, 639 | YeIMUniSDKStatusCode 640 | .NORMAL_SUCCESS.describe, { 641 | videoUrl: resUrl, 642 | thumbnailUrl: thumb 643 | .data.url 644 | }); 645 | }, 646 | fail: (err) => { 647 | errHandle(options, 648 | YeIMUniSDKStatusCode 649 | .NORMAL_ERROR.code, err); 650 | } 651 | }) 652 | }, 653 | fail: () => { 654 | errHandle(options, YeIMUniSDKStatusCode 655 | .COS_DOWNLOAD_ERROR_1 656 | .code, YeIMUniSDKStatusCode.COS_DOWNLOAD_ERROR_1 657 | .describe); 658 | } 659 | }); 660 | }, 661 | fail: () => { 662 | errHandle(options, YeIMUniSDKStatusCode.COS_UPLOAD_ERROR.code, 663 | YeIMUniSDKStatusCode.COS_UPLOAD_ERROR.describe); 664 | } 665 | }); 666 | if (options.onProgress !== undefined && typeof options.onProgress === 'function') { 667 | uploadTask.onProgressUpdate((res) => { 668 | options.onProgress(res); 669 | }); 670 | } 671 | }, 0); 672 | } else if (mediaUploadParams.storage === 'oss') { 673 | 674 | //阿里云对象存储 675 | setTimeout(async () => { 676 | await buildOSSSignature(); 677 | let uploadTask = commonUpload({ 678 | url: uploadUrl, 679 | name: 'file', 680 | data: { 681 | 'key': getUploadFilePath(filename, 'video'), 682 | 'policy': mediaUploadParams.policyBase64, 683 | 'OSSAccessKeyId': mediaUploadParams.accessKeyId, 684 | 'success_action_status': 200, 685 | 'signature': mediaUploadParams.signature, 686 | }, 687 | filePath: options.filepath, 688 | ignoreResult: true, 689 | success: () => { 690 | //阿里云视频截帧 691 | //?x-oss-process=video/snapshot,t_1000,f_jpg,m_fast 692 | successHandle(options, YeIMUniSDKStatusCode.NORMAL_SUCCESS.describe, { 693 | videoUrl: resUrl, 694 | thumbnailUrl: resUrl + 695 | '?x-oss-process=video/snapshot,t_1000,f_jpg,m_fast' 696 | }) 697 | }, 698 | fail: () => { 699 | errHandle(options, YeIMUniSDKStatusCode.OSS_UPLOAD_ERROR.code, 700 | YeIMUniSDKStatusCode.OSS_UPLOAD_ERROR.describe); 701 | } 702 | }); 703 | if (options.onProgress !== undefined && typeof options.onProgress === 'function') { 704 | uploadTask.onProgressUpdate((res) => { 705 | options.onProgress(res); 706 | }); 707 | } 708 | }); 709 | } else if (mediaUploadParams.storage === 'local') { 710 | //本地上传 711 | let uploadTask = commonUpload({ 712 | url: uploadUrl + Api.Upload.video, 713 | name: 'file', 714 | data: { 715 | 'key': getUploadFilePath(filename, 'video') 716 | }, 717 | header: { 718 | 'token': instance.token 719 | }, 720 | filePath: options.filepath, 721 | ignoreResult: false, 722 | success: (result) => { 723 | successHandle(options, YeIMUniSDKStatusCode.NORMAL_SUCCESS.describe, { 724 | videoUrl: getVisitURL() + result.data.url, 725 | thumbnailUrl: getVisitURL() + result.data.thumbnailUrl 726 | }) 727 | }, 728 | fail: () => { 729 | errHandle(options, YeIMUniSDKStatusCode.UPLOAD_ERROR.code, YeIMUniSDKStatusCode.UPLOAD_ERROR 730 | .describe); 731 | } 732 | }); 733 | if (options.onProgress !== undefined && typeof options.onProgress === 'function') { 734 | uploadTask.onProgressUpdate((res) => { 735 | options.onProgress(res); 736 | }); 737 | } 738 | } 739 | } 740 | 741 | export { 742 | getMediaUploadParams, 743 | upload, 744 | uploadImage, 745 | uploadAudio, 746 | uploadVideo 747 | } -------------------------------------------------------------------------------- /src/service/userService.js: -------------------------------------------------------------------------------- 1 | import { 2 | YeIMUniSDKDefines, 3 | instance 4 | } from '../yeim-uni-sdk'; 5 | import { 6 | Api, 7 | request 8 | } from '../func/request'; 9 | import { 10 | successHandle, 11 | errHandle 12 | } from '../func/callback'; 13 | import { 14 | YeIMUniSDKStatusCode 15 | } from '../const/yeim-status-code'; 16 | import { 17 | getCache, 18 | setCache 19 | } from '../func/storage'; 20 | import md5 from '../utils/md5'; 21 | 22 | /** 23 | * 24 | * 获取用户信息 25 | * 26 | * @param {Object} options - 参数对象 27 | * 28 | * @param {String} options.userId - 用户ID 29 | * @param {Boolean} options.cloud - 从云端获取 30 | * @param {(result)=>{}} [options.success] - 成功回调 31 | * @param {(error)=>{}} [options.fail] - 失败回调 32 | * 33 | */ 34 | function getUserInfo(options) { 35 | 36 | if (!instance.checkLogged()) { 37 | return errHandle(options, YeIMUniSDKStatusCode.LOGIN_EXPIRE.code, YeIMUniSDKStatusCode.LOGIN_EXPIRE.describe); 38 | } 39 | 40 | if (!options.userId) { 41 | return errHandle(options, YeIMUniSDKStatusCode.PARAMS_ERROR.code, 'userId 不能为空'); 42 | } 43 | 44 | let key = `yeim:userList:${md5(instance.userId)}`; 45 | 46 | if (options.cloud) { 47 | request(Api.User.fetchUserInfoById, 'GET', { 48 | userId: options.userId 49 | }).then((result) => { 50 | //保存到本地 51 | let list = getCache(key); 52 | list = list ? list : []; 53 | list.push(result); 54 | setCache(key, list); 55 | successHandle(options, YeIMUniSDKStatusCode.NORMAL_SUCCESS.describe, result); 56 | }).catch((fail) => { 57 | errHandle(options, fail.code, fail.message); 58 | log(1, fail); 59 | }); 60 | } else { 61 | //查询本地缓存 62 | let list = getCache(key); 63 | list = list ? list : []; 64 | let index = list.findIndex(item => { 65 | return item.userId === options.userId; 66 | }); 67 | if (index !== -1) { 68 | //返回本地缓存 69 | successHandle(options, YeIMUniSDKStatusCode.NORMAL_SUCCESS.describe, list[index]); 70 | //从云端更新一次本地缓存 71 | request(Api.User.fetchUserInfoById, 'GET', { 72 | userId: options.userId 73 | }).then((result) => { 74 | let list = getCache(key); 75 | list = list ? list : []; 76 | list[index] = result; 77 | setCache(key, list); 78 | }).catch((fail) => { 79 | errHandle(options, fail.code, fail.message); 80 | log(1, fail); 81 | }); 82 | } else { 83 | //本地没有缓存则从云端获取 84 | request(Api.User.fetchUserInfoById, 'GET', { 85 | userId: options.userId 86 | }).then((result) => { 87 | //保存到本地 88 | let list = getCache(key); 89 | list = list ? list : []; 90 | list.push(result); 91 | setCache(key, list); 92 | successHandle(options, YeIMUniSDKStatusCode.NORMAL_SUCCESS.describe, result); 93 | }).catch((fail) => { 94 | errHandle(options, fail.code, fail.message); 95 | log(1, fail); 96 | }); 97 | } 98 | } 99 | 100 | } 101 | 102 | /** 103 | * 104 | * 更新我的用户资料 105 | * 106 | * @param {Object} options - 参数对象 107 | * 108 | * @param {String} options.nickname - 昵称 109 | * @param {String} options.avatarUrl - 头像地址 110 | * @param {Number} options.gender - 性别,0=未知,1=男性,2=女性 111 | * @param {Number} options.mobile - 电话 112 | * @param {String} options.email - 邮箱 113 | * @param {String} options.birthday - 生日 114 | * @param {String} options.motto - 个性签名 115 | * @param {String} options.extend - 用户自定义扩展字段 116 | * @param {Number} options.addFriendType - 好友添加我的方式 117 | * @param {(result)=>{}} [options.success] - 成功回调 118 | * @param {(error)=>{}} [options.fail] - 失败回调 119 | * 120 | */ 121 | function updateUserInfo(options) { 122 | 123 | if (!instance.checkLogged()) { 124 | return errHandle(options, YeIMUniSDKStatusCode.LOGIN_EXPIRE.code, YeIMUniSDKStatusCode.LOGIN_EXPIRE.describe); 125 | } 126 | 127 | if (Object.keys(options).length <= 0) { 128 | return errHandle(options, YeIMUniSDKStatusCode.PARAMS_ERROR.code, '请至少选择一个属性进行更新'); 129 | } 130 | 131 | request(Api.User.updateUserInfo, 'POST', options).then(() => { 132 | successHandle(options, YeIMUniSDKStatusCode.NORMAL_SUCCESS.describe); 133 | }).catch((fail) => { 134 | errHandle(options, fail.code, fail.message); 135 | log(1, fail); 136 | }); 137 | 138 | } 139 | 140 | /** 141 | * 142 | * 获取黑名单列表 143 | * 144 | * @param {Object} options - 参数对象 145 | * 146 | * @param {(result)=>{}} [options.success] - 成功回调 147 | * @param {(error)=>{}} [options.fail] - 失败回调 148 | * 149 | */ 150 | function getBlackUserList(options) { 151 | 152 | if (!instance.checkLogged()) { 153 | return errHandle(options, YeIMUniSDKStatusCode.LOGIN_EXPIRE.code, YeIMUniSDKStatusCode.LOGIN_EXPIRE.describe); 154 | } 155 | 156 | request(Api.User.getBlackUserList, 'GET', {}).then((result) => { 157 | successHandle(options, YeIMUniSDKStatusCode.NORMAL_SUCCESS.describe, result); 158 | }).catch((fail) => { 159 | errHandle(options, fail.code, fail.message); 160 | log(1, fail); 161 | }); 162 | 163 | } 164 | 165 | /** 166 | * 167 | * 添加用户到黑名单 168 | * 169 | * 170 | * @param {Object} options - 参数对象 171 | * 172 | * {"members": ["user1", "user2"], success: (result) => {}, fail: (error) => {} } 173 | 174 | * 175 | * @param {Array} options.members - 用户ID列表 176 | * @param {(result)=>{}} [options.success] - 成功回调 177 | * @param {(error)=>{}} [options.fail] - 失败回调 178 | * 179 | * @example 180 | * addToBlackUserList({ 181 | members: ["user1", "user2"], 182 | success: (result) => {}, 183 | fail: (error) => {} 184 | }); 185 | */ 186 | function addToBlackUserList(options) { 187 | 188 | if (!instance.checkLogged()) { 189 | return errHandle(options, YeIMUniSDKStatusCode.LOGIN_EXPIRE.code, YeIMUniSDKStatusCode.LOGIN_EXPIRE.describe); 190 | } 191 | 192 | if (!options.members || options.members.length == 0) { 193 | return errHandle(options, YeIMUniSDKStatusCode.PARAMS_ERROR.code, 'members 不能为空'); 194 | } 195 | 196 | request(Api.User.addToBlackUserList, 'POST', { 197 | members: options.members 198 | }).then(() => { 199 | successHandle(options, YeIMUniSDKStatusCode.NORMAL_SUCCESS.describe); 200 | }).catch((fail) => { 201 | errHandle(options, fail.code, fail.message); 202 | log(1, fail); 203 | }); 204 | 205 | } 206 | 207 | /** 208 | * 209 | * 移除黑名单 210 | * 211 | * 212 | * @param {Object} options - 参数对象 213 | * 214 | * {"members": ["user1", "user2"], success: (result) => {}, fail: (error) => {} } 215 | 216 | * 217 | * @param {String} options.members - 用户ID列表 218 | * @param {(result)=>{}} [options.success] - 成功回调 219 | * @param {(error)=>{}} [options.fail] - 失败回调 220 | * 221 | * @example 222 | * removeFromBlacklist({ 223 | members: ["user1", "user2"], 224 | success: (result) => {}, 225 | fail: (error) => {} 226 | }); 227 | */ 228 | function removeFromBlacklist(options) { 229 | 230 | if (!instance.checkLogged()) { 231 | return errHandle(options, YeIMUniSDKStatusCode.LOGIN_EXPIRE.code, YeIMUniSDKStatusCode.LOGIN_EXPIRE.describe); 232 | } 233 | 234 | if (!options.members || options.members.length == 0) { 235 | return errHandle(options, YeIMUniSDKStatusCode.PARAMS_ERROR.code, 'members 不能为空'); 236 | } 237 | 238 | request(Api.User.removeFromBlacklist, 'POST', { 239 | members: options.members 240 | }).then(() => { 241 | successHandle(options, YeIMUniSDKStatusCode.NORMAL_SUCCESS.describe); 242 | }).catch((fail) => { 243 | errHandle(options, fail.code, fail.message); 244 | log(1, fail); 245 | }); 246 | 247 | } 248 | 249 | export { 250 | getUserInfo, 251 | updateUserInfo, 252 | getBlackUserList, 253 | addToBlackUserList, 254 | removeFromBlacklist 255 | } -------------------------------------------------------------------------------- /src/utils/CryptoJS.js: -------------------------------------------------------------------------------- 1 | var CryptoJS = CryptoJS || function(g, l) { 2 | var e = {}, 3 | d = e.lib = {}, 4 | m = function() {}, 5 | k = d.Base = { 6 | extend: function(a) { 7 | m.prototype = this; 8 | var c = new m; 9 | a && c.mixIn(a); 10 | c.hasOwnProperty("init") || (c.init = function() { 11 | c.$super.init.apply(this, arguments) 12 | }); 13 | c.init.prototype = c; 14 | c.$super = this; 15 | return c 16 | }, 17 | create: function() { 18 | var a = this.extend(); 19 | a.init.apply(a, arguments); 20 | return a 21 | }, 22 | init: function() {}, 23 | mixIn: function(a) { 24 | for (var c in a) a.hasOwnProperty(c) && (this[c] = a[c]); 25 | a.hasOwnProperty("toString") && (this.toString = a.toString) 26 | }, 27 | clone: function() { 28 | return this.init.prototype.extend(this) 29 | } 30 | }, 31 | p = d.WordArray = k.extend({ 32 | init: function(a, c) { 33 | a = this.words = a || []; 34 | this.sigBytes = c != l ? c : 4 * a.length 35 | }, 36 | toString: function(a) { 37 | return (a || n).stringify(this) 38 | }, 39 | concat: function(a) { 40 | var c = this.words, 41 | q = a.words, 42 | f = this.sigBytes; 43 | a = a.sigBytes; 44 | this.clamp(); 45 | if (f % 4) 46 | for (var b = 0; b < a; b++) c[f + b >>> 2] |= (q[b >>> 2] >>> 24 - 8 * (b % 4) & 255) << 47 | 24 - 8 * ((f + b) % 4); 48 | else if (65535 < q.length) 49 | for (b = 0; b < a; b += 4) c[f + b >>> 2] = q[b >>> 2]; 50 | else c.push.apply(c, q); 51 | this.sigBytes += a; 52 | return this 53 | }, 54 | clamp: function() { 55 | var a = this.words, 56 | c = this.sigBytes; 57 | a[c >>> 2] &= 4294967295 << 58 | 32 - 8 * (c % 4); 59 | a.length = g.ceil(c / 4) 60 | }, 61 | clone: function() { 62 | var a = k.clone.call(this); 63 | a.words = this.words.slice(0); 64 | return a 65 | }, 66 | random: function(a) { 67 | for (var c = [], b = 0; b < a; b += 4) c.push(4294967296 * g.random() | 0); 68 | return new p.init(c, a) 69 | } 70 | }), 71 | b = e.enc = {}, 72 | n = b.Hex = { 73 | stringify: function(a) { 74 | var c = a.words; 75 | a = a.sigBytes; 76 | for (var b = [], f = 0; f < a; f++) { 77 | var d = c[f >>> 2] >>> 24 - 8 * (f % 4) & 255; 78 | b.push((d >>> 4).toString(16)); 79 | b.push((d & 15).toString(16)) 80 | } 81 | return b.join("") 82 | }, 83 | parse: function(a) { 84 | for (var c = a.length, b = [], f = 0; f < c; f += 2) b[f >>> 3] |= parseInt(a.substr(f, 85 | 2), 16) << 24 - 4 * (f % 8); 86 | return new p.init(b, c / 2) 87 | } 88 | }, 89 | j = b.Latin1 = { 90 | stringify: function(a) { 91 | var c = a.words; 92 | a = a.sigBytes; 93 | for (var b = [], f = 0; f < a; f++) b.push(String.fromCharCode(c[f >>> 2] >>> 24 - 8 * (f % 4) & 94 | 255)); 95 | return b.join("") 96 | }, 97 | parse: function(a) { 98 | for (var c = a.length, b = [], f = 0; f < c; f++) b[f >>> 2] |= (a.charCodeAt(f) & 255) << 24 - 99 | 8 * (f % 4); 100 | return new p.init(b, c) 101 | } 102 | }, 103 | h = b.Utf8 = { 104 | stringify: function(a) { 105 | try { 106 | return decodeURIComponent(escape(j.stringify(a))) 107 | } catch (c) { 108 | throw Error("Malformed UTF-8 data"); 109 | } 110 | }, 111 | parse: function(a) { 112 | return j.parse(unescape(encodeURIComponent(a))) 113 | } 114 | }, 115 | r = d.BufferedBlockAlgorithm = k.extend({ 116 | reset: function() { 117 | this._data = new p.init; 118 | this._nDataBytes = 0 119 | }, 120 | _append: function(a) { 121 | "string" == typeof a && (a = h.parse(a)); 122 | this._data.concat(a); 123 | this._nDataBytes += a.sigBytes 124 | }, 125 | _process: function(a) { 126 | var c = this._data, 127 | b = c.words, 128 | f = c.sigBytes, 129 | d = this.blockSize, 130 | e = f / (4 * d), 131 | e = a ? g.ceil(e) : g.max((e | 0) - this._minBufferSize, 0); 132 | a = e * d; 133 | f = g.min(4 * a, f); 134 | if (a) { 135 | for (var k = 0; k < a; k += d) this._doProcessBlock(b, k); 136 | k = b.splice(0, a); 137 | c.sigBytes -= f 138 | } 139 | return new p.init(k, f) 140 | }, 141 | clone: function() { 142 | var a = k.clone.call(this); 143 | a._data = this._data.clone(); 144 | return a 145 | }, 146 | _minBufferSize: 0 147 | }); 148 | d.Hasher = r.extend({ 149 | cfg: k.extend(), 150 | init: function(a) { 151 | this.cfg = this.cfg.extend(a); 152 | this.reset() 153 | }, 154 | reset: function() { 155 | r.reset.call(this); 156 | this._doReset() 157 | }, 158 | update: function(a) { 159 | this._append(a); 160 | this._process(); 161 | return this 162 | }, 163 | finalize: function(a) { 164 | a && this._append(a); 165 | return this._doFinalize() 166 | }, 167 | blockSize: 16, 168 | _createHelper: function(a) { 169 | return function(b, d) { 170 | return (new a.init(d)).finalize(b) 171 | } 172 | }, 173 | _createHmacHelper: function(a) { 174 | return function(b, d) { 175 | return (new s.HMAC.init(a, 176 | d)).finalize(b) 177 | } 178 | } 179 | }); 180 | var s = e.algo = {}; 181 | return e 182 | }(Math); 183 | (function() { 184 | var g = CryptoJS, 185 | l = g.lib, 186 | e = l.WordArray, 187 | d = l.Hasher, 188 | m = [], 189 | l = g.algo.SHA1 = d.extend({ 190 | _doReset: function() { 191 | this._hash = new e.init([1732584193, 4023233417, 2562383102, 271733878, 3285377520]) 192 | }, 193 | _doProcessBlock: function(d, e) { 194 | for (var b = this._hash.words, n = b[0], j = b[1], h = b[2], g = b[3], l = b[4], a = 0; 80 > 195 | a; a++) { 196 | if (16 > a) m[a] = d[e + a] | 0; 197 | else { 198 | var c = m[a - 3] ^ m[a - 8] ^ m[a - 14] ^ m[a - 16]; 199 | m[a] = c << 1 | c >>> 31 200 | } 201 | c = (n << 5 | n >>> 27) + l + m[a]; 202 | c = 20 > a ? c + ((j & h | ~j & g) + 1518500249) : 40 > a ? c + ((j ^ h ^ g) + 203 | 1859775393) : 60 > a ? c + ((j & h | j & g | h & g) - 1894007588) : c + ((j ^ 204 | h ^ 205 | g) - 899497514); 206 | l = g; 207 | g = h; 208 | h = j << 30 | j >>> 2; 209 | j = n; 210 | n = c 211 | } 212 | b[0] = b[0] + n | 0; 213 | b[1] = b[1] + j | 0; 214 | b[2] = b[2] + h | 0; 215 | b[3] = b[3] + g | 0; 216 | b[4] = b[4] + l | 0 217 | }, 218 | _doFinalize: function() { 219 | var d = this._data, 220 | e = d.words, 221 | b = 8 * this._nDataBytes, 222 | g = 8 * d.sigBytes; 223 | e[g >>> 5] |= 128 << 24 - g % 32; 224 | e[(g + 64 >>> 9 << 4) + 14] = Math.floor(b / 4294967296); 225 | e[(g + 64 >>> 9 << 4) + 15] = b; 226 | d.sigBytes = 4 * e.length; 227 | this._process(); 228 | return this._hash 229 | }, 230 | clone: function() { 231 | var e = d.clone.call(this); 232 | e._hash = this._hash.clone(); 233 | return e 234 | } 235 | }); 236 | g.SHA1 = d._createHelper(l); 237 | g.HmacSHA1 = d._createHmacHelper(l) 238 | })(); 239 | (function() { 240 | var g = CryptoJS, 241 | l = g.enc.Utf8; 242 | g.algo.HMAC = g.lib.Base.extend({ 243 | init: function(e, d) { 244 | e = this._hasher = new e.init; 245 | "string" == typeof d && (d = l.parse(d)); 246 | var g = e.blockSize, 247 | k = 4 * g; 248 | d.sigBytes > k && (d = e.finalize(d)); 249 | d.clamp(); 250 | for (var p = this._oKey = d.clone(), b = this._iKey = d.clone(), n = p.words, j = b.words, 251 | h = 0; h < g; h++) n[h] ^= 1549556828, j[h] ^= 909522486; 252 | p.sigBytes = b.sigBytes = k; 253 | this.reset() 254 | }, 255 | reset: function() { 256 | var e = this._hasher; 257 | e.reset(); 258 | e.update(this._iKey) 259 | }, 260 | update: function(e) { 261 | this._hasher.update(e); 262 | return this 263 | }, 264 | finalize: function(e) { 265 | var d = 266 | this._hasher; 267 | e = d.finalize(e); 268 | d.reset(); 269 | return d.finalize(this._oKey.clone().concat(e)) 270 | } 271 | }) 272 | })(); 273 | export default CryptoJS; 274 | -------------------------------------------------------------------------------- /src/utils/fetch.js: -------------------------------------------------------------------------------- 1 | var global = 2 | (typeof globalThis !== 'undefined' && globalThis) || 3 | (typeof self !== 'undefined' && self) || 4 | (typeof global !== 'undefined' && global) || 5 | {} 6 | 7 | var support = { 8 | searchParams: 'URLSearchParams' in global, 9 | iterable: 'Symbol' in global && 'iterator' in Symbol, 10 | blob: 11 | 'FileReader' in global && 12 | 'Blob' in global && 13 | (function () { 14 | try { 15 | new Blob() 16 | return true 17 | } catch (e) { 18 | return false 19 | } 20 | })(), 21 | formData: 'FormData' in global, 22 | arrayBuffer: 'ArrayBuffer' in global 23 | } 24 | 25 | function isDataView(obj) { 26 | return obj && DataView.prototype.isPrototypeOf(obj) 27 | } 28 | 29 | if (support.arrayBuffer) { 30 | var viewClasses = [ 31 | '[object Int8Array]', 32 | '[object Uint8Array]', 33 | '[object Uint8ClampedArray]', 34 | '[object Int16Array]', 35 | '[object Uint16Array]', 36 | '[object Int32Array]', 37 | '[object Uint32Array]', 38 | '[object Float32Array]', 39 | '[object Float64Array]' 40 | ] 41 | 42 | var isArrayBufferView = 43 | ArrayBuffer.isView || 44 | function (obj) { 45 | return obj && viewClasses.indexOf(Object.prototype.toString.call(obj)) > -1 46 | } 47 | } 48 | 49 | function normalizeName(name) { 50 | if (typeof name !== 'string') { 51 | name = String(name) 52 | } 53 | if (/[^a-z0-9\-#$%&'*+.^_`|~!]/i.test(name) || name === '') { 54 | throw new TypeError('Invalid character in header field name: "' + name + '"') 55 | } 56 | return name.toLowerCase() 57 | } 58 | 59 | function normalizeValue(value) { 60 | if (typeof value !== 'string') { 61 | value = String(value) 62 | } 63 | return value 64 | } 65 | 66 | // Build a destructive iterator for the value list 67 | function iteratorFor(items) { 68 | var iterator = { 69 | next: function () { 70 | var value = items.shift() 71 | return { done: value === undefined, value: value } 72 | } 73 | } 74 | 75 | if (support.iterable) { 76 | iterator[Symbol.iterator] = function () { 77 | return iterator 78 | } 79 | } 80 | 81 | return iterator 82 | } 83 | 84 | export function Headers(headers) { 85 | this.map = {} 86 | 87 | if (headers instanceof Headers) { 88 | headers.forEach(function (value, name) { 89 | this.append(name, value) 90 | }, this) 91 | } else if (Array.isArray(headers)) { 92 | headers.forEach(function (header) { 93 | this.append(header[0], header[1]) 94 | }, this) 95 | } else if (headers) { 96 | Object.getOwnPropertyNames(headers).forEach(function (name) { 97 | this.append(name, headers[name]) 98 | }, this) 99 | } 100 | } 101 | 102 | Headers.prototype.append = function (name, value) { 103 | name = normalizeName(name) 104 | value = normalizeValue(value) 105 | var oldValue = this.map[name] 106 | this.map[name] = oldValue ? oldValue + ', ' + value : value 107 | } 108 | 109 | Headers.prototype['delete'] = function (name) { 110 | delete this.map[normalizeName(name)] 111 | } 112 | 113 | Headers.prototype.get = function (name) { 114 | name = normalizeName(name) 115 | return this.has(name) ? this.map[name] : null 116 | } 117 | 118 | Headers.prototype.has = function (name) { 119 | return this.map.hasOwnProperty(normalizeName(name)) 120 | } 121 | 122 | Headers.prototype.set = function (name, value) { 123 | this.map[normalizeName(name)] = normalizeValue(value) 124 | } 125 | 126 | Headers.prototype.forEach = function (callback, thisArg) { 127 | for (var name in this.map) { 128 | if (this.map.hasOwnProperty(name)) { 129 | callback.call(thisArg, this.map[name], name, this) 130 | } 131 | } 132 | } 133 | 134 | Headers.prototype.keys = function () { 135 | var items = [] 136 | this.forEach(function (value, name) { 137 | items.push(name) 138 | }) 139 | return iteratorFor(items) 140 | } 141 | 142 | Headers.prototype.values = function () { 143 | var items = [] 144 | this.forEach(function (value) { 145 | items.push(value) 146 | }) 147 | return iteratorFor(items) 148 | } 149 | 150 | Headers.prototype.entries = function () { 151 | var items = [] 152 | this.forEach(function (value, name) { 153 | items.push([name, value]) 154 | }) 155 | return iteratorFor(items) 156 | } 157 | 158 | if (support.iterable) { 159 | Headers.prototype[Symbol.iterator] = Headers.prototype.entries 160 | } 161 | 162 | function consumed(body) { 163 | if (body.bodyUsed) { 164 | return Promise.reject(new TypeError('Already read')) 165 | } 166 | body.bodyUsed = true 167 | } 168 | 169 | function fileReaderReady(reader) { 170 | return new Promise(function (resolve, reject) { 171 | reader.onload = function () { 172 | resolve(reader.result) 173 | } 174 | reader.onerror = function () { 175 | reject(reader.error) 176 | } 177 | }) 178 | } 179 | 180 | function readBlobAsArrayBuffer(blob) { 181 | var reader = new FileReader() 182 | var promise = fileReaderReady(reader) 183 | reader.readAsArrayBuffer(blob) 184 | return promise 185 | } 186 | 187 | function readBlobAsText(blob) { 188 | var reader = new FileReader() 189 | var promise = fileReaderReady(reader) 190 | reader.readAsText(blob) 191 | return promise 192 | } 193 | 194 | function readArrayBufferAsText(buf) { 195 | var view = new Uint8Array(buf) 196 | var chars = new Array(view.length) 197 | 198 | for (var i = 0; i < view.length; i++) { 199 | chars[i] = String.fromCharCode(view[i]) 200 | } 201 | return chars.join('') 202 | } 203 | 204 | function bufferClone(buf) { 205 | if (buf.slice) { 206 | return buf.slice(0) 207 | } else { 208 | var view = new Uint8Array(buf.byteLength) 209 | view.set(new Uint8Array(buf)) 210 | return view.buffer 211 | } 212 | } 213 | 214 | function Body() { 215 | this.bodyUsed = false 216 | 217 | this._initBody = function (body) { 218 | /* 219 | fetch-mock wraps the Response object in an ES6 Proxy to 220 | provide useful test harness features such as flush. However, on 221 | ES5 browsers without fetch or Proxy support pollyfills must be used; 222 | the proxy-pollyfill is unable to proxy an attribute unless it exists 223 | on the object before the Proxy is created. This change ensures 224 | Response.bodyUsed exists on the instance, while maintaining the 225 | semantic of setting Request.bodyUsed in the constructor before 226 | _initBody is called. 227 | */ 228 | this.bodyUsed = this.bodyUsed 229 | this._bodyInit = body 230 | if (!body) { 231 | this._bodyText = '' 232 | } else if (typeof body === 'string') { 233 | this._bodyText = body 234 | } else if (support.blob && Blob.prototype.isPrototypeOf(body)) { 235 | this._bodyBlob = body 236 | } else if (support.formData && FormData.prototype.isPrototypeOf(body)) { 237 | this._bodyFormData = body 238 | } else if (support.searchParams && URLSearchParams.prototype.isPrototypeOf(body)) { 239 | this._bodyText = body.toString() 240 | } else if (support.arrayBuffer && support.blob && isDataView(body)) { 241 | this._bodyArrayBuffer = bufferClone(body.buffer) 242 | // IE 10-11 can't handle a DataView body. 243 | this._bodyInit = new Blob([this._bodyArrayBuffer]) 244 | } else if (support.arrayBuffer && (ArrayBuffer.prototype.isPrototypeOf(body) || isArrayBufferView(body))) { 245 | this._bodyArrayBuffer = bufferClone(body) 246 | } else { 247 | this._bodyText = body = Object.prototype.toString.call(body) 248 | } 249 | 250 | if (!this.headers.get('content-type')) { 251 | if (typeof body === 'string') { 252 | this.headers.set('content-type', 'text/plain;charset=UTF-8') 253 | } else if (this._bodyBlob && this._bodyBlob.type) { 254 | this.headers.set('content-type', this._bodyBlob.type) 255 | } else if (support.searchParams && URLSearchParams.prototype.isPrototypeOf(body)) { 256 | this.headers.set('content-type', 'application/x-www-form-urlencoded;charset=UTF-8') 257 | } 258 | } 259 | } 260 | 261 | if (support.blob) { 262 | this.blob = function () { 263 | var rejected = consumed(this) 264 | if (rejected) { 265 | return rejected 266 | } 267 | 268 | if (this._bodyBlob) { 269 | return Promise.resolve(this._bodyBlob) 270 | } else if (this._bodyArrayBuffer) { 271 | return Promise.resolve(new Blob([this._bodyArrayBuffer])) 272 | } else if (this._bodyFormData) { 273 | throw new Error('could not read FormData body as blob') 274 | } else { 275 | return Promise.resolve(new Blob([this._bodyText])) 276 | } 277 | } 278 | 279 | this.arrayBuffer = function () { 280 | if (this._bodyArrayBuffer) { 281 | var isConsumed = consumed(this) 282 | if (isConsumed) { 283 | return isConsumed 284 | } 285 | if (ArrayBuffer.isView(this._bodyArrayBuffer)) { 286 | return Promise.resolve( 287 | this._bodyArrayBuffer.buffer.slice( 288 | this._bodyArrayBuffer.byteOffset, 289 | this._bodyArrayBuffer.byteOffset + this._bodyArrayBuffer.byteLength 290 | ) 291 | ) 292 | } else { 293 | return Promise.resolve(this._bodyArrayBuffer) 294 | } 295 | } else { 296 | return this.blob().then(readBlobAsArrayBuffer) 297 | } 298 | } 299 | } 300 | 301 | this.text = function () { 302 | var rejected = consumed(this) 303 | if (rejected) { 304 | return rejected 305 | } 306 | 307 | if (this._bodyBlob) { 308 | return readBlobAsText(this._bodyBlob) 309 | } else if (this._bodyArrayBuffer) { 310 | return Promise.resolve(readArrayBufferAsText(this._bodyArrayBuffer)) 311 | } else if (this._bodyFormData) { 312 | throw new Error('could not read FormData body as text') 313 | } else { 314 | return Promise.resolve(this._bodyText) 315 | } 316 | } 317 | 318 | if (support.formData) { 319 | this.formData = function () { 320 | return this.text().then(decode) 321 | } 322 | } 323 | 324 | this.json = function () { 325 | return this.text().then(JSON.parse) 326 | } 327 | 328 | return this 329 | } 330 | 331 | // HTTP methods whose capitalization should be normalized 332 | var methods = ['DELETE', 'GET', 'HEAD', 'OPTIONS', 'POST', 'PUT'] 333 | 334 | function normalizeMethod(method) { 335 | var upcased = method.toUpperCase() 336 | return methods.indexOf(upcased) > -1 ? upcased : method 337 | } 338 | 339 | export function Request(input, options) { 340 | if (!(this instanceof Request)) { 341 | throw new TypeError('Please use the "new" operator, this DOM object constructor cannot be called as a function.') 342 | } 343 | 344 | options = options || {} 345 | var body = options.body 346 | 347 | if (input instanceof Request) { 348 | if (input.bodyUsed) { 349 | throw new TypeError('Already read') 350 | } 351 | this.url = input.url 352 | this.credentials = input.credentials 353 | if (!options.headers) { 354 | this.headers = new Headers(input.headers) 355 | } 356 | this.method = input.method 357 | this.mode = input.mode 358 | this.signal = input.signal 359 | if (!body && input._bodyInit != null) { 360 | body = input._bodyInit 361 | input.bodyUsed = true 362 | } 363 | } else { 364 | this.url = String(input) 365 | } 366 | 367 | this.credentials = options.credentials || this.credentials || 'same-origin' 368 | if (options.headers || !this.headers) { 369 | this.headers = new Headers(options.headers) 370 | } 371 | this.method = normalizeMethod(options.method || this.method || 'GET') 372 | this.mode = options.mode || this.mode || null 373 | this.signal = options.signal || this.signal || (function () { 374 | if ('AbortController' in global) { 375 | var ctrl = new AbortController(); 376 | return ctrl.signal; 377 | } 378 | }()); 379 | this.referrer = null 380 | 381 | if ((this.method === 'GET' || this.method === 'HEAD') && body) { 382 | throw new TypeError('Body not allowed for GET or HEAD requests') 383 | } 384 | this._initBody(body) 385 | 386 | if (this.method === 'GET' || this.method === 'HEAD') { 387 | if (options.cache === 'no-store' || options.cache === 'no-cache') { 388 | // Search for a '_' parameter in the query string 389 | var reParamSearch = /([?&])_=[^&]*/ 390 | if (reParamSearch.test(this.url)) { 391 | // If it already exists then set the value with the current time 392 | this.url = this.url.replace(reParamSearch, '$1_=' + new Date().getTime()) 393 | } else { 394 | // Otherwise add a new '_' parameter to the end with the current time 395 | var reQueryString = /\?/ 396 | this.url += (reQueryString.test(this.url) ? '&' : '?') + '_=' + new Date().getTime() 397 | } 398 | } 399 | } 400 | } 401 | 402 | Request.prototype.clone = function () { 403 | return new Request(this, { body: this._bodyInit }) 404 | } 405 | 406 | function decode(body) { 407 | var form = new FormData() 408 | body 409 | .trim() 410 | .split('&') 411 | .forEach(function (bytes) { 412 | if (bytes) { 413 | var split = bytes.split('=') 414 | var name = split.shift().replace(/\+/g, ' ') 415 | var value = split.join('=').replace(/\+/g, ' ') 416 | form.append(decodeURIComponent(name), decodeURIComponent(value)) 417 | } 418 | }) 419 | return form 420 | } 421 | 422 | function parseHeaders(rawHeaders) { 423 | var headers = new Headers() 424 | // Replace instances of \r\n and \n followed by at least one space or horizontal tab with a space 425 | // https://tools.ietf.org/html/rfc7230#section-3.2 426 | var preProcessedHeaders = rawHeaders.replace(/\r?\n[\t ]+/g, ' ') 427 | // Avoiding split via regex to work around a common IE11 bug with the core-js 3.6.0 regex polyfill 428 | // https://github.com/github/fetch/issues/748 429 | // https://github.com/zloirock/core-js/issues/751 430 | preProcessedHeaders 431 | .split('\r') 432 | .map(function (header) { 433 | return header.indexOf('\n') === 0 ? header.substr(1, header.length) : header 434 | }) 435 | .forEach(function (line) { 436 | var parts = line.split(':') 437 | var key = parts.shift().trim() 438 | if (key) { 439 | var value = parts.join(':').trim() 440 | headers.append(key, value) 441 | } 442 | }) 443 | return headers 444 | } 445 | 446 | Body.call(Request.prototype) 447 | 448 | export function Response(bodyInit, options) { 449 | if (!(this instanceof Response)) { 450 | throw new TypeError('Please use the "new" operator, this DOM object constructor cannot be called as a function.') 451 | } 452 | if (!options) { 453 | options = {} 454 | } 455 | 456 | this.type = 'default' 457 | this.status = options.status === undefined ? 200 : options.status 458 | this.ok = this.status >= 200 && this.status < 300 459 | this.statusText = options.statusText === undefined ? '' : '' + options.statusText 460 | this.headers = new Headers(options.headers) 461 | this.url = options.url || '' 462 | this._initBody(bodyInit) 463 | } 464 | 465 | Body.call(Response.prototype) 466 | 467 | Response.prototype.clone = function () { 468 | return new Response(this._bodyInit, { 469 | status: this.status, 470 | statusText: this.statusText, 471 | headers: new Headers(this.headers), 472 | url: this.url 473 | }) 474 | } 475 | 476 | Response.error = function () { 477 | var response = new Response(null, { status: 0, statusText: '' }) 478 | response.type = 'error' 479 | return response 480 | } 481 | 482 | var redirectStatuses = [301, 302, 303, 307, 308] 483 | 484 | Response.redirect = function (url, status) { 485 | if (redirectStatuses.indexOf(status) === -1) { 486 | throw new RangeError('Invalid status code') 487 | } 488 | 489 | return new Response(null, { status: status, headers: { location: url } }) 490 | } 491 | 492 | export var DOMException = global.DOMException 493 | try { 494 | new DOMException() 495 | } catch (err) { 496 | DOMException = function (message, name) { 497 | this.message = message 498 | this.name = name 499 | var error = Error(message) 500 | this.stack = error.stack 501 | } 502 | DOMException.prototype = Object.create(Error.prototype) 503 | DOMException.prototype.constructor = DOMException 504 | } 505 | 506 | export function fetch(input, init) { 507 | return new Promise(function (resolve, reject) { 508 | var request = new Request(input, init) 509 | 510 | if (request.signal && request.signal.aborted) { 511 | return reject(new DOMException('Aborted', 'AbortError')) 512 | } 513 | 514 | var xhr = new XMLHttpRequest() 515 | 516 | function abortXhr() { 517 | xhr.abort() 518 | } 519 | 520 | xhr.onload = function () { 521 | var options = { 522 | status: xhr.status, 523 | statusText: xhr.statusText, 524 | headers: parseHeaders(xhr.getAllResponseHeaders() || '') 525 | } 526 | options.url = 'responseURL' in xhr ? xhr.responseURL : options.headers.get('X-Request-URL') 527 | var body = 'response' in xhr ? xhr.response : xhr.responseText 528 | setTimeout(function () { 529 | resolve(new Response(body, options)) 530 | }, 0) 531 | } 532 | 533 | xhr.onerror = function () { 534 | setTimeout(function () { 535 | reject(new TypeError('Network request failed')) 536 | }, 0) 537 | } 538 | 539 | xhr.ontimeout = function () { 540 | setTimeout(function () { 541 | reject(new TypeError('Network request failed')) 542 | }, 0) 543 | } 544 | 545 | xhr.onabort = function () { 546 | setTimeout(function () { 547 | reject(new DOMException('Aborted', 'AbortError')) 548 | }, 0) 549 | } 550 | 551 | function fixUrl(url) { 552 | try { 553 | return url === '' && global.location.href ? global.location.href : url 554 | } catch (e) { 555 | return url 556 | } 557 | } 558 | 559 | xhr.open(request.method, fixUrl(request.url), true) 560 | 561 | if (request.credentials === 'include') { 562 | xhr.withCredentials = true 563 | } else if (request.credentials === 'omit') { 564 | xhr.withCredentials = false 565 | } 566 | 567 | if ('responseType' in xhr) { 568 | if (support.blob) { 569 | xhr.responseType = 'blob' 570 | } else if ( 571 | support.arrayBuffer && 572 | request.headers.get('Content-Type') && 573 | request.headers.get('Content-Type').indexOf('application/octet-stream') !== -1 574 | ) { 575 | xhr.responseType = 'arraybuffer' 576 | } 577 | } 578 | 579 | if (init && typeof init.headers === 'object' && !(init.headers instanceof Headers)) { 580 | Object.getOwnPropertyNames(init.headers).forEach(function (name) { 581 | xhr.setRequestHeader(name, normalizeValue(init.headers[name])) 582 | }) 583 | } else { 584 | request.headers.forEach(function (value, name) { 585 | xhr.setRequestHeader(name, value) 586 | }) 587 | } 588 | 589 | if (request.signal) { 590 | request.signal.addEventListener('abort', abortXhr) 591 | 592 | xhr.onreadystatechange = function () { 593 | // DONE (success or failure) 594 | if (xhr.readyState === 4) { 595 | request.signal.removeEventListener('abort', abortXhr) 596 | } 597 | } 598 | } 599 | 600 | xhr.send(typeof request._bodyInit === 'undefined' ? null : request._bodyInit) 601 | }) 602 | } 603 | 604 | fetch.polyfill = true 605 | 606 | if (!global.fetch) { 607 | global.fetch = fetch 608 | global.Headers = Headers 609 | global.Request = Request 610 | global.Response = Response 611 | } -------------------------------------------------------------------------------- /src/utils/md5.js: -------------------------------------------------------------------------------- 1 | var rotateLeft = function(lValue, iShiftBits) { 2 | return (lValue << iShiftBits) | (lValue >>> (32 - iShiftBits)); 3 | } 4 | 5 | var addUnsigned = function(lX, lY) { 6 | var lX4, lY4, lX8, lY8, lResult; 7 | lX8 = (lX & 0x80000000); 8 | lY8 = (lY & 0x80000000); 9 | lX4 = (lX & 0x40000000); 10 | lY4 = (lY & 0x40000000); 11 | lResult = (lX & 0x3FFFFFFF) + (lY & 0x3FFFFFFF); 12 | if (lX4 & lY4) return (lResult ^ 0x80000000 ^ lX8 ^ lY8); 13 | if (lX4 | lY4) { 14 | if (lResult & 0x40000000) return (lResult ^ 0xC0000000 ^ lX8 ^ lY8); 15 | else return (lResult ^ 0x40000000 ^ lX8 ^ lY8); 16 | } else { 17 | return (lResult ^ lX8 ^ lY8); 18 | } 19 | } 20 | 21 | var F = function(x, y, z) { 22 | return (x & y) | ((~x) & z); 23 | } 24 | 25 | var G = function(x, y, z) { 26 | return (x & z) | (y & (~z)); 27 | } 28 | 29 | var H = function(x, y, z) { 30 | return (x ^ y ^ z); 31 | } 32 | 33 | var I = function(x, y, z) { 34 | return (y ^ (x | (~z))); 35 | } 36 | 37 | var FF = function(a, b, c, d, x, s, ac) { 38 | a = addUnsigned(a, addUnsigned(addUnsigned(F(b, c, d), x), ac)); 39 | return addUnsigned(rotateLeft(a, s), b); 40 | }; 41 | 42 | var GG = function(a, b, c, d, x, s, ac) { 43 | a = addUnsigned(a, addUnsigned(addUnsigned(G(b, c, d), x), ac)); 44 | return addUnsigned(rotateLeft(a, s), b); 45 | }; 46 | 47 | var HH = function(a, b, c, d, x, s, ac) { 48 | a = addUnsigned(a, addUnsigned(addUnsigned(H(b, c, d), x), ac)); 49 | return addUnsigned(rotateLeft(a, s), b); 50 | }; 51 | 52 | var II = function(a, b, c, d, x, s, ac) { 53 | a = addUnsigned(a, addUnsigned(addUnsigned(I(b, c, d), x), ac)); 54 | return addUnsigned(rotateLeft(a, s), b); 55 | }; 56 | 57 | var convertToWordArray = function(string) { 58 | var lWordCount; 59 | var lMessageLength = string.length; 60 | var lNumberOfWordsTempOne = lMessageLength + 8; 61 | var lNumberOfWordsTempTwo = (lNumberOfWordsTempOne - (lNumberOfWordsTempOne % 64)) / 64; 62 | var lNumberOfWords = (lNumberOfWordsTempTwo + 1) * 16; 63 | var lWordArray = Array(lNumberOfWords - 1); 64 | var lBytePosition = 0; 65 | var lByteCount = 0; 66 | while (lByteCount < lMessageLength) { 67 | lWordCount = (lByteCount - (lByteCount % 4)) / 4; 68 | lBytePosition = (lByteCount % 4) * 8; 69 | lWordArray[lWordCount] = (lWordArray[lWordCount] | (string.charCodeAt(lByteCount) << lBytePosition)); 70 | lByteCount++; 71 | } 72 | lWordCount = (lByteCount - (lByteCount % 4)) / 4; 73 | lBytePosition = (lByteCount % 4) * 8; 74 | lWordArray[lWordCount] = lWordArray[lWordCount] | (0x80 << lBytePosition); 75 | lWordArray[lNumberOfWords - 2] = lMessageLength << 3; 76 | lWordArray[lNumberOfWords - 1] = lMessageLength >>> 29; 77 | return lWordArray; 78 | }; 79 | 80 | var wordToHex = function(lValue) { 81 | var WordToHexValue = "", 82 | WordToHexValueTemp = "", 83 | lByte, lCount; 84 | for (lCount = 0; lCount <= 3; lCount++) { 85 | lByte = (lValue >>> (lCount * 8)) & 255; 86 | WordToHexValueTemp = "0" + lByte.toString(16); 87 | WordToHexValue = WordToHexValue + WordToHexValueTemp.substr(WordToHexValueTemp.length - 2, 2); 88 | } 89 | return WordToHexValue; 90 | }; 91 | 92 | var uTF8Encode = function(string) { 93 | string = string.replace(/\x0d\x0a/g, "\x0a"); 94 | var output = ""; 95 | for (var n = 0; n < string.length; n++) { 96 | var c = string.charCodeAt(n); 97 | if (c < 128) { 98 | output += String.fromCharCode(c); 99 | } else if ((c > 127) && (c < 2048)) { 100 | output += String.fromCharCode((c >> 6) | 192); 101 | output += String.fromCharCode((c & 63) | 128); 102 | } else { 103 | output += String.fromCharCode((c >> 12) | 224); 104 | output += String.fromCharCode(((c >> 6) & 63) | 128); 105 | output += String.fromCharCode((c & 63) | 128); 106 | } 107 | } 108 | return output; 109 | }; 110 | 111 | function md5(string) { 112 | string = string + ""; 113 | var x = Array(); 114 | var k, AA, BB, CC, DD, a, b, c, d; 115 | var S11 = 7, 116 | S12 = 12, 117 | S13 = 17, 118 | S14 = 22; 119 | var S21 = 5, 120 | S22 = 9, 121 | S23 = 14, 122 | S24 = 20; 123 | var S31 = 4, 124 | S32 = 11, 125 | S33 = 16, 126 | S34 = 23; 127 | var S41 = 6, 128 | S42 = 10, 129 | S43 = 15, 130 | S44 = 21; 131 | string = uTF8Encode(string); 132 | x = convertToWordArray(string); 133 | a = 0x67452301; 134 | b = 0xEFCDAB89; 135 | c = 0x98BADCFE; 136 | d = 0x10325476; 137 | for (k = 0; k < x.length; k += 16) { 138 | AA = a; 139 | BB = b; 140 | CC = c; 141 | DD = d; 142 | a = FF(a, b, c, d, x[k + 0], S11, 0xD76AA478); 143 | d = FF(d, a, b, c, x[k + 1], S12, 0xE8C7B756); 144 | c = FF(c, d, a, b, x[k + 2], S13, 0x242070DB); 145 | b = FF(b, c, d, a, x[k + 3], S14, 0xC1BDCEEE); 146 | a = FF(a, b, c, d, x[k + 4], S11, 0xF57C0FAF); 147 | d = FF(d, a, b, c, x[k + 5], S12, 0x4787C62A); 148 | c = FF(c, d, a, b, x[k + 6], S13, 0xA8304613); 149 | b = FF(b, c, d, a, x[k + 7], S14, 0xFD469501); 150 | a = FF(a, b, c, d, x[k + 8], S11, 0x698098D8); 151 | d = FF(d, a, b, c, x[k + 9], S12, 0x8B44F7AF); 152 | c = FF(c, d, a, b, x[k + 10], S13, 0xFFFF5BB1); 153 | b = FF(b, c, d, a, x[k + 11], S14, 0x895CD7BE); 154 | a = FF(a, b, c, d, x[k + 12], S11, 0x6B901122); 155 | d = FF(d, a, b, c, x[k + 13], S12, 0xFD987193); 156 | c = FF(c, d, a, b, x[k + 14], S13, 0xA679438E); 157 | b = FF(b, c, d, a, x[k + 15], S14, 0x49B40821); 158 | a = GG(a, b, c, d, x[k + 1], S21, 0xF61E2562); 159 | d = GG(d, a, b, c, x[k + 6], S22, 0xC040B340); 160 | c = GG(c, d, a, b, x[k + 11], S23, 0x265E5A51); 161 | b = GG(b, c, d, a, x[k + 0], S24, 0xE9B6C7AA); 162 | a = GG(a, b, c, d, x[k + 5], S21, 0xD62F105D); 163 | d = GG(d, a, b, c, x[k + 10], S22, 0x2441453); 164 | c = GG(c, d, a, b, x[k + 15], S23, 0xD8A1E681); 165 | b = GG(b, c, d, a, x[k + 4], S24, 0xE7D3FBC8); 166 | a = GG(a, b, c, d, x[k + 9], S21, 0x21E1CDE6); 167 | d = GG(d, a, b, c, x[k + 14], S22, 0xC33707D6); 168 | c = GG(c, d, a, b, x[k + 3], S23, 0xF4D50D87); 169 | b = GG(b, c, d, a, x[k + 8], S24, 0x455A14ED); 170 | a = GG(a, b, c, d, x[k + 13], S21, 0xA9E3E905); 171 | d = GG(d, a, b, c, x[k + 2], S22, 0xFCEFA3F8); 172 | c = GG(c, d, a, b, x[k + 7], S23, 0x676F02D9); 173 | b = GG(b, c, d, a, x[k + 12], S24, 0x8D2A4C8A); 174 | a = HH(a, b, c, d, x[k + 5], S31, 0xFFFA3942); 175 | d = HH(d, a, b, c, x[k + 8], S32, 0x8771F681); 176 | c = HH(c, d, a, b, x[k + 11], S33, 0x6D9D6122); 177 | b = HH(b, c, d, a, x[k + 14], S34, 0xFDE5380C); 178 | a = HH(a, b, c, d, x[k + 1], S31, 0xA4BEEA44); 179 | d = HH(d, a, b, c, x[k + 4], S32, 0x4BDECFA9); 180 | c = HH(c, d, a, b, x[k + 7], S33, 0xF6BB4B60); 181 | b = HH(b, c, d, a, x[k + 10], S34, 0xBEBFBC70); 182 | a = HH(a, b, c, d, x[k + 13], S31, 0x289B7EC6); 183 | d = HH(d, a, b, c, x[k + 0], S32, 0xEAA127FA); 184 | c = HH(c, d, a, b, x[k + 3], S33, 0xD4EF3085); 185 | b = HH(b, c, d, a, x[k + 6], S34, 0x4881D05); 186 | a = HH(a, b, c, d, x[k + 9], S31, 0xD9D4D039); 187 | d = HH(d, a, b, c, x[k + 12], S32, 0xE6DB99E5); 188 | c = HH(c, d, a, b, x[k + 15], S33, 0x1FA27CF8); 189 | b = HH(b, c, d, a, x[k + 2], S34, 0xC4AC5665); 190 | a = II(a, b, c, d, x[k + 0], S41, 0xF4292244); 191 | d = II(d, a, b, c, x[k + 7], S42, 0x432AFF97); 192 | c = II(c, d, a, b, x[k + 14], S43, 0xAB9423A7); 193 | b = II(b, c, d, a, x[k + 5], S44, 0xFC93A039); 194 | a = II(a, b, c, d, x[k + 12], S41, 0x655B59C3); 195 | d = II(d, a, b, c, x[k + 3], S42, 0x8F0CCC92); 196 | c = II(c, d, a, b, x[k + 10], S43, 0xFFEFF47D); 197 | b = II(b, c, d, a, x[k + 1], S44, 0x85845DD1); 198 | a = II(a, b, c, d, x[k + 8], S41, 0x6FA87E4F); 199 | d = II(d, a, b, c, x[k + 15], S42, 0xFE2CE6E0); 200 | c = II(c, d, a, b, x[k + 6], S43, 0xA3014314); 201 | b = II(b, c, d, a, x[k + 13], S44, 0x4E0811A1); 202 | a = II(a, b, c, d, x[k + 4], S41, 0xF7537E82); 203 | d = II(d, a, b, c, x[k + 11], S42, 0xBD3AF235); 204 | c = II(c, d, a, b, x[k + 2], S43, 0x2AD7D2BB); 205 | b = II(b, c, d, a, x[k + 9], S44, 0xEB86D391); 206 | a = addUnsigned(a, AA); 207 | b = addUnsigned(b, BB); 208 | c = addUnsigned(c, CC); 209 | d = addUnsigned(d, DD); 210 | } 211 | var tempValue = wordToHex(a) + wordToHex(b) + wordToHex(c) + wordToHex(d); 212 | return tempValue.toLowerCase(); 213 | } 214 | 215 | export default md5 216 | -------------------------------------------------------------------------------- /src/utils/mitt.umd.js: -------------------------------------------------------------------------------- 1 | !function (e, n) { "object" == typeof exports && "undefined" != typeof module ? module.exports = n() : "function" == typeof define && define.amd ? define(n) : (e = e || self).mitt = n() }(this, function () { return function (e) { return { all: e = e || new Map, on: function (n, t) { var f = e.get(n); f ? f.push(t) : e.set(n, [t]) }, off: function (n, t) { var f = e.get(n); f && (t ? f.splice(f.indexOf(t) >>> 0, 1) : e.set(n, [])) }, emit: function (n, t) { var f = e.get(n); f && f.slice().map(function (e) { e(t) }), (f = e.get("*")) && f.slice().map(function (e) { e(n, t) }) } } } }); -------------------------------------------------------------------------------- /src/utils/queryParams.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 对象转url参数 3 | * @param {*} data,对象 4 | * @param {*} isPrefix,是否自动加上"?" 5 | */ 6 | function queryParams(data = {}, isPrefix = true, arrayFormat = 'brackets') { 7 | let prefix = isPrefix ? '?' : '' 8 | let _result = [] 9 | if (['indices', 'brackets', 'repeat', 'comma'].indexOf(arrayFormat) == -1) arrayFormat = 'brackets'; 10 | for (let key in data) { 11 | let value = data[key] 12 | // 去掉为空的参数 13 | if (['', undefined, null].indexOf(value) >= 0) { 14 | continue; 15 | } 16 | // 如果值为数组,另行处理 17 | if (value.constructor === Array) { 18 | // e.g. {ids: [1, 2, 3]} 19 | switch (arrayFormat) { 20 | case 'indices': 21 | // 结果: ids[0]=1&ids[1]=2&ids[2]=3 22 | for (let i = 0; i < value.length; i++) { 23 | _result.push(key + '[' + i + ']=' + value[i]) 24 | } 25 | break; 26 | case 'brackets': 27 | // 结果: ids[]=1&ids[]=2&ids[]=3 28 | value.forEach(_value => { 29 | _result.push(key + '[]=' + _value) 30 | }) 31 | break; 32 | case 'repeat': 33 | // 结果: ids=1&ids=2&ids=3 34 | value.forEach(_value => { 35 | _result.push(key + '=' + _value) 36 | }) 37 | break; 38 | case 'comma': 39 | // 结果: ids=1,2,3 40 | let commaStr = ""; 41 | value.forEach(_value => { 42 | commaStr += (commaStr ? "," : "") + _value; 43 | }) 44 | _result.push(key + '=' + commaStr) 45 | break; 46 | default: 47 | value.forEach(_value => { 48 | _result.push(key + '[]=' + _value) 49 | }) 50 | } 51 | } else { 52 | _result.push(key + '=' + value) 53 | } 54 | } 55 | return _result.length ? prefix + _result.join('&') : '' 56 | } 57 | 58 | export default queryParams; -------------------------------------------------------------------------------- /src/yeim-uni-sdk.js: -------------------------------------------------------------------------------- 1 | /*eslint-disable*/ 2 | 3 | /** 4 | * 5 | * YeIM-Uni-SDK 6 | * 可以私有化部署的全开源即时通讯js-sdk,仅需集成 SDK 即可轻松实现聊天能力,支持Web或uni-app项目接入使用,满足通信需要。 7 | * 8 | * @author wzJun1 9 | * @link https://github.com/wzJun1/YeIM-Uni-SDK 10 | * @doc https://wzjun1.netlify.app/ye_plugins/sdk/yeimunisdk 11 | * @copyright wzJun1 2022, 2024. All rights reserved. 12 | * @license Apache-2.0 13 | * 14 | */ 15 | 16 | import { 17 | version 18 | } from '../package.json'; 19 | 20 | import { 21 | YeIMUniSDKDefines 22 | } from './const/yeim-defines'; 23 | 24 | import { 25 | YeIMUniSDKStatusCode 26 | } from './const/yeim-status-code'; 27 | 28 | import WebSocketUtil from './func/websocket'; 29 | 30 | import { 31 | successHandle, 32 | errHandle 33 | } from './func/callback'; 34 | 35 | import mitt from './utils/mitt.umd' 36 | 37 | import { 38 | emit, 39 | addEventListener, 40 | removeEventListener 41 | } from './func/event'; 42 | 43 | import log from './func/log'; 44 | 45 | import { 46 | createTextMessage, 47 | createTextAtMessage, 48 | createImageMessage, 49 | createImageMessageFromUrl, 50 | createAudioMessage, 51 | createAudioMessageFromUrl, 52 | createLocationMessage, 53 | createVideoMessage, 54 | createVideoMessageFromUrl, 55 | createCustomMessage, 56 | createMergerMessage, 57 | createForwardMessage, 58 | sendMessage, 59 | saveMessage, 60 | getHistoryMessageList, 61 | revokeMessage, 62 | deleteMessage, 63 | handleMessageRevoked 64 | } from './service/messageService' 65 | 66 | import { 67 | deleteConversation, 68 | saveAndUpdateConversation, 69 | clearConversationUnread, 70 | getConversation, 71 | getConversationList, 72 | saveCloudConversationListToLocal, 73 | handlePrivateConversationReadReceipt 74 | } from './service/conversationService' 75 | 76 | import { 77 | createGroup, 78 | dissolveGroup, 79 | getGroup, 80 | getGroupList, 81 | transferLeader, 82 | updateGroup, 83 | joinGroup, 84 | leaveGroup, 85 | addGroupUsers, 86 | removeGroupUsers, 87 | setAdminstrator, 88 | getGroupApplyList, 89 | getGroupUserList, 90 | handleApply, 91 | setMute 92 | } from './service/groupService' 93 | 94 | import { 95 | getMediaUploadParams, 96 | upload, 97 | } from './service/uploadService' 98 | 99 | import { 100 | getUserInfo, 101 | updateUserInfo, 102 | getBlackUserList, 103 | addToBlackUserList, 104 | removeFromBlacklist 105 | } from './service/userService' 106 | 107 | import { 108 | setPushPermissions, 109 | createNotificationChannel, 110 | bindAppUserPushCID 111 | } from './service/pushService' 112 | 113 | import { 114 | acceptApply, 115 | addFriend, 116 | deleteFriend, 117 | getFriendApplyList, 118 | getFriendList, 119 | handleFriendApplyListChanged, 120 | handleFriendListChanged, 121 | refuseApply, 122 | setApplyListRead, 123 | updateFriend 124 | } from './service/friendService'; 125 | 126 | /** 127 | * YeIMUniSDK实例化对象 128 | */ 129 | let instance; 130 | 131 | /** 132 | * YeIMUniSDK 133 | * @class 134 | */ 135 | class YeIMUniSDK { 136 | 137 | /** 138 | * @constructs 139 | */ 140 | constructor(options = {}) { 141 | 142 | /** 143 | * 全局配置参数 144 | */ 145 | this.defaults = { 146 | 147 | /** 148 | * http url 149 | */ 150 | baseURL: undefined, 151 | 152 | /** 153 | * socket url 154 | */ 155 | socketURL: undefined, 156 | 157 | /** 158 | * 事件前缀 159 | */ 160 | eventPrefix: 'YeIM_', 161 | 162 | /** 163 | * 最大重连次数,0不限制一直重连 164 | */ 165 | reConnectTotal: 15, 166 | 167 | /** 168 | * 重连时间间隔 169 | */ 170 | reConnectInterval: 3000, 171 | 172 | /** 173 | * 心跳时间间隔(默认30s) 174 | */ 175 | heartInterval: 30000, 176 | 177 | /** 178 | * 日志等级 179 | * 0 普通日志,日志量较多,接入时建议使用 180 | * 1 关键性日志,日志量较少,生产环境时建议使用 181 | * 2 无日志级别,SDK 将不打印任何日志 182 | */ 183 | logLevel: 0, 184 | 185 | /** 186 | * SDK自定义初始化参数 187 | */ 188 | ...options, 189 | 190 | /** 191 | * APP离线通知配置 192 | */ 193 | notification: { 194 | 195 | /** 196 | * 登录后是否自动检测通知权限 197 | */ 198 | autoPermission: false, 199 | 200 | /** 201 | * 手机安卓8.0以上的通知渠道ID(需要申请) 202 | */ 203 | oppoChannelId: 'Default', //OPPO手机安卓8.0以上的通知渠道ID 204 | 205 | /** 206 | * 小米手机重要级别消息通知渠道ID(需要申请,一般默认为high_system) 207 | */ 208 | xiaomiChannelId: 'high_system', 209 | 210 | ...options.notification 211 | }, 212 | 213 | }; 214 | 215 | /** 216 | * uni环境 217 | */ 218 | if (typeof uni !== 'undefined') { 219 | this.uni = true; 220 | } else { 221 | this.uni = false; 222 | } 223 | /** 224 | * 版本号 225 | */ 226 | this.version = version; 227 | /** 228 | * 事件总线 229 | */ 230 | this.emitter = mitt(); 231 | /** 232 | * 是否首次连接 233 | */ 234 | this.firstConnect = true; 235 | /** 236 | * webSocket 237 | */ 238 | this.webSocket = undefined; 239 | /** 240 | * 是否已登陆YeIMServer 241 | */ 242 | this.socketLogged = false; 243 | /** 244 | * 是否允许重连 245 | */ 246 | this.allowReconnect = true; 247 | /** 248 | * 重连锁,防止多重调用 249 | */ 250 | this.lockReconnect = false; 251 | /** 252 | * 重连次数 253 | */ 254 | this.reConnectNum = 0; 255 | /** 256 | * 登录连接超时定时器 257 | */ 258 | this.connectTimer = undefined; 259 | /** 260 | * 心跳定时器 261 | */ 262 | this.heartTimer = undefined; 263 | /** 264 | * 检测定时器 265 | */ 266 | this.checkTimer = undefined; 267 | /** 268 | * 用户信息 269 | */ 270 | this.user = {}; 271 | /** 272 | * 用户ID 273 | */ 274 | this.userId = undefined; 275 | /** 276 | * 登陆token 277 | */ 278 | this.token = undefined; 279 | /** 280 | * 媒体上传参数 281 | */ 282 | this.mediaUploadParams = {}; 283 | /** 284 | * 移动端推送标识符 285 | */ 286 | this.mobileDeviceId = undefined; 287 | /** 288 | * APP是否在前台 289 | */ 290 | this.inApp = true; 291 | } 292 | 293 | /** 294 | * 初始化SDK 295 | * @param options 配置项 296 | * @returns YeIMUniSDK实例化对象 297 | */ 298 | static init(options = {}) { 299 | if (typeof uni !== 'undefined' && uni.$YeIMInstance) { 300 | instance = uni.$YeIMInstance; 301 | } 302 | if (instance) { 303 | return instance; 304 | } 305 | if (!options.baseURL || !options.socketURL) { 306 | return console.error(YeIMUniSDKStatusCode.SDK_PARAMS_ERROR.describe); 307 | } 308 | instance = new YeIMUniSDK(options); 309 | if (typeof uni !== 'undefined') { 310 | uni.$YeIMInstance = instance; 311 | } 312 | YeIMUniSDK.prototype.createTextMessage = createTextMessage; //创建文字消息 313 | YeIMUniSDK.prototype.createTextAtMessage = createTextAtMessage; //创建群聊文字@消息 314 | YeIMUniSDK.prototype.createImageMessage = createImageMessage; //创建图片消息 315 | YeIMUniSDK.prototype.createImageMessageFromUrl = createImageMessageFromUrl; //创建图片Url直发消息 316 | YeIMUniSDK.prototype.createAudioMessage = createAudioMessage; //创建语音消息 317 | YeIMUniSDK.prototype.createAudioMessageFromUrl = createAudioMessageFromUrl; //创建语音Url直发消息 318 | YeIMUniSDK.prototype.createVideoMessage = createVideoMessage; //创建小视频消息 319 | YeIMUniSDK.prototype.createVideoMessageFromUrl = createVideoMessageFromUrl; //创建小视频Url直发消息 320 | YeIMUniSDK.prototype.createLocationMessage = createLocationMessage; //创建位置消息 321 | YeIMUniSDK.prototype.createCustomMessage = createCustomMessage; //创建自定义消息 322 | YeIMUniSDK.prototype.createMergerMessage = createMergerMessage; //创建合并消息 323 | YeIMUniSDK.prototype.createForwardMessage = createForwardMessage; //创建转发消息 324 | YeIMUniSDK.prototype.upload = upload; //通用上传接口 325 | YeIMUniSDK.prototype.sendMessage = sendMessage; //发送消息统一接口 326 | YeIMUniSDK.prototype.getHistoryMessageList = 327 | getHistoryMessageList; //v1.1.7开始使用!获取历史消息记录(本地默认只存取每个会话的最新20条记录,剩余记录均从云端拉取) 328 | YeIMUniSDK.prototype.revokeMessage = revokeMessage; //撤回消息 329 | YeIMUniSDK.prototype.deleteMessage = deleteMessage; //删除消息 330 | YeIMUniSDK.prototype.getConversation = getConversation; //获取会话详情 331 | YeIMUniSDK.prototype.getConversationList = getConversationList; //获取会话列表 332 | YeIMUniSDK.prototype.clearConversationUnread = clearConversationUnread; //清除指定会话未读数 333 | YeIMUniSDK.prototype.deleteConversation = deleteConversation; //删除指定会话和聊天记录 334 | YeIMUniSDK.prototype.getUserInfo = getUserInfo; //获取用户资料 335 | YeIMUniSDK.prototype.updateUserInfo = updateUserInfo; //更新用户昵称和头像 336 | YeIMUniSDK.prototype.getBlackUserList = getBlackUserList; //获取黑名单列表 337 | YeIMUniSDK.prototype.addToBlackUserList = addToBlackUserList; //添加用户到黑名单 338 | YeIMUniSDK.prototype.removeFromBlacklist = removeFromBlacklist; //把用户从黑名单移除 339 | YeIMUniSDK.prototype.addEventListener = addEventListener; //设置监听器 340 | YeIMUniSDK.prototype.removeEventListener = removeEventListener; //移除监听器 341 | YeIMUniSDK.prototype.createGroup = createGroup; //创建群组 342 | YeIMUniSDK.prototype.dissolveGroup = dissolveGroup; //解散群组 343 | YeIMUniSDK.prototype.updateGroup = updateGroup; //更新群组资料 344 | YeIMUniSDK.prototype.getGroup = getGroup; //获取群组资料 345 | YeIMUniSDK.prototype.transferLeader = transferLeader; //转让群主 346 | YeIMUniSDK.prototype.getGroupList = getGroupList; //获取我的群组列表 347 | YeIMUniSDK.prototype.joinGroup = joinGroup; //申请入群 348 | YeIMUniSDK.prototype.leaveGroup = leaveGroup; //退出群组 349 | YeIMUniSDK.prototype.addGroupUsers = addGroupUsers; //添加群成员 350 | YeIMUniSDK.prototype.getGroupUserList = getGroupUserList; //获取群成员列表 351 | YeIMUniSDK.prototype.removeGroupUsers = removeGroupUsers; //移除群成员 352 | YeIMUniSDK.prototype.setAdminstrator = setAdminstrator; //设置群管理员 353 | YeIMUniSDK.prototype.setMute = setMute; //禁言群成员 354 | YeIMUniSDK.prototype.getGroupApplyList = getGroupApplyList; //获取名下群组用户入群申请列表 355 | YeIMUniSDK.prototype.handleApply = handleApply; //处理入群申请 356 | YeIMUniSDK.prototype.getFriendList = getFriendList; //获取好友列表 357 | YeIMUniSDK.prototype.getFriendApplyList = getFriendApplyList; //获取好友申请列表 358 | YeIMUniSDK.prototype.setApplyListRead = setApplyListRead; //将全部好友申请设置为已读状态 359 | YeIMUniSDK.prototype.acceptApply = acceptApply; //同意好友申请 360 | YeIMUniSDK.prototype.refuseApply = refuseApply; //拒绝好友申请 361 | YeIMUniSDK.prototype.addFriend = addFriend; //添加好友 362 | YeIMUniSDK.prototype.deleteFriend = deleteFriend; //添加好友 363 | YeIMUniSDK.prototype.updateFriend = updateFriend; //更新好友资料 364 | log(1, "============= YeIMUniSDK 初始化成功!版本号:" + instance.version + " ============="); 365 | return instance; 366 | } 367 | 368 | /** 369 | * 获取SDK实例化对象 370 | */ 371 | static getInstance() { 372 | if (typeof uni !== 'undefined' && uni.$YeIMInstance) { 373 | instance = uni.$YeIMInstance; 374 | } 375 | if (instance) { 376 | return instance; 377 | } 378 | console.error("SDK未初始化,无法获取实例化对象") 379 | return undefined; 380 | } 381 | 382 | /** 383 | * 连接YeIMServer 384 | * @param options 配置项 385 | * @returns YeIMUniSDK实例化对象 386 | */ 387 | connect(options = {}) { 388 | 389 | if (!options.userId || !options.token) { 390 | return errHandle(options, YeIMUniSDKStatusCode.NO_USERID.code, YeIMUniSDKStatusCode.NO_USERID 391 | .describe); 392 | } 393 | 394 | //拼接websocket连接url 395 | let url = this.defaults.socketURL + "/" + options.userId + "/" + options.token; 396 | //创建websocket连接 397 | this.webSocket = new WebSocketUtil(url); 398 | 399 | //设置websocket相关监听 400 | this.webSocket.onOpen(this.socketOpen.bind(this)); 401 | this.webSocket.onClose(this.socketClose.bind(this)); 402 | this.webSocket.onError(this.socketError.bind(this)); 403 | this.webSocket.onMessage(this.socketMessage.bind(this)); 404 | 405 | //连接异常 406 | if (this.connectTimer) { 407 | clearTimeout(this.connectTimer); 408 | this.connectTimer = undefined; 409 | } 410 | this.connectTimer = setTimeout(() => { 411 | this.connectTimer = undefined; 412 | log(1, '连接服务端失败,请检查配置'); 413 | return errHandle(options, YeIMUniSDKStatusCode.CONNECT_ERROR.code, YeIMUniSDKStatusCode 414 | .CONNECT_ERROR 415 | .describe); 416 | }, this.defaults.reConnectInterval + 1000); 417 | 418 | //临时设置监听onMessage 419 | this.webSocket.onMessage((res) => { 420 | if (this.socketLogged) { 421 | clearTimeout(this.connectTimer); 422 | this.connectTimer = undefined; 423 | return; 424 | z 425 | } 426 | clearTimeout(this.connectTimer); 427 | this.connectTimer = undefined; 428 | let data = JSON.parse(res.data); 429 | //监听到登陆成功 430 | if (data.code == 201) { 431 | //设置用户相关参数 432 | let user = data.data.user; 433 | this.user = user; 434 | this.userId = options.userId; 435 | this.token = options.token; 436 | //设置推送参数 437 | let pushConfig = data.data.pushConfig; 438 | if (pushConfig.oppoChannelId) { 439 | this.defaults.notification.oppoChannelId = pushConfig.oppoChannelId; 440 | } 441 | if (pushConfig.xiaomiChannelId) { 442 | this.defaults.notification.xiaomiChannelId = pushConfig.xiaomiChannelId; 443 | } 444 | //设置socket state(socketLogged)登陆成功 445 | this.socketLogged = true; 446 | this.reConnectNum = 0; 447 | log(1, `YeIMServer登陆成功,登陆用户:${options.userId}`) 448 | //登陆成功后从服务端获取一下全部会话,并发送事件CONVERSATION_LIST_CHANGED 449 | saveCloudConversationListToLocal(1, 100000); 450 | //登陆成功触发一次好友列表更新事件 451 | handleFriendListChanged(); 452 | //登陆成功触发一次好友申请列表更新事件 453 | handleFriendApplyListChanged(); 454 | //获取媒体上传参数 455 | getMediaUploadParams(); 456 | //如果在uni环境下,执行以下操作 457 | if (this.uni) { 458 | //3.1 申请权限 459 | if (this.defaults.notification.autoPermission) { 460 | setPushPermissions(); 461 | } 462 | //创建推送渠道 463 | createNotificationChannel(); 464 | //APP用户绑定个推CID到后端,用于离线推送 465 | bindAppUserPushCID(); 466 | //监听在线透传消息 467 | let uuidList = []; 468 | uni.onPushMessage((res) => { 469 | //APP在后台,并且没被杀掉的时候,收到透传消息在客户端创建通知提示 470 | if (!this.inApp && res.type == 'receive') { 471 | let uuid = res.data.__UUID__; 472 | if (uuidList.indexOf(uuid) <= 0) { 473 | uuidList.push(uuid); 474 | uni.createPushMessage({ 475 | title: res.data.title, 476 | content: res.data.content, 477 | sound: 'system', 478 | success: () => {}, 479 | fail: () => {} 480 | }) 481 | } 482 | } 483 | }) 484 | } 485 | return successHandle(options, YeIMUniSDKStatusCode.NORMAL_SUCCESS.describe, user); 486 | } else { 487 | return errHandle(options, YeIMUniSDKStatusCode.LOGIN_ERROR.code, YeIMUniSDKStatusCode 488 | .LOGIN_ERROR 489 | .describe); 490 | } 491 | }) 492 | } 493 | 494 | /** 495 | * 断开连接 496 | * 手动操作断开连接后,不会重连 497 | */ 498 | disConnect() { 499 | this.allowReconnect = false; 500 | this.webSocket.close(); 501 | this.webSocket.destroy(); 502 | } 503 | 504 | /** 505 | * 重连 506 | */ 507 | reConnect() { 508 | 509 | if (this.allowReconnect && !this.lockReconnect) { 510 | //没有用户信息,取消重连 511 | if (!this.userId || !this.token) { 512 | this.allowReconnect = false; 513 | return; 514 | } 515 | //设置了最大重连次数,并且已经到了限制,不再连。 516 | if (this.defaults.reConnectTotal > 0 && this.defaults.reConnectTotal <= this.reConnectNum) { 517 | this.allowReconnect = false; 518 | return; 519 | } 520 | this.reConnectNum++; 521 | this.lockReconnect = true; 522 | //没连接上会一直重连,设置延迟避免请求过多 523 | //通知网络状态变化 524 | emit(YeIMUniSDKDefines.EVENT.NET_CHANGED, 'connecting'); 525 | setTimeout(() => { 526 | log(1, `WebSocket 正在进行第${this.reConnectNum}次重连`) 527 | try { 528 | this.connect({ 529 | userId: this.userId, 530 | token: this.token 531 | }); 532 | } catch (e) { 533 | console.log(e) 534 | } 535 | this.lockReconnect = false; 536 | }, this.defaults.reConnectInterval); //这里设置重连间隔(ms) 537 | 538 | } 539 | } 540 | 541 | /** 542 | * 获取当前连接状态 543 | * "CLOSED":3,"CLOSING":2,"CONNECTING":0,"OPEN":1 544 | */ 545 | readyState() { 546 | if (this.webSocket && this.webSocket.socketTask) { 547 | return this.webSocket.socketTask.readyState; 548 | } else { 549 | return 3; 550 | } 551 | } 552 | 553 | /** 554 | * 开始心跳 555 | */ 556 | startHeartTimer() { 557 | this.clearHeartTimer(); 558 | if (this.webSocket && this.webSocket.socketTask) { 559 | this.heartTimer = setTimeout(() => { 560 | if (this.readyState() == 1) { 561 | let heartJson = { 562 | type: 'heart', 563 | data: 'ping' 564 | }; 565 | this.webSocket.send(heartJson); 566 | this.checkTimer = setTimeout(() => { 567 | this.webSocket.close(); 568 | }, this.defaults.heartInterval) 569 | } 570 | }, this.defaults.heartInterval) 571 | } 572 | } 573 | 574 | /** 575 | * 清除心跳定时器,取消心跳 576 | */ 577 | clearHeartTimer() { 578 | clearTimeout(this.heartTimer); 579 | clearTimeout(this.checkTimer); 580 | return this; 581 | } 582 | 583 | 584 | /** 585 | * socket 打开回调 586 | * @param {Object} info 587 | */ 588 | socketOpen(info) { 589 | log(1, 'WebSocket成功连接'); 590 | emit(YeIMUniSDKDefines.EVENT.NET_CHANGED, 'connected'); 591 | } 592 | 593 | /** 594 | * socket 关闭回调 595 | * @param {Object} err 596 | */ 597 | socketClose(err) { 598 | log(1, 'WebSocket断开连接'); 599 | this.socketLogged = false; 600 | this.webSocket.close(); 601 | this.webSocket.destroy(); 602 | emit(YeIMUniSDKDefines.EVENT.NET_CHANGED, 'closed'); 603 | this.reConnect(); 604 | } 605 | 606 | /** 607 | * socket 错误回调 608 | * @param {Object} err 609 | */ 610 | socketError(err) { 611 | log(1, err); 612 | this.reConnect(); 613 | } 614 | 615 | /** 616 | * socket 消息回调 617 | * @param {Object} res 618 | */ 619 | socketMessage(res) { 620 | log(0, res); 621 | //收到任意消息代表心跳成功 622 | this.startHeartTimer(); 623 | if (!this.socketLogged) return; 624 | let data = JSON.parse(res.data); 625 | if (data) { 626 | this.socketMessageHandle(data); 627 | } else { 628 | log(1, res); 629 | } 630 | } 631 | 632 | /** 633 | * WebSocket消息统一处理 634 | * @param {Object} data 635 | */ 636 | socketMessageHandle(data) { 637 | if (data.code == 200) { 638 | //收到消息 639 | let message = data.data; 640 | 641 | //1. 保存消息,会话数据由在线推送 642 | saveMessage(message); 643 | 644 | //2. 发送消息接收事件 645 | emit(YeIMUniSDKDefines.EVENT.MESSAGE_RECEIVED, message); 646 | 647 | } else if (data.code == 203) { 648 | //收到会话更新 649 | let conversation = data.data; 650 | saveAndUpdateConversation(conversation); 651 | } else if (data.code == 205) { 652 | //收到私聊会话已读回执 653 | let conversation = data.data; 654 | handlePrivateConversationReadReceipt(conversation.conversationId); 655 | } else if (data.code == 207) { 656 | //收到消息撤回事件 657 | handleMessageRevoked(data.data); 658 | } else if (data.code == 208) { 659 | //好友列表更新事件 660 | setTimeout(() => { 661 | handleFriendListChanged(); 662 | }, 500); 663 | } else if (data.code == 209) { 664 | //好友申请列表更新 665 | setTimeout(() => { 666 | handleFriendApplyListChanged(); 667 | }, 500); 668 | } else if (data.code == 210) { 669 | //好友申请被拒绝 670 | emit(YeIMUniSDKDefines.EVENT.FRIEND_APPLY_REFUSE, data.data); 671 | //更新好友申请列表 672 | setTimeout(() => { 673 | handleFriendApplyListChanged(1); 674 | }, 500); 675 | } else if (data.code == 10009) { 676 | //被踢下线不允许重连 677 | this.allowReconnect = false; 678 | this.clearHeartTimer(); 679 | this.webSocket.close(); 680 | emit(YeIMUniSDKDefines.EVENT.KICKED_OUT, true); 681 | log(1, `用户:${this.userId} 由于多个实例同时登录被踢出`); 682 | } 683 | } 684 | 685 | 686 | /** 687 | * 拦截登陆状态 688 | */ 689 | checkLogged() { 690 | if (!this.socketLogged || !this.userId || !this.token) { 691 | return false; 692 | } else { 693 | return true; 694 | } 695 | } 696 | 697 | /** 698 | * 设置APP在前台 699 | */ 700 | intoApp() { 701 | this.inApp = true; 702 | } 703 | 704 | /** 705 | * 设置APP已进入后台 706 | */ 707 | leaveApp() { 708 | this.inApp = false; 709 | } 710 | 711 | } 712 | 713 | export { 714 | instance, 715 | YeIMUniSDK, 716 | YeIMUniSDKDefines 717 | }; 718 | -------------------------------------------------------------------------------- /src/yeim-uni-sdk.min.d.ts: -------------------------------------------------------------------------------- 1 | export declare module './yeim-uni-sdk.min.js' { 2 | /** 3 | * 4 | * YeIM-Uni-SDK 5 | * 可以私有化部署的全开源即时通讯js-sdk,仅需集成 SDK 即可轻松实现聊天能力,支持Web或uni-app项目接入使用,满足通信需要。 6 | * 7 | * @author wzJun1 8 | * @link https://github.com/wzJun1/YeIM-Uni-SDK 9 | * @doc https://wzjun1.netlify.app/ye_plugins/sdk/yeimunisdk 10 | * @copyright wzJun1 2022, 2023. All rights reserved. 11 | * @license Apache-2.0 12 | * 13 | */ 14 | declare class YeIMUniSDK { 15 | 16 | /** 17 | * 初始化SDK 18 | * 19 | * @param options 配置项 20 | * @param options.baseURL - YeIMServer http url (如无特殊需求,服务端启动后仅需修改ip或者域名即可) 21 | * @param options.socketURL - YeIMServer socket url(如无特殊需求,服务端启动后仅需修改ip或者域名即可) 22 | * @param options.logLevel - SDK 日志等级 23 | * @param options.reConnectInterval - socket 重连时间间隔 24 | * @param options.reConnectTotal - socket 最大重连次数,0不限制一直重连 25 | * @param options.heartInterval - socket 心跳时间间隔(默认30s) 26 | * @param options.notification - APP离线通知配置 27 | * @returns {YeIMUniSDK} YeIMUniSDK实例化对象 28 | */ 29 | static init(options : { 30 | 31 | /** 32 | * YeIMServer http url (如无特殊需求,服务端启动后仅需修改ip或者域名即可) 33 | */ 34 | baseURL : string; 35 | 36 | /** 37 | * YeIMServer socket url(如无特殊需求,服务端启动后仅需修改ip或者域名即可) 38 | */ 39 | socketURL : string; 40 | /** 41 | * SDK 日志等级 42 | * 0 普通日志,日志量较多,接入时建议使用 43 | * 1 关键性日志,日志量较少,生产环境时建议使用 44 | * 2 无日志级别,SDK 将不打印任何日志 45 | */ 46 | 47 | logLevel ?: number; 48 | /** 49 | * socket 重连时间间隔 50 | */ 51 | 52 | reConnectInterval ?: number; 53 | 54 | /** 55 | * socket 最大重连次数,0不限制一直重连 56 | */ 57 | 58 | reConnectTotal ?: number; 59 | 60 | /** 61 | * socket 心跳时间间隔(默认30s) 62 | */ 63 | heartInterval ?: number; 64 | 65 | /** 66 | * APP离线通知配置 67 | */ 68 | notification ?: object; 69 | 70 | }) : YeIMUniSDK; 71 | 72 | /** 73 | * 获取SDK实例化对象 74 | */ 75 | static getInstance() : YeIMUniSDK?; 76 | 77 | /** 78 | * 连接登录YeIM 79 | * 80 | * @param options 配置项 81 | * @param options.userId 已注册的用户ID字符串 82 | * @param options.token 登录token,由请求服务端Http接口换取 83 | * @param {(result)=>void} [options.success] - 成功回调 84 | * @param {(error)=>void} [options.fail] - 失败回调 85 | * @returns {void} 86 | */ 87 | connect(options : { 88 | userId : string, 89 | token : string, 90 | success ?: (result : any) => void, 91 | fail ?: (error : any) => void 92 | }) : void; 93 | 94 | /** 95 | * 创建文本消息 96 | * 97 | * @param {Options} options - 创建消息参数对象 98 | * 99 | * @param {String} options.toId - 接受者用户ID 100 | * @param {String} options.conversationType - 会话类型(私聊、群聊)定义在YeIMUniSDKDefines,YeIMUniSDKDefines.CONVERSATION_TYPE.PRIVATE = 私聊,YeIMUniSDKDefines.CONVERSATION_TYPE.GROUP = 群聊 101 | * @param {Object} options.body - 文本消息对象 102 | * @param {String} options.body.text - 文本消息内容 103 | * @param {String} options.extra - 自定义扩展数据 104 | * 105 | * @return {(Object|Message)} Message 消息对象 106 | * 107 | */ 108 | createTextMessage(options : { 109 | toId : string, 110 | conversationType : YeIMUniSDKDefines.CONVERSATION_TYPE, 111 | body : { 112 | text : string 113 | }, 114 | extra ?: string 115 | }) : Message | Object; 116 | 117 | /** 118 | * 创建群聊 @ 艾特消息 119 | * 120 | * @param {Options} options - 创建消息参数对象 121 | * 122 | * @param {String} options.toId - 群ID 123 | * @param {String} options.conversationType - 会话类型(私聊、群聊)定义在YeIMUniSDKDefines,YeIMUniSDKDefines.CONVERSATION_TYPE.PRIVATE = 私聊,YeIMUniSDKDefines.CONVERSATION_TYPE.GROUP = 群聊 当前消息仅支持群里 124 | * @param {Body} options.body - 文本消息对象 125 | * @param {String} options.body.text - 文本消息内容 126 | * @param {Array} options.body.atUserIdList - 要艾特的用户ID列表 127 | * @param {String} options.extra - 自定义扩展数据 128 | * 129 | * @return {(Object|Message)} Message 消息对象 130 | * 131 | */ 132 | createTextAtMessage(options : { 133 | toId : string, 134 | conversationType : YeIMUniSDKDefines.CONVERSATION_TYPE, 135 | body : { 136 | text : string, 137 | atUserIdList : Array 138 | }, 139 | extra ?: string 140 | }) : Message | Object; 141 | 142 | /** 143 | * 创建图片消息 144 | * 145 | * 仅支持单张图片 146 | * 147 | * @param {Options} options - 创建消息参数对象 148 | * 149 | * @param {String} options.toId - 接受者用户ID 150 | * @param {String} options.conversationType - 会话类型(私聊、群聊)定义在YeIMUniSDKDefines,YeIMUniSDKDefines.CONVERSATION_TYPE.PRIVATE = 私聊,YeIMUniSDKDefines.CONVERSATION_TYPE.GROUP = 群聊 151 | * @param {Body} options.body - 图片消息对象 152 | * @param {File} options.body.file - 图片消息对象 153 | * @param {String} options.body.file.tempFilePath - 本地图片文件临时路径 154 | * @param {Number} options.body.file.width - 图片宽度 155 | * @param {Number} options.body.file.height - 图片高度 156 | * @param {String} options.extra - 自定义扩展数据 157 | * 158 | * @return {(Object|Message)} Message 消息对象 159 | * 160 | */ 161 | createImageMessage(options : { 162 | toId : string, 163 | conversationType : YeIMUniSDKDefines.CONVERSATION_TYPE, 164 | body : { 165 | file : { 166 | tempFilePath : string, 167 | width : number, 168 | height : number 169 | } 170 | }, 171 | extra ?: string 172 | }) : Message | Object; 173 | 174 | /** 175 | * 创建图片Url直发消息 176 | * 177 | * 仅支持单张图片 178 | * 179 | * @param {Options} options - 创建消息参数对象 180 | * 181 | * @param {String} options.toId - 接受者用户ID 182 | * @param {String} options.conversationType - 会话类型(私聊、群聊)定义在YeIMUniSDKDefines,YeIMUniSDKDefines.CONVERSATION_TYPE.PRIVATE = 私聊,YeIMUniSDKDefines.CONVERSATION_TYPE.GROUP = 群聊 183 | * @param {Body} options.body - 图片消息对象 184 | * @param {String} options.body.originalUrl - 原图网络Url 185 | * @param {Number} options.body.originalWidth - 原图宽度 186 | * @param {Number} options.body.originalHeight - 原图高度 187 | * @param {String} options.body.thumbnailUrl - 缩略图网络Url 188 | * @param {Number} options.body.thumbnailWidth - 缩略图宽度 189 | * @param {Number} options.body.thumbnailHeight - 缩略图高度 190 | * @param {String} options.extra - 自定义扩展数据 191 | * 192 | * @return {(Object|Message)} Message 消息对象 193 | * 194 | */ 195 | createImageMessageFromUrl(options : { 196 | toId : string, 197 | conversationType : YeIMUniSDKDefines.CONVERSATION_TYPE, 198 | body : { 199 | originalUrl : string, 200 | originalWidth : number, 201 | originalHeight : number, 202 | thumbnailUrl : string, 203 | thumbnailWidth : number, 204 | thumbnailHeight : number 205 | }, 206 | extra ?: string 207 | }) : Message | Object; 208 | 209 | 210 | /** 211 | * 创建语音消息 212 | * 213 | * 仅支持AAC格式音频 214 | * 215 | * @param {Options} options - 创建消息参数对象 216 | * 217 | * @param {String} options.toId - 接受者用户ID 218 | * @param {String} options.conversationType - 会话类型(私聊、群聊)定义在YeIMUniSDKDefines,YeIMUniSDKDefines.CONVERSATION_TYPE.PRIVATE = 私聊,YeIMUniSDKDefines.CONVERSATION_TYPE.GROUP = 群聊 219 | * @param {Body} options.body - 音频消息对象 220 | * @param {File} options.body.file - 音频文件信息 221 | * @param {String} options.body.file.tempFilePath - 本地音频文件临时路径 222 | * @param {Number} options.body.file.duration - 音频时长,单位秒 223 | * @param {String} options.extra - 自定义扩展数据 224 | * 225 | * @return {(Object|Message)} Message 消息对象 226 | * 227 | */ 228 | createAudioMessage(options : { 229 | toId : string, 230 | conversationType : YeIMUniSDKDefines.CONVERSATION_TYPE, 231 | body : { 232 | file : { 233 | tempFilePath : string, 234 | duration : number 235 | } 236 | }, 237 | extra ?: string 238 | }) : Message | Object; 239 | 240 | /** 241 | * 创建语音Url直发消息 242 | * 243 | * @param {Options} options - 创建消息参数对象 244 | * 245 | * @param {String} options.toId - 接受者用户ID 246 | * @param {String} options.conversationType - 会话类型(私聊、群聊)定义在YeIMUniSDKDefines,YeIMUniSDKDefines.CONVERSATION_TYPE.PRIVATE = 私聊,YeIMUniSDKDefines.CONVERSATION_TYPE.GROUP = 群聊 247 | * @param {Body} options.body - 音频消息对象 248 | * @param {String} options.body.audioUrl - 音频网络Url 249 | * @param {Number} options.body.duration - 音频时长,单位秒 250 | * @param {String} options.extra - 自定义扩展数据 251 | * 252 | * @return {(Object|Message)} Message 消息对象 253 | * 254 | */ 255 | createAudioMessageFromUrl(options : { 256 | toId : string, 257 | conversationType : YeIMUniSDKDefines.CONVERSATION_TYPE, 258 | body : { 259 | tempFilePath : string, 260 | duration : number 261 | }, 262 | extra ?: string 263 | }) : Message | Object; 264 | 265 | /** 266 | * 创建小视频消息 267 | * 268 | * @param {Options} options - 创建消息参数对象 269 | * 270 | * @param {String} options.toId - 接受者用户ID 271 | * @param {String} options.conversationType - 会话类型(私聊、群聊)定义在YeIMUniSDKDefines,YeIMUniSDKDefines.CONVERSATION_TYPE.PRIVATE = 私聊,YeIMUniSDKDefines.CONVERSATION_TYPE.GROUP = 群聊 272 | * @param {Body} options.body - 视频消息对象 273 | * @param {File} options.body.file - 视频文件信息 274 | * @param {String} options.body.file.tempFilePath - 本地小视频文件临时路径 275 | * @param {Number} options.body.file.duration - 视频时长 276 | * @param {Number} options.body.file.width - 视频宽度 277 | * @param {Number} options.body.file.height - 视频高度 278 | * @param {String} options.extra - 自定义扩展数据 279 | * 280 | * @return {(Object|Message)} Message 消息对象 281 | * 282 | */ 283 | createVideoMessage(options : { 284 | toId : string, 285 | conversationType : YeIMUniSDKDefines.CONVERSATION_TYPE, 286 | body : { 287 | file : { 288 | tempFilePath : string, 289 | duration : number, 290 | width : number, 291 | height : number 292 | } 293 | }, 294 | extra ?: string 295 | }) : Message | Object; 296 | 297 | /** 298 | * 创建小视频Url直发消息 299 | * 300 | * @param {Options} options - 创建消息参数对象 301 | * 302 | * @param {String} options.toId - 接受者用户ID 303 | * @param {String} options.conversationType - 会话类型(私聊、群聊)定义在YeIMUniSDKDefines,YeIMUniSDKDefines.CONVERSATION_TYPE.PRIVATE = 私聊,YeIMUniSDKDefines.CONVERSATION_TYPE.GROUP = 群聊 304 | * @param {Body} options.body - 视频消息对象 305 | * @param {String} options.body.videoUrl - 视频网络Url 306 | * @param {String} options.body.thumbnailUrl - 视频缩略图网络Url 307 | * @param {Number} options.body.duration - 视频时长 308 | * @param {Number} options.body.width - 视频宽度 309 | * @param {Number} options.body.height - 视频高度 310 | * @param {String} options.extra - 自定义扩展数据 311 | * 312 | * @return {(Object|Message)} Message 消息对象 313 | * 314 | */ 315 | createVideoMessageFromUrl(options : { 316 | toId : string, 317 | conversationType : YeIMUniSDKDefines.CONVERSATION_TYPE, 318 | body : { 319 | videoUrl : string, 320 | thumbnailUrl : string, 321 | duration : number, 322 | width : number, 323 | height : number 324 | }, 325 | extra ?: string 326 | }) : Message | Object; 327 | 328 | /** 329 | * 创建位置消息 330 | * 331 | * @param {Options} options - 创建消息参数对象 332 | * 333 | * { "toId": "接受者用户ID", "conversationType": YeIMUniSDKDefines.CONVERSATION_TYPE.PRIVATE, body: { address: "地址名称", description: "地址详细描述", longitude: 105.000000, latitude: 31.000000 }} 334 | * 335 | * @param {String} options.toId - 接受者用户ID 336 | * @param {String} options.conversationType - 会话类型(私聊、群聊)定义在YeIMUniSDKDefines,YeIMUniSDKDefines.CONVERSATION_TYPE.PRIVATE = 私聊,YeIMUniSDKDefines.CONVERSATION_TYPE.GROUP = 群聊 337 | * @param {Body} options.body - 位置消息对象 338 | * @param {String} options.body.address - 地址名称 339 | * @param {String} options.body.description - 地址详细描述 340 | * @param {Number} options.body.longitude - 经度 341 | * @param {Number} options.body.latitude - 纬度 342 | * @param {String} options.extra - 自定义扩展数据 343 | * 344 | * @return {(Object|Message)} Message 消息对象 345 | * 346 | */ 347 | createLocationMessage(options : { 348 | toId : string, 349 | conversationType : YeIMUniSDKDefines.CONVERSATION_TYPE, 350 | body : { 351 | address : string, 352 | description : string, 353 | longitude : number, 354 | latitude : number 355 | }, 356 | extra ?: string 357 | }) : Message | Object; 358 | 359 | /** 360 | * 创建自定义消息 361 | * 362 | * 自定义消息内容放在body字段 363 | * 364 | * @param {Options} options - 创建消息参数对象 365 | * 366 | * { "toId": "接受者用户ID", "conversationType": YeIMUniSDKDefines.CONVERSATION_TYPE.PRIVATE, body: {} } 367 | * 368 | * @param {String} options.toId - 接受者用户ID 369 | * @param {String} options.conversationType - 会话类型(私聊、群聊)定义在YeIMUniSDKDefines,YeIMUniSDKDefines.CONVERSATION_TYPE.PRIVATE = 私聊,YeIMUniSDKDefines.CONVERSATION_TYPE.GROUP = 群聊 370 | * @param {Body} options.body - 自定义消息内容 371 | * @param {String|Object} options.body.custom - 消息内容 372 | * @param {String} options.extra - 自定义扩展数据 373 | * 374 | * @return {(Object|Message)} Message 消息对象 375 | * 376 | */ 377 | createCustomMessage(options : { 378 | toId : string, 379 | conversationType : YeIMUniSDKDefines.CONVERSATION_TYPE, 380 | body : { 381 | custom : string | object 382 | }, 383 | extra ?: string 384 | }) : Message | Object; 385 | 386 | /** 387 | * 创建合并消息 388 | * 389 | * @param {Options} options - 创建消息参数对象 390 | * 391 | * @param {String} options.toId - 接受者用户ID 392 | * @param {String} options.conversationType - 会话类型(私聊、群聊)定义在YeIMUniSDKDefines,YeIMUniSDKDefines.CONVERSATION_TYPE.PRIVATE = 私聊,YeIMUniSDKDefines.CONVERSATION_TYPE.GROUP = 群聊 393 | * @param {Body} options.body - 文本消息对象 394 | * @param {String} options.body.title - 合并消息的标题 395 | * @param {Array} options.body.messageList - 合并消息的标题 396 | * @param {Array} options.body.summaryList - 合并消息的标题 397 | * @param {String} options.extra - 自定义扩展数据 398 | * 399 | * @return {(Object|Message)} Message 消息对象 400 | * 401 | */ 402 | createMergerMessage(options : { 403 | toId : string, 404 | conversationType : YeIMUniSDKDefines.CONVERSATION_TYPE, 405 | body : { 406 | title : string, 407 | messageList : Array, 408 | summaryList : Array 409 | }, 410 | extra ?: string 411 | }) : Message | Object; 412 | 413 | /** 414 | * 创建转发消息 415 | * 416 | * @param {Options} options - 创建消息参数对象 417 | * 418 | * @param {String} options.toId - 接受者用户ID 419 | * @param {String} options.conversationType - 会话类型(私聊、群聊)定义在YeIMUniSDKDefines,YeIMUniSDKDefines.CONVERSATION_TYPE.PRIVATE = 私聊,YeIMUniSDKDefines.CONVERSATION_TYPE.GROUP = 群聊 420 | * @param {Body} options.body - 文本消息对象 421 | * @param {Message} options.body.message - 要转发的消息 422 | * @param {String} options.extra - 自定义扩展数据 423 | * 424 | * @return {(Object|Message)} Message 消息对象 425 | * 426 | */ 427 | createForwardMessage(options : { 428 | toId : string, 429 | conversationType : YeIMUniSDKDefines.CONVERSATION_TYPE, 430 | body : { 431 | message : Message | object, 432 | }, 433 | extra ?: string 434 | }) : Message | Object; 435 | 436 | /** 437 | * 438 | * 通用上传接口 439 | * 440 | * @param {Options} options 441 | * @param {String} options.filename - 文件名称(需带后缀) 442 | * @param {String} options.filepath - 本地文件临时路径 443 | * @param {(result)=>void} [options.success] - 成功回调 444 | * @param {(error)=>void} [options.fail] - 失败回调 445 | * @param {(progress)=>void} [options.onProgress] - 上传进度回调 446 | * 447 | */ 448 | upload(options : { 449 | filename : string, 450 | filepath : string, 451 | success ?: (result : any) => void, 452 | fail ?: (error : any) => void 453 | }) : void; 454 | 455 | /** 456 | * 457 | * @version 1.1.7 458 | * 459 | * 获取历史消息记录 460 | * 461 | * @param {Options} options - 参数对象 462 | * 463 | * @param {String} options.nextMessageId - 下次拉取的开始ID 464 | * @param {String} options.conversationId - 会话ID 465 | * @param {Number} options.limit - 拉取数量,默认:20 466 | * @param {(result)=>void} options.success - 成功回调 467 | * @param {(error)=>void} options.fail - 失败回调 468 | * 469 | */ 470 | getHistoryMessageList(options : { 471 | nextMessageId ?: string, 472 | conversationId : string, 473 | limit ?: number, 474 | success ?: (result : any) => void, 475 | fail ?: (error : any) => void 476 | }) : void; 477 | 478 | /** 479 | * 480 | * 删除消息 481 | * 482 | * @param {Options} options - 参数对象 483 | * 484 | * @param {Message} message - 要删除的消息对象 485 | * @param {(result)=>void} options.success - 成功回调 486 | * @param {(error)=>void} options.fail - 失败回调 487 | * 488 | */ 489 | deleteMessage(options : { 490 | message : Message | object, 491 | success ?: (result : any) => void, 492 | fail ?: (error : any) => void 493 | }) : void; 494 | 495 | /** 496 | * 497 | * 撤回消息 498 | * 499 | * @param {Options} options - 参数对象 500 | * 501 | * @param {Message} message - 要撤回的消息对象 502 | * @param {(result)=>void} options.success - 成功回调 503 | * @param {(error)=>void} options.fail - 失败回调 504 | * 505 | */ 506 | revokeMessage(options : { 507 | message : Message | object, 508 | success ?: (result : any) => void, 509 | fail ?: (error : any) => void 510 | }) : void; 511 | 512 | /** 513 | * 根据会话ID获取本地会话详情 514 | * 515 | * @param {String} conversationId - 会话ID 516 | * @return {Conversation} conversation 517 | */ 518 | getConversation(conversationId : string) : Conversation | object; 519 | 520 | /** 521 | * 获取本地会话列表 522 | * 523 | * @param {Options} options - 参数对象 524 | * 525 | * @param {String} options.page - 页码 526 | * @param {String} options.limit - 每页数量 527 | * @param {(result)=>{}} [options.success] - 成功回调 528 | * @param {(error)=>{}} [options.fail] - 失败回调 529 | */ 530 | getConversationList(options : { 531 | page : number, 532 | limit : number, 533 | success ?: (result : any) => void, 534 | fail ?: (error : any) => void 535 | }) : void; 536 | 537 | /** 538 | * 根据会话ID删除会话和聊天记录(包括云端) 539 | * 540 | * @param {Object} conversationId 541 | * @return {void} 542 | */ 543 | deleteConversation(conversationId : string) : void; 544 | 545 | /** 546 | * 清除指定会话未读数 547 | * 548 | * 云端同时发送给对端已读事件(私聊) 549 | * 550 | * @param {String} conversationId - 会话ID 551 | * @return {void} 552 | */ 553 | clearConversationUnread(conversationId : string) : void; 554 | 555 | /** 556 | * 557 | * 发送消息统一入口 558 | * 559 | * @param {Options} options - 参数对象 560 | * 561 | * @param {Message} options.message - 消息对象 562 | * @param {(result)=>void} [options.success] - 成功回调 563 | * @param {(error)=>void} [options.fail] - 失败回调 564 | * 565 | * @example 566 | * sendMessage({ 567 | message: message, 568 | success: (result) => {}, 569 | fail: (error) => {} 570 | }); 571 | */ 572 | sendMessage(options : { 573 | message : Message | Object | undefined, 574 | success ?: (result : any) => void, 575 | fail ?: (error : any) => void 576 | }) : void; 577 | 578 | /** 579 | * 580 | * 获取用户信息 581 | * 582 | * @param {Options} options - 参数对象 583 | * 584 | * @param {String} options.userId - 用户ID 585 | * @param {(result)=>void} [options.success] - 成功回调 586 | * @param {(error)=>void} [options.fail] - 失败回调 587 | * 588 | */ 589 | getUserInfo(options : { 590 | userId : string, 591 | success ?: (result : any) => void, 592 | fail ?: (error : any) => void 593 | }) : void; 594 | 595 | /** 596 | * 597 | * 更新我的用户资料 598 | * 599 | * @param {Options} options - 参数对象 600 | * 601 | * @param {String} options.nickname - 昵称 602 | * @param {String} options.avatarUrl - 头像地址 603 | * @param {Number} options.gender - 性别,0=未知,1=男性,2=女性 604 | * @param {Number} options.mobile - 电话 605 | * @param {String} options.email - 邮箱 606 | * @param {String} options.birthday - 生日 607 | * @param {String} options.motto - 个性签名 608 | * @param {String} options.extend - 用户自定义扩展字段 609 | * @param {Number} options.addFriendType - 好友添加我的方式 610 | * @param {(result)=>void} [options.success] - 成功回调 611 | * @param {(error)=>void} [options.fail] - 失败回调 612 | * 613 | */ 614 | updateUserInfo(options : { 615 | nickname ?: string, 616 | avatarUrl ?: string, 617 | gender ?: number, 618 | mobile ?: number, 619 | email ?: string, 620 | birthday ?: string, 621 | motto ?: string, 622 | extend ?: string, 623 | addFriendType ?: YeIMUniSDKDefines.USER.ADDFRIEND, 624 | success ?: (result : any) => void, 625 | fail ?: (error : any) => void 626 | }) : void; 627 | 628 | /** 629 | * 630 | * 获取黑名单列表 631 | * 632 | * @param {Options} options - 参数对象 633 | * 634 | * @param {(result)=>void} [options.success] - 成功回调 635 | * @param {(error)=>void} [options.fail] - 失败回调 636 | * 637 | */ 638 | getBlackUserList(options : { 639 | success ?: (result : any) => void, 640 | fail ?: (error : any) => void 641 | }) : void; 642 | 643 | /** 644 | * 645 | * 添加用户到黑名单 646 | * 647 | * 648 | * @param {Options} options - 参数对象 649 | * 650 | * 651 | * @param {Array} options.members - 用户ID列表 652 | * @param {(result)=>void} [options.success] - 成功回调 653 | * @param {(error)=>void} [options.fail] - 失败回调 654 | * 655 | * @example 656 | * addToBlackUserList({ 657 | members: ["user1", "user2"], 658 | success: (result) => {}, 659 | fail: (error) => {} 660 | }); 661 | */ 662 | addToBlackUserList(options : { 663 | members : Array 664 | success ?: (result : any) => void, 665 | fail ?: (error : any) => void 666 | }) : void; 667 | 668 | /** 669 | * 670 | * 把用户移出黑名单 671 | * 672 | * 673 | * @param {Options} options - 参数对象 674 | * 675 | * 676 | * @param {Array} options.members - 用户ID列表 677 | * @param {(result)=>void} [options.success] - 成功回调 678 | * @param {(error)=>void} [options.fail] - 失败回调 679 | * 680 | * @example 681 | * removeFromBlacklist({ 682 | members: ["user1", "user2"], 683 | success: (result) => {}, 684 | fail: (error) => {} 685 | }); 686 | */ 687 | removeFromBlacklist(options : { 688 | members : Array 689 | success ?: (result : any) => void, 690 | fail ?: (error : any) => void 691 | }) : void; 692 | 693 | /** 694 | * 添加IM事件监听器 695 | * @param {Object} event - 事件类型 YeIMUniSDKDefines.EVENT 696 | * @param {(result)=>void} callback 监听回调方法 697 | */ 698 | addEventListener(event : string, callback : (result : any) => void); 699 | 700 | /** 701 | * 移除IM事件监听器 702 | * @param {Object} event - 事件类型 YeIMUniSDKDefines.EVENT 703 | * @param {(result)=>void} callback 监听回调方法 704 | */ 705 | removeEventListener(event : string, callback : (result : any) => void); 706 | 707 | /** 708 | * 709 | * 创建群组 710 | * 711 | * @param {Options} options - 参数对象 712 | * 713 | * @param {String} options.name - 群名称 714 | * @param {String} options.avatarUrl - 群头像 715 | * @param {String} [options.groupId] - 群ID,未填写时系统自动生成 716 | * @param {Number} [options.joinMode] - 群申请处理方式 - YeIMUniSDKDefines.GROUP.JOINMODE 717 | * @param {String} [options.introduction] - 群简介 718 | * @param {String} [options.notification] - 群公告 719 | * @param {Array} [options.members] - 创建群聊初始化成员(用户ID数组) 720 | * @param {(result)=>void} [options.success] - 成功回调 721 | * @param {(error)=>void} [options.fail] - 失败回调 722 | * 723 | * @example 724 | * createGroup({ 725 | name: "", 726 | avatarUrl: "", 727 | success: (result) => {}, 728 | fail: (error) => {} 729 | }); 730 | */ 731 | createGroup(options : { 732 | name : string, 733 | avatarUrl : string, 734 | groupId ?: string, 735 | joinMode ?: number, 736 | introduction ?: string, 737 | notification ?: string, 738 | members ?: Array, 739 | success ?: (result : any) => void, 740 | fail ?: (error : any) => void 741 | }) : void; 742 | 743 | /** 744 | * 745 | * 解散群组 746 | * 747 | * 仅群主可操作 748 | * 749 | * @param {Options} options - 参数对象 750 | * 751 | * @param {String} [options.groupId] - 群ID 752 | * @param {(result)=>void} [options.success] - 成功回调 753 | * @param {(error)=>void} [options.fail] - 失败回调 754 | * 755 | * @example 756 | * dissolveGroup({ 757 | groupId: "", 758 | success: (result) => {}, 759 | fail: (error) => {} 760 | }); 761 | */ 762 | dissolveGroup(options : { 763 | groupId : string, 764 | success ?: (result : any) => void, 765 | fail ?: (error : any) => void 766 | }) : void; 767 | 768 | /** 769 | * 770 | * 更新群组资料 771 | * 772 | * @param {Options} options - 参数对象 773 | * 774 | * @param {String} [options.groupId] - 群ID 775 | * @param {String} [options.name] - 群名称 776 | * @param {String} [options.avatarUrl] - 群头像 777 | * @param {YeIMUniSDKDefines.GROUP.JOINMODE} [options.joinMode] - 群申请处理方式 778 | * @param {String} [options.introduction] - 群简介 779 | * @param {String} [options.notification] - 群公告 780 | * @param {Number} [options.isMute] - 全体禁言 0,1 781 | * @param {(result)=>void} [options.success] - 成功回调 782 | * @param {(error)=>void} [options.fail] - 失败回调 783 | * 784 | * @example 785 | * updateGroup({ 786 | groupId: "", 787 | name: "", 788 | avatarUrl: "", 789 | success: (result) => {}, 790 | fail: (error) => {} 791 | }); 792 | */ 793 | updateGroup(options : { 794 | groupId : string, 795 | name ?: string, 796 | avatarUrl ?: string, 797 | joinMode ?: number, 798 | introduction ?: string, 799 | notification ?: string, 800 | members ?: Array, 801 | success ?: (result : any) => void, 802 | fail ?: (error : any) => void 803 | }) : void; 804 | 805 | /** 806 | * 807 | * 通过群ID获取群组资料 808 | * 809 | * @param {Options} options - 参数对象 810 | * 811 | * @param {String} options.groupId - 群ID 812 | * @param {(result)=>void} [options.success] - 成功回调 813 | * @param {(error)=>void} [options.fail] - 失败回调 814 | * 815 | * @example 816 | * getGroup({ 817 | groupId: "", 818 | success: (result) => {}, 819 | fail: (error) => {} 820 | }); 821 | */ 822 | getGroup(options : { 823 | groupId : string, 824 | success ?: (result : any) => void, 825 | fail ?: (error : any) => void 826 | }) : void; 827 | 828 | /** 829 | * 830 | * 转让群主 831 | * 832 | * 仅群主可操作 833 | * 834 | * @param {Options} options - 参数对象 835 | * 836 | * @param {String} [options.groupId] - 群ID 837 | * @param {String} [options.userId] - 转让用户ID 838 | * @param {(result)=>void} [options.success] - 成功回调 839 | * @param {(error)=>void} [options.fail] - 失败回调 840 | * 841 | * @example 842 | * transferLeader({ 843 | groupId: "", 844 | userId: "", 845 | success: (result) => {}, 846 | fail: (error) => {} 847 | }); 848 | */ 849 | transferLeader(options : { 850 | groupId : string, 851 | userId : string, 852 | success ?: (result : any) => void, 853 | fail ?: (error : any) => void 854 | }) : void; 855 | 856 | /** 857 | * 858 | * 获取我的群组列表 859 | * 860 | * @param {Options} options - 参数对象 861 | * 862 | * @param {(result)=>void} [options.success] - 成功回调 863 | * @param {(error)=>void} [options.fail] - 失败回调 864 | * 865 | * @example 866 | * getGroupList({ 867 | success: (result) => {}, 868 | fail: (error) => {} 869 | }); 870 | */ 871 | getGroupList(options : { 872 | success ?: (result : any) => void, 873 | fail ?: (error : any) => void 874 | }) : void; 875 | 876 | /** 877 | * 878 | * 用户申请入群 879 | * 880 | * IM内用户均可调用此接口申请进入传入的群组ID的群,根据群申请处理方式不同,可能直接进入,也可能等待审核 881 | * 882 | * @param {Options} options - 参数对象 883 | * 884 | * @param {String} options.groupId - 群ID 885 | * @param {(result)=>void} [options.success] - 成功回调 886 | * @param {(error)=>void} [options.fail] - 失败回调 887 | * 888 | * @example 889 | * joinGroup({ 890 | groupId: "", 891 | success: (result) => {}, 892 | fail: (error) => {} 893 | }); 894 | */ 895 | joinGroup(options : { 896 | groupId : string, 897 | success ?: (result : any) => void, 898 | fail ?: (error : any) => void 899 | }) : void; 900 | 901 | /** 902 | * 903 | * 退出群组 904 | * 905 | * @param {Options} options - 参数对象 906 | * 907 | * @param {String} options.groupId - 群ID 908 | * @param {(result)=>void} [options.success] - 成功回调 909 | * @param {(error)=>void} [options.fail] - 失败回调 910 | * 911 | * @example 912 | * leaveGroup({ 913 | groupId: "", 914 | success: (result) => {}, 915 | fail: (error) => {} 916 | }); 917 | */ 918 | leaveGroup(options : { 919 | groupId : string, 920 | success ?: (result : any) => void, 921 | fail ?: (error : any) => void 922 | }) : void; 923 | 924 | /** 925 | * 926 | * 添加群成员 927 | * 928 | * 不限权限,IM内用户均可调用此接口,但根据群申请处理方式不同,可能直接进入,也可能等待审核 929 | * 930 | * @param {Options} options - 参数对象 931 | * 932 | * @param {String} options.groupId - 群ID 933 | * @param {Array} options.members - 用户ID列表 934 | * @param {(result)=>void} [options.success] - 成功回调 935 | * @param {(error)=>void} [options.fail] - 失败回调 936 | * 937 | * @example 938 | * addGroupUsers({ 939 | groupId: "", 940 | members: ["user1", "user2"], 941 | success: (result) => {}, 942 | fail: (error) => {} 943 | }); 944 | */ 945 | addGroupUsers(options : { 946 | groupId : string, 947 | members : Array, 948 | success ?: (result : any) => void, 949 | fail ?: (error : any) => void 950 | }) : void; 951 | 952 | /** 953 | * 954 | * 移除群成员 955 | * 956 | * 仅群主可使用此接口 957 | * 958 | * @param {Object} options - 参数对象 959 | * 960 | * @param {String} options.groupId - 群ID 961 | * @param {Array} options.members - 用户ID列表 962 | * @param {(result)=>void} [options.success] - 成功回调 963 | * @param {(error)=>void} [options.fail] - 失败回调 964 | * 965 | * @example 966 | * removeGroupUsers({ 967 | groupId: "", 968 | members: ["user1", "user2"], 969 | success: (result) => {}, 970 | fail: (error) => {} 971 | }); 972 | */ 973 | removeGroupUsers(options : { 974 | groupId : string, 975 | members : Array, 976 | success ?: (result : any) => void, 977 | fail ?: (error : any) => void 978 | }) : void; 979 | 980 | /** 981 | * 982 | * 设置群管理员 983 | * 984 | * 仅群主可操作此接口 985 | * 986 | * @param {Options} options - 参数对象 987 | * 988 | * @param {String} options.groupId - 群ID 989 | * @param {String} options.userId - 要设置的用户ID 990 | * @param {Number} options.isAdmin - 是否设置管理员,0=取消,1=设置 991 | * @param {(result)=>void} [options.success] - 成功回调 992 | * @param {(error)=>void} [options.fail] - 失败回调 993 | * 994 | * @example 995 | * setAdminstrator({ 996 | groupId: "", 997 | userId: "", 998 | isAdmin: 1, 999 | success: (result) => {}, 1000 | fail: (error) => {} 1001 | }); 1002 | */ 1003 | setAdminstrator(options : { 1004 | groupId : string, 1005 | userId : string, 1006 | isAdmin : number, 1007 | success ?: (result : any) => void, 1008 | fail ?: (error : any) => void 1009 | }) : void; 1010 | 1011 | /** 1012 | * 1013 | * 禁言群成员 1014 | * 1015 | * 群组中群主或管理员可调用此接口禁言群成员,每次设置的分钟数均从操作时间开始算起,设置0则取消禁言 1016 | * 1017 | * @param {Options} options - 参数对象 1018 | * 1019 | * @param {String} options.groupId - 群ID 1020 | * @param {String} options.userId - 禁言用户ID 1021 | * @param {Number} options.time - 禁言分钟数 1022 | * @param {(result)=>void} [options.success] - 成功回调 1023 | * @param {(error)=>void} [options.fail] - 失败回调 1024 | * 1025 | * @example 1026 | * setMute({ 1027 | groupId: "group_1", 1028 | userId: "user1", 1029 | time: 10, 1030 | success: (result) => {}, 1031 | fail: (error) => {} 1032 | }); 1033 | */ 1034 | setMute(options : { 1035 | groupId : string, 1036 | userId : string, 1037 | time : number, 1038 | success ?: (result : any) => void, 1039 | fail ?: (error : any) => void 1040 | }) : void; 1041 | 1042 | /** 1043 | * 1044 | * 获取群组用户入群申请列表 1045 | * 1046 | * 群主或管理员调用此接口可获取名下所有群组入群申请列表 1047 | * 1048 | * @param {Options} options - 参数对象 1049 | * 1050 | * @param {(result)=>void} [options.success] - 成功回调 1051 | * @param {(error)=>void} [options.fail] - 失败回调 1052 | * 1053 | * @example 1054 | * getGroupApplyList({ 1055 | success: (result) => {}, 1056 | fail: (error) => {} 1057 | }); 1058 | */ 1059 | getGroupApplyList(options : { 1060 | success ?: (result : any) => void, 1061 | fail ?: (error : any) => void 1062 | }) : void; 1063 | 1064 | /** 1065 | * 1066 | * 处理群组用户入群申请 1067 | * 1068 | * 申请记录所在的群组中群主或管理员可调用此接口处理申请 1069 | * 1070 | * @param {Options} options - 参数对象 1071 | * 1072 | * @param {Number} options.id - 申请记录的ID 1073 | * @param {Number} options.status - 处理结果 YeIMUniSDKDefines.GROUP.APPLYSTATU 1074 | * @param {(result)=>void} [options.success] - 成功回调 1075 | * @param {(error)=>void} [options.fail] - 失败回调 1076 | * 1077 | * @example 1078 | * handleApply({ 1079 | id: 5, 1080 | status: YeIMUniSDKDefines.GROUP.APPLYSTATUS.AGREE, 1081 | success: (result) => {}, 1082 | fail: (error) => {} 1083 | }); 1084 | */ 1085 | handleApply(options : { 1086 | id : number, 1087 | status : number 1088 | success ?: (result : any) => void, 1089 | fail ?: (error : any) => void 1090 | }) : void; 1091 | 1092 | /** 1093 | * 1094 | * 获取群成员列表 1095 | * 1096 | * @param {Options} options - 参数对象 1097 | * 1098 | * @param {String} options.groupId - 群ID 1099 | * @param {(result)=>void} [options.success] - 成功回调 1100 | * @param {(error)=>void} [options.fail] - 失败回调 1101 | * 1102 | * @example 1103 | * getGroupUserList({ 1104 | groupId: "group_1", 1105 | success: (result) => {}, 1106 | fail: (error) => {} 1107 | }); 1108 | */ 1109 | getGroupUserList(options : { 1110 | groupId : string, 1111 | success ?: (result : any) => void, 1112 | fail ?: (error : any) => void 1113 | }) : void; 1114 | 1115 | /** 1116 | * 1117 | * 从云端获取好友列表 1118 | * 1119 | * @param {Options} options - 参数对象 1120 | * 1121 | * @param {Boolean} options.cloud - 是否从云端拉取 1122 | * @param {Number} options.profile - 资料类型,0=简略资料,1=详细资料 (云端拉取有效) 1123 | * @param {Number} options.page - 页码 1124 | * @param {Number} options.limit - 每页数量 1125 | * 1126 | * @param {(result)=>{}} [options.success] - 成功回调 1127 | * @param {(error)=>{}} [options.fail] - 失败回调 1128 | */ 1129 | getFriendList(options : { 1130 | cloud ?: boolean, 1131 | profile ?: number, 1132 | page ?: number, 1133 | limit ?: number, 1134 | success ?: (result : any) => void, 1135 | fail ?: (error : any) => void 1136 | }) : void; 1137 | 1138 | /** 1139 | * 1140 | * 从云端获取好友申请列表 1141 | * 1142 | * @param {Options} options - 参数对象 1143 | * 1144 | * @param {Boolean} options.cloud - 是否从云端拉取 1145 | * @param {Number} options.type - 类型,0=发给我申请,1=我发出去的申请 (云端拉取有效) 1146 | * @param {Number} options.page - 页码 1147 | * @param {Number} options.limit - 每页数量 1148 | * 1149 | * @param {(result)=>{}} [options.success] - 成功回调 1150 | * @param {(error)=>{}} [options.fail] - 失败回调 1151 | */ 1152 | getFriendApplyList(options : { 1153 | cloud ?: boolean, 1154 | type ?: number, 1155 | page ?: number, 1156 | limit ?: number, 1157 | success ?: (result : any) => void, 1158 | fail ?: (error : any) => void 1159 | }) : void; 1160 | 1161 | /** 1162 | * 1163 | * 将全部好友申请设置为已读状态 1164 | * 1165 | * @param {Options} options - 参数对象 1166 | * 1167 | * @param {(result)=>{}} [options.success] - 成功回调 1168 | * @param {(error)=>{}} [options.fail] - 失败回调 1169 | */ 1170 | setApplyListRead(options : { 1171 | success ?: (result : any) => void, 1172 | fail ?: (error : any) => void 1173 | }) : void; 1174 | 1175 | /** 1176 | * 1177 | * 添加好友 1178 | * 1179 | * @param {Options} options - 参数对象 1180 | * 1181 | * @param {String} options.userId - 用户ID 1182 | * @param {String} options.remark - 好友备注 1183 | * @param {String} options.extraMessage - 附言 1184 | * 1185 | * @param {(result)=>{}} [options.success] - 成功回调 1186 | * @param {(error)=>{}} [options.fail] - 失败回调 1187 | */ 1188 | addFriend(options : { 1189 | userId : string, 1190 | remark ?: string, 1191 | extraMessage ?: string, 1192 | success ?: (result : any) => void, 1193 | fail ?: (error : any) => void 1194 | }) : void; 1195 | 1196 | /** 1197 | * 1198 | * 删除好友 1199 | * 1200 | * @param {Options} options - 参数对象 1201 | * 1202 | * @param {Array} options.members - 好友ID列表 1203 | * 1204 | * @param {(result)=>{}} [options.success] - 成功回调 1205 | * @param {(error)=>{}} [options.fail] - 失败回调 1206 | */ 1207 | deleteFriend(options : { 1208 | members : Array, 1209 | success ?: (result : any) => void, 1210 | fail ?: (error : any) => void 1211 | }) : void; 1212 | 1213 | /** 1214 | * 1215 | * 更新好友资料 1216 | * 1217 | * @param {Options} options - 参数对象 1218 | * 1219 | * @param {String} options.userId - 好友ID 1220 | * @param {String} options.remark - 好友备注 1221 | * @param {String} options.extend - 自定义扩展字段 1222 | * 1223 | * @param {(result)=>{}} [options.success] - 成功回调 1224 | * @param {(error)=>{}} [options.fail] - 失败回调 1225 | */ 1226 | updateFriend(options : { 1227 | userId : string, 1228 | remark ?: string, 1229 | extend ?: string, 1230 | success ?: (result : any) => void, 1231 | fail ?: (error : any) => void 1232 | }) : void; 1233 | 1234 | /** 1235 | * 1236 | * 同意好友申请 1237 | * 1238 | * @param {Options} options - 参数对象 1239 | * 1240 | * @param {Number} options.id - 申请ID 1241 | * @param {String} options.remark - 备注 1242 | * 1243 | * @param {(result)=>{}} [options.success] - 成功回调 1244 | * @param {(error)=>{}} [options.fail] - 失败回调 1245 | */ 1246 | acceptApply(options : { 1247 | id : number, 1248 | remark ?: string, 1249 | success ?: (result : any) => void, 1250 | fail ?: (error : any) => void 1251 | }) : void; 1252 | 1253 | /** 1254 | * 1255 | * 拒绝好友申请 1256 | * 1257 | * @param {Options} options - 参数对象 1258 | * 1259 | * @param {Number} options.id - 申请ID 1260 | * 1261 | * @param {(result)=>{}} [options.success] - 成功回调 1262 | * @param {(error)=>{}} [options.fail] - 失败回调 1263 | */ 1264 | refuseApply(options : { 1265 | id : number, 1266 | success ?: (result : any) => void, 1267 | fail ?: (error : any) => void 1268 | }) : void; 1269 | 1270 | /** 1271 | * 断开连接,此操作不会重连 1272 | */ 1273 | disConnect() : void; 1274 | 1275 | /** 1276 | * 获取当前连接状态 1277 | * 1278 | * 0:连接中 1:连接已打开 2:连接关闭中 3:连接已关闭 1279 | * @returns {number} 1280 | */ 1281 | readyState() : number; 1282 | 1283 | /** 1284 | * 设置APP在前台 1285 | */ 1286 | intoApp() : void; 1287 | 1288 | /** 1289 | * 设置APP已进入后台 1290 | */ 1291 | leaveApp() : void; 1292 | 1293 | } 1294 | 1295 | interface Message { 1296 | 1297 | } 1298 | 1299 | interface Conversation { 1300 | 1301 | } 1302 | 1303 | /** 1304 | * YeIMUniSDK预定义常量 1305 | */ 1306 | // @ts-ignore 1307 | declare const YeIMUniSDKDefines = { 1308 | /** 1309 | * 事件类型 1310 | */ 1311 | EVENT: { 1312 | /** 1313 | * 网络状态变化 1314 | */ 1315 | NET_CHANGED, 1316 | /** 1317 | * 会话列表变化 1318 | */ 1319 | CONVERSATION_LIST_CHANGED, 1320 | /** 1321 | * 收到消息 1322 | */ 1323 | MESSAGE_RECEIVED, 1324 | /** 1325 | * 撤回消息 1326 | */ 1327 | MESSAGE_REVOKED, 1328 | /** 1329 | * 私聊会话已读回执 1330 | */ 1331 | PRIVATE_READ_RECEIPT, 1332 | /** 1333 | * 用户被踢下线 1334 | */ 1335 | KICKED_OUT 1336 | }, 1337 | /** 1338 | * 会话类型 1339 | */ 1340 | CONVERSATION_TYPE: { 1341 | /** 1342 | * 私聊 1343 | */ 1344 | PRIVATE, 1345 | /** 1346 | * 群聊 1347 | */ 1348 | GROUP 1349 | }, 1350 | /** 1351 | * 消息类型 1352 | */ 1353 | MESSAGE_TYPE: { 1354 | /** 1355 | * 文本消息 1356 | */ 1357 | TEXT, 1358 | /** 1359 | * 文本 @ 消息 1360 | */ 1361 | TEXT_AT, 1362 | /** 1363 | * 图片消息 1364 | */ 1365 | IMAGE, 1366 | /** 1367 | * 语音消息 1368 | */ 1369 | AUDIO, 1370 | /** 1371 | * 小视频消息 1372 | */ 1373 | VIDEO, 1374 | /** 1375 | * 位置消息 1376 | */ 1377 | LOCATION, 1378 | /** 1379 | * 自定义消息 1380 | */ 1381 | CUSTOM, 1382 | /** 1383 | * 合并消息 1384 | */ 1385 | MERGER, 1386 | /** 1387 | * 转发消息 1388 | */ 1389 | FORWARD, 1390 | /** 1391 | * 群聊系统通知 1392 | */ 1393 | GROUP_SYS_NOTICE 1394 | }, 1395 | /** 1396 | * 用户相关常量 1397 | */ 1398 | USER: { 1399 | /** 1400 | * 加好友验证方式 1401 | */ 1402 | ADDFRIEND: { 1403 | /** 1404 | * 允许任何人添加自己为好友 1405 | */ 1406 | ALLOW, 1407 | /** 1408 | * 需要经过自己确认才能添加自己为好友 1409 | */ 1410 | CONFIRM, 1411 | /** 1412 | * 拒绝加好友 1413 | */ 1414 | DENY 1415 | } 1416 | }, 1417 | /** 1418 | * 群组相关常量 1419 | */ 1420 | GROUP: { 1421 | /** 1422 | * 群申请处理方式 1423 | */ 1424 | JOINMODE: { 1425 | /** 1426 | * 自有加入,不需要申请和审核,不需要被邀请人允许。 1427 | */ 1428 | FREE, 1429 | /** 1430 | * 验证加入,需要申请,以及群主或管理员的同意才能入群 1431 | */ 1432 | CHECK, 1433 | /** 1434 | * 禁止加入 1435 | */ 1436 | FORBIDDEN 1437 | }, 1438 | /** 1439 | * 入群申请处理结果 1440 | */ 1441 | APPLYSTATUS: { 1442 | /** 1443 | * 待处理 1444 | */ 1445 | PENDING, 1446 | /** 1447 | * 同意 1448 | */ 1449 | AGREE, 1450 | /** 1451 | * 拒绝 1452 | */ 1453 | REFUSE 1454 | } 1455 | }, 1456 | } 1457 | } --------------------------------------------------------------------------------