server.ts 41 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206
  1. import { TUINotification } from "../../../TUIPlugin";
  2. import Message from "../../../TUICore/tim/index";
  3. import IComponentServer from "../IComponentServer";
  4. import { JSONToObject } from "./utils/utils";
  5. /**
  6. * class TUIChatServer
  7. *
  8. * TUIChat 逻辑主体
  9. */
  10. export default class TUIChatServer extends IComponentServer {
  11. public TUICore: any;
  12. public store: any;
  13. public currentStore: any = {};
  14. constructor(TUICore: any) {
  15. super();
  16. this.TUICore = TUICore;
  17. this.bindTIMEvent();
  18. this.store = TUICore.setComponentStore("TUIChat", {}, this.updateStore.bind(this));
  19. }
  20. /**
  21. * 组件销毁
  22. * destroy
  23. */
  24. public destroyed() {
  25. this.unbindTIMEvent();
  26. }
  27. /**
  28. * 数据监听回调
  29. * data listener callback
  30. *
  31. * @param {any} newValue 新数据
  32. * @param {any} oldValue 旧数据
  33. *
  34. */
  35. updateStore(newValue: any, oldValue: any) {
  36. Object.assign(this.currentStore, newValue);
  37. if (!newValue.conversation.conversationID) {
  38. this.currentStore.messageList = [];
  39. return;
  40. }
  41. if (newValue.conversation.conversationID && newValue.conversation.conversationID !== oldValue.conversation.conversationID) {
  42. this.render(newValue.conversation);
  43. }
  44. }
  45. public render(conversation: any) {
  46. const len = 15;
  47. this.currentStore.isFirstRender = true;
  48. this.currentStore.messageList = [];
  49. this.currentStore.readSet.clear();
  50. this.getMessageList({
  51. conversationID: conversation.conversationID,
  52. count: len,
  53. });
  54. if (conversation.type === this.TUICore.TIM.TYPES.CONV_GROUP) {
  55. this.currentStore.userInfo.isGroup = true;
  56. const options = {
  57. groupID: conversation.groupProfile.groupID,
  58. userIDList: [conversation.groupProfile.selfInfo.userID],
  59. };
  60. this.getGroupProfile({ groupID: conversation.groupProfile.groupID });
  61. this.getGroupMemberProfile(options).then((res: any) => {
  62. const { memberList } = res.data;
  63. const [selfInfo] = memberList;
  64. this.currentStore.selfInfo = selfInfo;
  65. });
  66. this?.TUICore?.TUIServer?.TUIGroup?.getGroupMemberList({
  67. groupID: conversation.groupProfile.groupID,
  68. count: 100,
  69. offset: 0,
  70. }).then((res: any) => {
  71. this.currentStore.allMemberList = res.data?.memberList;
  72. });
  73. } else {
  74. this.currentStore.userInfo.isGroup = false;
  75. this.currentStore.userInfo.list = [conversation?.userProfile];
  76. }
  77. }
  78. /**
  79. * /////////////////////////////////////////////////////////////////////////////////
  80. * //
  81. * // TIM 事件监听注册接口
  82. * // TIM Event listener registration interface
  83. * //
  84. * /////////////////////////////////////////////////////////////////////////////////
  85. */
  86. private bindTIMEvent() {
  87. this.TUICore.tim.on(this.TUICore.TIM.EVENT.MESSAGE_RECEIVED, this.handleMessageReceived, this);
  88. this.TUICore.tim.on(this.TUICore.TIM.EVENT.MESSAGE_MODIFIED, this.handleMessageModified, this);
  89. this.TUICore.tim.on(this.TUICore.TIM.EVENT.MESSAGE_REVOKED, this.handleMessageRevoked, this);
  90. this.TUICore.tim.on(this.TUICore.TIM.EVENT.MESSAGE_READ_BY_PEER, this.handleMessageReadByPeer, this);
  91. this.TUICore.tim.on(this.TUICore.TIM.EVENT.GROUP_LIST_UPDATED, this.handleGroupListUpdated, this);
  92. this.TUICore.tim.on(this.TUICore.TIM.EVENT.MESSAGE_READ_RECEIPT_RECEIVED, this.handleMessageReadReceiptReceived, this);
  93. }
  94. private unbindTIMEvent() {
  95. this.TUICore.tim.off(this.TUICore.TIM.EVENT.MESSAGE_RECEIVED, this.handleMessageReceived);
  96. this.TUICore.tim.off(this.TUICore.TIM.EVENT.MESSAGE_MODIFIED, this.handleMessageModified);
  97. this.TUICore.tim.off(this.TUICore.TIM.EVENT.MESSAGE_REVOKED, this.handleMessageRevoked);
  98. this.TUICore.tim.off(this.TUICore.TIM.EVENT.MESSAGE_READ_BY_PEER, this.handleMessageReadByPeer);
  99. this.TUICore.tim.off(this.TUICore.TIM.EVENT.GROUP_LIST_UPDATED, this.handleGroupListUpdated);
  100. this.TUICore.tim.off(this.TUICore.TIM.EVENT.MESSAGE_READ_RECEIPT_RECEIVED, this.handleMessageReadReceiptReceived);
  101. }
  102. private handleMessageReceived(event: any) {
  103. event?.data?.forEach((message: Message) => {
  104. if (message?.conversationID === this?.store?.conversation?.conversationID) {
  105. this.currentStore.messageList = [...this.currentStore.messageList, message];
  106. }
  107. TUINotification.getInstance().notify(message);
  108. });
  109. }
  110. private handleMessageModified(event: any) {
  111. const middleData = this.currentStore.messageList;
  112. this.currentStore.messageList = [];
  113. this.currentStore.messageList = middleData;
  114. }
  115. private handleMessageRevoked(event: any) {
  116. const middleData = this.currentStore.messageList;
  117. this.currentStore.messageList = [];
  118. this.currentStore.messageList = middleData;
  119. }
  120. private handleMessageReadByPeer(event: any) {
  121. const middleData = this.currentStore.messageList;
  122. this.currentStore.messageList = [];
  123. this.currentStore.messageList = middleData;
  124. }
  125. private handleGroupListUpdated(event: any) {
  126. event?.data.map((item: any) => {
  127. if (item?.groupID === this?.store?.conversation?.groupProfile?.groupID) {
  128. this.store.conversation.groupProfile = item;
  129. this.currentStore.conversation = {};
  130. this.currentStore.conversation = this.store.conversation;
  131. }
  132. return item;
  133. });
  134. }
  135. private handleMessageReadReceiptReceived(event: any) {
  136. const middleData = this.currentStore.messageList;
  137. this.currentStore.messageList = [];
  138. this.currentStore.messageList = middleData;
  139. }
  140. /**
  141. * /////////////////////////////////////////////////////////////////////////////////
  142. * //
  143. * // 处理 TIM 接口参数及回调
  144. * // Handling TIM interface parameters and callbacks
  145. * //
  146. * /////////////////////////////////////////////////////////////////////////////////
  147. */
  148. /**
  149. * 创建消息生成参数
  150. * Create message generation parameters
  151. *
  152. * @param {Object} content 消息体
  153. * @param {String} type 消息类型 text: 文本类型 file: 文件类型 face: 表情 location: 地址 custom: 自定义 merger: 合并 forward: 转发
  154. * @param {Callback} callback 回调函数
  155. * @param {any} to 发送的对象
  156. * @returns {options} 消息参数
  157. */
  158. public handleMessageOptions(content: any, type: string, callback?: any, to?: any) {
  159. const options: any = {
  160. to: "",
  161. conversationType: to?.type || this.store.conversation.type,
  162. payload: content,
  163. needReadReceipt: this.currentStore.needReadReceipt,
  164. };
  165. if (this.currentStore.needTyping) {
  166. options.cloudCustomData = {
  167. messageFeature: {
  168. needTyping: 1,
  169. version: 1,
  170. },
  171. };
  172. options.cloudCustomData = JSON.stringify(options.cloudCustomData);
  173. }
  174. if (type === "file" && callback) {
  175. options.onProgress = callback;
  176. }
  177. switch (options.conversationType) {
  178. case this.TUICore.TIM.TYPES.CONV_C2C:
  179. options.to = to?.userProfile?.userID || this.store.conversation?.userProfile?.userID || "";
  180. break;
  181. case this.TUICore.TIM.TYPES.CONV_GROUP:
  182. options.to = to?.groupProfile?.groupID || this.store.conversation?.groupProfile?.groupID || "";
  183. break;
  184. default:
  185. break;
  186. }
  187. return options;
  188. }
  189. /**
  190. * 处理异步函数
  191. * Handling asynchronous functions
  192. *
  193. * @param {callback} callback 回调函数
  194. * @returns {Promise} 返回异步函数
  195. */
  196. public handlePromiseCallback(callback: any) {
  197. return new Promise<void>((resolve, reject) => {
  198. const config = {
  199. TUIName: "TUIChat",
  200. callback: () => {
  201. callback && callback(resolve, reject);
  202. },
  203. };
  204. this.TUICore.setAwaitFunc(config.TUIName, config.callback);
  205. });
  206. }
  207. /**
  208. * 重试异步函数
  209. * Retry asynchronous functions
  210. * 默认执行一次,之后按时间间隔列表重复执行直到成功,重复次数完毕后仍失败则失败
  211. * Execute once by default, and then repeat it according to the time interval list until it succeeds.
  212. * If it still fails after the number of repetitions is complete, it will reject.
  213. *
  214. * @param {callback} callback 回调函数/callback function
  215. * @param {Array<number>} intervalList 间隔时间列表/interval list
  216. * @param {callback} retryBreakFn 强制重复结束条件函数/break retry function
  217. * @returns {Promise} 返回异步函数/return
  218. */
  219. public handlePromiseCallbackRetry(
  220. callback: any,
  221. intervalList: Array<number> = [],
  222. retryBreakFn: any = function () {
  223. return false;
  224. }
  225. ): Promise<any> {
  226. return new Promise<void>((resolve, reject) => {
  227. let times = 0;
  228. function tryFn() {
  229. times++;
  230. callback()
  231. .then(resolve)
  232. .catch((error: any) => {
  233. if (times > intervalList.length || (retryBreakFn && retryBreakFn(error))) {
  234. reject(error);
  235. return;
  236. }
  237. setTimeout(tryFn, intervalList[times - 1]);
  238. });
  239. }
  240. tryFn();
  241. });
  242. }
  243. /**
  244. * 文件上传进度函数处理
  245. * File upload progress function processing
  246. *
  247. * @param {number} progress 文件上传进度 1表示完成
  248. * @param {message} message 文件消息
  249. */
  250. public handleUploadProgress(progress: number, message: any) {
  251. this.currentStore.messageList.map((item: any) => {
  252. if (item.ID === message.ID) {
  253. item.progress = progress;
  254. }
  255. return item;
  256. });
  257. }
  258. /**
  259. * 删除会话
  260. *
  261. * @param {string} conversationID 会话ID
  262. * @returns {Promise}
  263. */
  264. public async deleteConversation(conversationID: string) {
  265. return this.handlePromiseCallback(async (resolve: any, reject: any) => {
  266. try {
  267. const imResponse: any = await this.TUICore.tim.deleteConversation(conversationID);
  268. resolve(imResponse);
  269. } catch (error) {
  270. reject(error);
  271. }
  272. });
  273. }
  274. /**
  275. * /////////////////////////////////////////////////////////////////////////////////
  276. * //
  277. * // TIM 方法
  278. * // TIM methods
  279. * //
  280. * /////////////////////////////////////////////////////////////////////////////////
  281. */
  282. /**
  283. * 发送表情消息
  284. * Send face messages
  285. *
  286. * @param {Object} data 消息内容/message content
  287. * @param {Number} data.index 表情索引/face index
  288. * @param {String} data.data 额外数据/extra data
  289. * @returns {Promise}
  290. */
  291. public sendFaceMessage(data: any): Promise<any> {
  292. return this.handlePromiseCallback(async (resolve: any, reject: any) => {
  293. try {
  294. const options = this.handleMessageOptions(data, "face");
  295. const message = this.TUICore.tim.createFaceMessage(options);
  296. this.currentStore.messageList.push(message);
  297. const imResponse = await this.TUICore.tim.sendMessage(message);
  298. this.currentStore.messageList = this.currentStore.messageList.map((item: any) => {
  299. if (item.ID === imResponse.data.message.ID) {
  300. return imResponse.data.message;
  301. }
  302. return item;
  303. });
  304. resolve(imResponse);
  305. } catch (error) {
  306. reject(error);
  307. const middleData = this.currentStore.messageList;
  308. this.currentStore.messageList = [];
  309. this.currentStore.messageList = middleData;
  310. }
  311. });
  312. }
  313. /**
  314. * 发送图片消息
  315. * Send image message
  316. *
  317. * @param {Image} image 图片文件/image
  318. * @returns {Promise}
  319. */
  320. public sendImageMessage(image: any): Promise<any> {
  321. return this.handlePromiseCallback(async (resolve: any, reject: any) => {
  322. try {
  323. const options = this.handleMessageOptions({ file: image }, "file", (progress: number) => {
  324. this.handleUploadProgress(progress, message);
  325. });
  326. const message = this.TUICore.tim.createImageMessage(options);
  327. message.progress = 0.01;
  328. this.currentStore.messageList.push(message);
  329. const imResponse = await this.TUICore.tim.sendMessage(message);
  330. this.currentStore.messageList = this.currentStore.messageList.map((item: any) => {
  331. if (item.ID === imResponse.data.message.ID) {
  332. return imResponse.data.message;
  333. }
  334. return item;
  335. });
  336. resolve(imResponse);
  337. } catch (error) {
  338. reject(error);
  339. const middleData = this.currentStore.messageList;
  340. this.currentStore.messageList = [];
  341. this.currentStore.messageList = middleData;
  342. }
  343. });
  344. }
  345. /**
  346. * 发送视频消息
  347. * Send video message
  348. *
  349. * @param {Video} video 视频文件/video
  350. * @returns {Promise}
  351. */
  352. public sendVideoMessage(video: any): Promise<any> {
  353. return this.handlePromiseCallback(async (resolve: any, reject: any) => {
  354. try {
  355. const options = this.handleMessageOptions({ file: video }, "file", (progress: number) => {
  356. this.handleUploadProgress(progress, message);
  357. });
  358. const message = this.TUICore.tim.createVideoMessage(options);
  359. message.progress = 0.01;
  360. this.currentStore.messageList.push(message);
  361. const imResponse = await this.TUICore.tim.sendMessage(message);
  362. this.currentStore.messageList = this.currentStore.messageList.map((item: any) => {
  363. if (item.ID === imResponse.data.message.ID) {
  364. return imResponse.data.message;
  365. }
  366. return item;
  367. });
  368. resolve(imResponse);
  369. } catch (error) {
  370. reject(error);
  371. const middleData = this.currentStore.messageList;
  372. this.currentStore.messageList = [];
  373. this.currentStore.messageList = middleData;
  374. }
  375. });
  376. }
  377. /**
  378. * 发送文件消息
  379. * Send file message
  380. *
  381. * @param {File} file 文件/file
  382. * @returns {Promise}
  383. */
  384. public sendFileMessage(file: any): Promise<any> {
  385. return this.handlePromiseCallback(async (resolve: any, reject: any) => {
  386. try {
  387. const options = this.handleMessageOptions({ file }, "file", (progress: number) => {
  388. this.handleUploadProgress(progress, message);
  389. });
  390. const message = this.TUICore.tim.createFileMessage(options);
  391. message.progress = 0.01;
  392. this.currentStore.messageList.push(message);
  393. const imResponse = await this.TUICore.tim.sendMessage(message);
  394. this.currentStore.messageList = this.currentStore.messageList.map((item: any) => {
  395. if (item.ID === imResponse.data.message.ID) {
  396. return imResponse.data.message;
  397. }
  398. return item;
  399. });
  400. resolve(imResponse);
  401. } catch (error) {
  402. reject(error);
  403. const middleData = this.currentStore.messageList;
  404. this.currentStore.messageList = [];
  405. this.currentStore.messageList = middleData;
  406. }
  407. });
  408. }
  409. /**
  410. * 发送自定义消息
  411. * Send Custom message
  412. *
  413. * @param {Object} data 消息内容/message content
  414. * @param {String} data.data 自定义消息的数据字段/custom message data fields
  415. * @param {String} data.description 自定义消息的说明字段/custom message description fields
  416. * @param {String} data.extension 自定义消息的扩展字段/custom message extension fields
  417. * @returns {Promise}
  418. */
  419. public sendCustomMessage(data: any): Promise<any> {
  420. return this.handlePromiseCallback(async (resolve: any, reject: any) => {
  421. try {
  422. data.data = JSON.stringify(data.data);
  423. const options = this.handleMessageOptions(data, "custom");
  424. const message = this.TUICore.tim.createCustomMessage(options);
  425. this.currentStore.messageList.push(message);
  426. const imResponse = await this.TUICore.tim.sendMessage(message);
  427. this.currentStore.messageList = this.currentStore.messageList.map((item: any) => {
  428. if (item.ID === imResponse.data.message.ID) {
  429. return imResponse.data.message;
  430. }
  431. return item;
  432. });
  433. resolve(imResponse);
  434. } catch (error) {
  435. reject(error);
  436. const middleData = this.currentStore.messageList;
  437. this.currentStore.messageList = [];
  438. this.currentStore.messageList = middleData;
  439. }
  440. });
  441. }
  442. /**
  443. * 发送地理位置消息
  444. * Send location message
  445. *
  446. * @param {Object} data 消息内容/message content
  447. * @param {String} data.description 地理位置描述信息/geographic descriptive information
  448. * @param {Number} data.longitude 经度/longitude
  449. * @param {Number} data.latitude 纬度/latitude
  450. * @returns {Promise}
  451. */
  452. public sendLocationMessage(data: any): Promise<any> {
  453. return this.handlePromiseCallback(async (resolve: any, reject: any) => {
  454. try {
  455. const options = this.handleMessageOptions(data, "location");
  456. const message = this.TUICore.tim.createLocationMessage(options);
  457. this.currentStore.messageList.push(message);
  458. const imResponse = await this.TUICore.tim.sendMessage(message);
  459. resolve(imResponse);
  460. } catch (error) {
  461. reject(error);
  462. const middleData = this.currentStore.messageList;
  463. this.currentStore.messageList = [];
  464. this.currentStore.messageList = middleData;
  465. }
  466. });
  467. }
  468. /**
  469. * 转发消息
  470. * forward message
  471. *
  472. * @param {message} message 消息实例/message
  473. * @param {any} to 转发的对象/forward to
  474. * @returns {Promise}
  475. */
  476. public forwardMessage(message: any, to: any): Promise<any> {
  477. return this.handlePromiseCallback(async (resolve: any, reject: any) => {
  478. try {
  479. const options = this.handleMessageOptions(message, "forward", {}, to);
  480. const imMessage = this.TUICore.tim.createForwardMessage(options);
  481. const imResponse = await this.TUICore.tim.sendMessage(imMessage);
  482. if (this.store.conversation.conversationID === imResponse.data.message.conversationID) {
  483. this.currentStore.messageList.push(imResponse.data.message);
  484. }
  485. resolve(imResponse);
  486. } catch (error) {
  487. reject(error);
  488. const middleData = this.currentStore.messageList;
  489. this.currentStore.messageList = [];
  490. this.currentStore.messageList = middleData;
  491. }
  492. });
  493. }
  494. /**
  495. * 发送消息已读回执
  496. * Send message read receipt
  497. *
  498. * @param {Array} messageList 同一个 C2C 或 GROUP 会话的消息列表,最大长度为30/A list of messages for the same C2C or GROUP conversation, with a maximum length of 30
  499. * @returns {Promise}
  500. */
  501. public async sendMessageReadReceipt(messageList: Array<any>) {
  502. return this.handlePromiseCallback(async (resolve: any, reject: any) => {
  503. try {
  504. const imResponse: any = await this.TUICore.tim.sendMessageReadReceipt(messageList);
  505. resolve(imResponse);
  506. } catch (error) {
  507. reject(error);
  508. }
  509. });
  510. }
  511. /**
  512. * 拉取已读回执列表
  513. * Pull read receipt list
  514. *
  515. * @param {Array} messageList 同一群会话的消息列表/The message list of the same group of the conversation
  516. * @returns {Promise}
  517. */
  518. public async getMessageReadReceiptList(messageList: Array<any>) {
  519. return this.handlePromiseCallback(async (resolve: any, reject: any) => {
  520. try {
  521. const imResponse: any = await this.TUICore.tim.getMessageReadReceiptList(messageList);
  522. resolve(imResponse);
  523. } catch (error) {
  524. reject(error);
  525. }
  526. });
  527. }
  528. /**
  529. * /////////////////////////////////////////////////////////////////////////////////
  530. * //
  531. * // 对外方法
  532. * //
  533. * /////////////////////////////////////////////////////////////////////////////////
  534. */
  535. /**
  536. * 获取 messageList
  537. * get messagelist
  538. *
  539. * @param {any} options 获取 messageList 参数/messageList options
  540. * @param {Boolean} history 是否获取历史消息/Whether to get historical information
  541. * @returns {Promise}
  542. */
  543. public async getMessageList(options: any, history?: boolean) {
  544. return this.handlePromiseCallback(async (resolve: any, reject: any) => {
  545. try {
  546. const imResponse = await this.TUICore.tim.getMessageList(options);
  547. if (imResponse.data.messageList.length) {
  548. await this.getMessageReadReceiptList(imResponse.data.messageList);
  549. }
  550. if (!history) {
  551. this.currentStore.messageList = imResponse.data.messageList;
  552. } else {
  553. this.currentStore.messageList = [...imResponse.data.messageList, ...this.currentStore.messageList];
  554. }
  555. this.currentStore.nextReqMessageID = imResponse.data.nextReqMessageID;
  556. this.currentStore.isCompleted = imResponse.data.isCompleted;
  557. resolve(imResponse);
  558. } catch (error) {
  559. reject(error);
  560. }
  561. });
  562. }
  563. /**
  564. * 获取历史消息
  565. * get history messagelist
  566. *
  567. * @returns {Promise}
  568. */
  569. public async getHistoryMessageList() {
  570. const options = {
  571. conversationID: this.currentStore.conversation.conversationID,
  572. nextReqMessageID: this.currentStore.nextReqMessageID,
  573. count: 15,
  574. };
  575. if (!this.currentStore.isCompleted) {
  576. this.getMessageList(options, true);
  577. }
  578. }
  579. /**
  580. * 发送文本消息
  581. * send text message
  582. *
  583. * @param {any} text 发送的消息/text message
  584. * @param {object} data 被引用消息的内容/The content of the quoted message
  585. * @returns {Promise}
  586. */
  587. public sendTextMessage(text: any, data: any): Promise<any> {
  588. return this.handlePromiseCallback(async (resolve: any, reject: any) => {
  589. try {
  590. const options = this.handleMessageOptions({ text }, "text");
  591. let cloudCustomDataObj = {};
  592. if (options.cloudCustomData) {
  593. try {
  594. cloudCustomDataObj = JSONToObject(options.cloudCustomData);
  595. } catch {
  596. cloudCustomDataObj = {};
  597. }
  598. }
  599. const cloudCustomData = JSON.stringify(data);
  600. const secondOptions = Object.assign(options, {
  601. cloudCustomData,
  602. ...cloudCustomDataObj,
  603. });
  604. const message = this.TUICore.tim.createTextMessage(secondOptions);
  605. this.currentStore.messageList.push(message);
  606. const imResponse = await this.TUICore.tim.sendMessage(message);
  607. this.currentStore.messageList = this.currentStore.messageList.map((item: any) => {
  608. if (item.ID === imResponse.data.message.ID) {
  609. return imResponse.data.message;
  610. }
  611. return item;
  612. });
  613. resolve(imResponse);
  614. } catch (error) {
  615. reject(error);
  616. const middleData = this.currentStore.messageList;
  617. this.currentStore.messageList = [];
  618. this.currentStore.messageList = middleData;
  619. }
  620. });
  621. }
  622. /**
  623. * 发送【对方正在输入中】在线自定义消息
  624. * send typing online custom message
  625. *
  626. * @param {Object} data 消息内容/message content
  627. * @param {String} data.data 自定义消息的数据字段/custom message data field
  628. * @param {String} data.description 自定义消息的说明字段/custom message description field
  629. * @param {String} data.extension 自定义消息的扩展字段/custom message extension field
  630. * @returns {Promise}
  631. */
  632. public sendTypingMessage(data: any): Promise<any> {
  633. return this.handlePromiseCallback(async (resolve: any, reject: any) => {
  634. try {
  635. data.data = JSON.stringify(data.data);
  636. const options = this.handleMessageOptions(data, "custom");
  637. const message = this.TUICore.tim.createCustomMessage(options);
  638. const imResponse = await this.TUICore.tim.sendMessage(message, {
  639. onlineUserOnly: true,
  640. });
  641. resolve(imResponse);
  642. } catch (error) {
  643. reject(error);
  644. const middleData = this.currentStore.messageList;
  645. this.currentStore.messageList = [];
  646. this.currentStore.messageList = middleData;
  647. }
  648. });
  649. }
  650. /**
  651. * 发送@ 提醒功能的文本消息
  652. * Send @ Reminder text message
  653. *
  654. * @param {any} data 消息内容/message content
  655. * @param {String} data.text 文本消息/text message
  656. * @param {Array} data.atUserList 需要 @ 的用户列表,如果需要 @ALL,请传入 TIM.TYPES.MSG_AT_ALL / List of users who need @, if you need @ALL, please pass in TIM.TYPES.MSG_AT_ALL
  657. * @returns {message}
  658. *
  659. * - 注:此接口仅用于群聊/This interface is only used for group chat
  660. */
  661. public sendTextAtMessage(data: any) {
  662. return this.handlePromiseCallback(async (resolve: any, reject: any) => {
  663. try {
  664. const options = this.handleMessageOptions(data, "text");
  665. const message = this.TUICore.tim.createTextAtMessage(options);
  666. this.currentStore.messageList.push(message);
  667. const imResponse = await this.TUICore.tim.sendMessage(message);
  668. this.currentStore.messageList = this.currentStore.messageList.map((item: any) => {
  669. if (item.ID === imResponse.data.message.ID) {
  670. return imResponse.data.message;
  671. }
  672. return item;
  673. });
  674. resolve(imResponse);
  675. } catch (error) {
  676. reject(error);
  677. const middleData = this.currentStore.messageList;
  678. this.currentStore.messageList = [];
  679. this.currentStore.messageList = middleData;
  680. }
  681. });
  682. }
  683. /**
  684. * 发送合并消息
  685. * send merger message
  686. *
  687. * @param {Object} data 消息内容/message content
  688. * @param {Array.<Message>} data.messageList 合并的消息列表/merger message list
  689. * @param {String} data.title 合并的标题/merger title
  690. * @param {String} data.abstractList 摘要列表,不同的消息类型可以设置不同的摘要信息/Summary list, different message types can set different summary information
  691. * @param {String} data.compatibleText 兼容文本/ompatible text
  692. * @returns {Promise}
  693. */
  694. public sendMergerMessage(data: any): Promise<any> {
  695. return this.handlePromiseCallback(async (resolve: any, reject: any) => {
  696. try {
  697. const options = this.handleMessageOptions(data, "merger");
  698. const message = this.TUICore.tim.createMergerMessage(options);
  699. this.currentStore.messageList.push(message);
  700. const imResponse = await this.TUICore.tim.sendMessage(message);
  701. this.currentStore.messageList = this.currentStore.messageList.map((item: any) => {
  702. if (item.ID === imResponse.data.message.ID) {
  703. return imResponse.data.message;
  704. }
  705. return item;
  706. });
  707. resolve(imResponse);
  708. } catch (error) {
  709. reject(error);
  710. const middleData = this.currentStore.messageList;
  711. this.currentStore.messageList = [];
  712. this.currentStore.messageList = middleData;
  713. }
  714. });
  715. }
  716. /**
  717. * 消息撤回
  718. * revoke message
  719. *
  720. * @param {message} message 消息实例/message
  721. * @returns {Promise}
  722. */
  723. public revokeMessage(message: any): Promise<any> {
  724. return this.handlePromiseCallback(async (resolve: any, reject: any) => {
  725. try {
  726. const imResponse = await this.TUICore.tim.revokeMessage(message);
  727. const cloudCustomData = JSONToObject(message?.cloudCustomData);
  728. if (cloudCustomData?.messageReply?.messageRootID) {
  729. await this.revokeReplyMessage(message);
  730. }
  731. resolve(imResponse);
  732. } catch (error) {
  733. reject(error);
  734. const middleData = this.currentStore.messageList;
  735. this.currentStore.messageList = [];
  736. this.currentStore.messageList = middleData;
  737. }
  738. });
  739. }
  740. /**
  741. * 重发消息
  742. * resend message
  743. *
  744. * @param {message} message 消息实例/message
  745. * @returns {Promise}
  746. */
  747. public resendMessage(message: any): Promise<any> {
  748. return this.handlePromiseCallback(async (resolve: any, reject: any) => {
  749. try {
  750. const imResponse = await this.TUICore.tim.resendMessage(message);
  751. this.currentStore.messageList = this.currentStore.messageList.filter((item: any) => item.ID !== message.ID);
  752. this.currentStore.messageList.push(imResponse.data.message);
  753. resolve(imResponse);
  754. } catch (error) {
  755. reject(error);
  756. }
  757. });
  758. }
  759. /**
  760. * 删除消息
  761. * delete message
  762. *
  763. * @param {Array.<message>} messages 消息实例/message
  764. * @returns {Promise}
  765. */
  766. public deleteMessage(messages: any): Promise<any> {
  767. return this.handlePromiseCallback(async (resolve: any, reject: any) => {
  768. try {
  769. const imResponse = await this.TUICore.tim.deleteMessage(messages);
  770. resolve(imResponse);
  771. const middleData = this.currentStore.messageList;
  772. this.currentStore.messageList = [];
  773. this.currentStore.messageList = middleData;
  774. } catch (error) {
  775. reject(error);
  776. }
  777. });
  778. }
  779. /**
  780. * 变更消息
  781. * modify message
  782. *
  783. * @param {Array.<message>} message 消息实例/message
  784. * @returns {Promise}
  785. */
  786. public modifyMessage(message: any): Promise<any> {
  787. return this.handlePromiseCallback(async (resolve: any, reject: any) => {
  788. try {
  789. const imResponse = await this.TUICore.tim.modifyMessage(message);
  790. resolve(imResponse);
  791. } catch (error) {
  792. // 修改消息失败
  793. // Modify message error
  794. const code = (error as any)?.code;
  795. const data = (error as any)?.data;
  796. if (code === 2480) {
  797. console.warn("MODIFY_MESSAGE_ERROR", "修改消息发生冲突,data.message 是最新的消息", "data.message:", data?.message);
  798. } else if (code === 2481) {
  799. console.warn("MODIFY_MESSAGE_ERROR", "不支持修改直播群消息");
  800. } else if (code === 20026) {
  801. console.warn("MODIFY_MESSAGE_ERROR", "消息不存在");
  802. }
  803. reject(error);
  804. }
  805. });
  806. }
  807. /**
  808. * 回复消息
  809. * reply message
  810. * @param {Array.<message>} message 消息实例/message
  811. * @returns {Promise}
  812. */
  813. public replyMessage(message: any, messageRoot?: any): Promise<any> {
  814. const replyFunction = () => {
  815. return this.handlePromiseCallback(async (resolve: any, reject: any) => {
  816. try {
  817. const repliesObject = {
  818. messageAbstract: message?.payload?.text,
  819. messageSender: message?.from,
  820. messageID: message?.ID,
  821. messageType: message?.type,
  822. messageTime: message?.time,
  823. messageSequence: message?.sequence,
  824. version: 1,
  825. };
  826. if (!messageRoot) {
  827. const cloudCustomData = JSONToObject(message?.cloudCustomData);
  828. const messageRootID = cloudCustomData?.messageReply?.messageRootID;
  829. messageRoot = (await this?.currentStore?.messageList?.find((item: any) => item?.ID === messageRootID)) || this.findMessage(messageRootID);
  830. }
  831. const rootCloudCustomData = messageRoot?.cloudCustomData ? JSONToObject(messageRoot?.cloudCustomData) : { messageReplies: {} };
  832. if (rootCloudCustomData?.messageReplies?.replies) {
  833. rootCloudCustomData.messageReplies.replies = [
  834. // eslint-disable-next-line no-unsafe-optional-chaining
  835. ...rootCloudCustomData?.messageReplies?.replies,
  836. repliesObject,
  837. ];
  838. } else {
  839. rootCloudCustomData.messageReplies = {
  840. replies: [repliesObject],
  841. version: 1,
  842. };
  843. }
  844. messageRoot.cloudCustomData = JSON.stringify(rootCloudCustomData);
  845. const imResponse = this.modifyMessage(messageRoot);
  846. resolve(imResponse);
  847. } catch (error) {
  848. reject(error);
  849. }
  850. });
  851. };
  852. const retryBreakFunction = function (error: any) {
  853. if (error && error?.code === 2480) return false;
  854. return true;
  855. };
  856. return this.handlePromiseCallbackRetry(replyFunction, [500, 1000, 3000], retryBreakFunction);
  857. }
  858. /**
  859. * 撤回回复消息
  860. * revoke reply message
  861. * @param {Array.<message>} message 消息实例/message
  862. * @returns {Promise}
  863. */
  864. public revokeReplyMessage(message: any, messageRoot?: any): Promise<any> {
  865. const revokeReplyFunction = () => {
  866. return this.handlePromiseCallback(async (resolve: any, reject: any) => {
  867. try {
  868. if (!messageRoot) {
  869. const cloudCustomData = JSONToObject(message?.cloudCustomData);
  870. const messageRootID = cloudCustomData?.messageReply?.messageRootID;
  871. messageRoot = (await this?.currentStore?.messageList?.find((item: any) => item?.ID === messageRootID)) || this.findMessage(messageRootID);
  872. }
  873. const rootCloudCustomData = messageRoot?.cloudCustomData ? JSONToObject(messageRoot?.cloudCustomData) : { messageReplies: {} };
  874. if (rootCloudCustomData?.messageReplies?.replies) {
  875. const index = rootCloudCustomData.messageReplies.replies.findIndex((item: any) => item?.messageID === message?.ID);
  876. rootCloudCustomData?.messageReplies?.replies?.splice(index, 1);
  877. }
  878. messageRoot.cloudCustomData = JSON.stringify(rootCloudCustomData);
  879. const imResponse = this.modifyMessage(messageRoot);
  880. resolve(imResponse);
  881. } catch (error) {
  882. reject(error);
  883. }
  884. });
  885. };
  886. const retryBreakFunction = function (error: any) {
  887. if (error && error?.code === 2480) return false;
  888. return true;
  889. };
  890. return this.handlePromiseCallbackRetry(revokeReplyFunction, [500, 1000, 3000], retryBreakFunction);
  891. }
  892. /**
  893. * 表情回应
  894. * emoji react
  895. * @param {Array.<message>} message 消息实例/message
  896. * @returns {Promise}
  897. */
  898. public emojiReact(message: any, emojiID: any): Promise<any> {
  899. const emojiReactFunction = () => {
  900. return this.handlePromiseCallback(async (resolve: any, reject: any) => {
  901. try {
  902. if (!message || !message?.ID || !emojiID) reject();
  903. const userID = this.TUICore?.TUIServer?.TUIProfile?.store?.profile?.userID;
  904. message = (await this?.currentStore?.messageList?.find((item: any) => item?.ID === message?.ID)) || this.findMessage(message?.ID);
  905. const cloudCustomData = message?.cloudCustomData ? JSONToObject(message?.cloudCustomData) : { messageReact: {} };
  906. if (cloudCustomData?.messageReact?.reacts) {
  907. if (cloudCustomData?.messageReact?.reacts[emojiID]) {
  908. const index = cloudCustomData?.messageReact?.reacts[emojiID]?.indexOf(userID);
  909. if (index === -1) {
  910. cloudCustomData?.messageReact?.reacts[emojiID]?.push(userID);
  911. } else {
  912. cloudCustomData?.messageReact?.reacts[emojiID]?.splice(index, 1);
  913. if (cloudCustomData?.messageReact?.reacts[emojiID]?.length === 0) {
  914. delete cloudCustomData?.messageReact?.reacts[emojiID];
  915. }
  916. }
  917. } else {
  918. cloudCustomData.messageReact.reacts[emojiID] = [userID];
  919. }
  920. } else {
  921. cloudCustomData.messageReact = {
  922. reacts: {},
  923. version: 1,
  924. };
  925. cloudCustomData.messageReact.reacts[emojiID] = [userID];
  926. }
  927. message.cloudCustomData = JSON.stringify(cloudCustomData);
  928. const imResponse = this.modifyMessage(message);
  929. resolve(imResponse);
  930. } catch (error) {
  931. reject(error);
  932. }
  933. });
  934. };
  935. const retryBreakFunction = function (error: any) {
  936. if (error && error?.code === 2480) return false;
  937. return true;
  938. };
  939. return this.handlePromiseCallbackRetry(emojiReactFunction, [500, 1000, 3000], retryBreakFunction);
  940. }
  941. /**
  942. * 查询消息
  943. * find message
  944. * @param {String} messageID 消息实例ID/messageID
  945. * @returns {Promise}
  946. */
  947. public findMessage(messageID: string): Promise<any> {
  948. return this.handlePromiseCallback(async (resolve: any, reject: any) => {
  949. try {
  950. const imResponse = await this.TUICore.tim.findMessage(messageID);
  951. resolve(imResponse);
  952. } catch (error) {
  953. reject(error);
  954. }
  955. });
  956. }
  957. /**
  958. * 获取群组属性
  959. * get group profile
  960. *
  961. * @param {any} options 参数
  962. * @param {String} options.groupID 群组ID
  963. * @param {Array.<String>} options.groupProfileFilter 群资料过滤器
  964. * @returns {Promise}
  965. */
  966. public getGroupProfile(options: any): Promise<any> {
  967. return this.handlePromiseCallback(async (resolve: any, reject: any) => {
  968. try {
  969. const imResponse = await this.TUICore.tim.getGroupProfile(options);
  970. this.currentStore.conversation.groupProfile = imResponse.data.group;
  971. resolve(imResponse);
  972. } catch (error) {
  973. reject(error);
  974. }
  975. });
  976. }
  977. /**
  978. * 获取群成员资料
  979. * get group member profile
  980. *
  981. * @param {any} options 参数
  982. * @param {String} options.groupID 群组ID
  983. * @param {Array.<String>} options.userIDList 要查询的群成员用户 ID 列表
  984. * @param { Array.<String>} options.memberCustomFieldFilter 群成员自定义字段筛选
  985. * @returns {Promise}
  986. */
  987. public getGroupMemberProfile(options: any): Promise<any> {
  988. return this.handlePromiseCallback(async (resolve: any, reject: any) => {
  989. try {
  990. const imResponse = await this.TUICore.tim.getGroupMemberProfile(options);
  991. resolve(imResponse);
  992. } catch (error) {
  993. reject(error);
  994. }
  995. });
  996. }
  997. /**
  998. * 处理申请加群
  999. * handling group application
  1000. * - 管理员
  1001. * administrator
  1002. *
  1003. * @param {any} options 参数
  1004. * @param {String} options.handleAction 处理结果 Agree(同意) / Reject(拒绝)
  1005. * @param {String} options.handleMessage 附言
  1006. * @param {Message} options.message 对应【群系统通知】的消息实例
  1007. * @returns {Promise}
  1008. */
  1009. public handleGroupApplication(options: any): Promise<any> {
  1010. return this.handlePromiseCallback(async (resolve: any, reject: any) => {
  1011. try {
  1012. const imResponse = await this.TUICore.tim.handleGroupApplication(options);
  1013. resolve(imResponse);
  1014. } catch (error) {
  1015. reject(error);
  1016. }
  1017. });
  1018. }
  1019. /**
  1020. * 获取其他用户资料
  1021. * get user profile
  1022. *
  1023. * @param {Array<string>} userIDList 用户的账号列表/userID list
  1024. * @returns {Promise}
  1025. */
  1026. public async getUserProfile(userIDList: Array<string>) {
  1027. return this.handlePromiseCallback(async (resolve: any, reject: any) => {
  1028. try {
  1029. const imResponse = await this.TUICore.tim.getUserProfile({
  1030. userIDList,
  1031. });
  1032. resolve(imResponse);
  1033. } catch (error) {
  1034. reject(error);
  1035. }
  1036. });
  1037. }
  1038. /**
  1039. * 获取 SDK 缓存的好友列表
  1040. * Get the friend list cached by the SDK
  1041. *
  1042. * @param {Array<string>} userIDList 用户的账号列表
  1043. * @returns {Promise}
  1044. */
  1045. public async getFriendList(): Promise<void> {
  1046. return this.handlePromiseCallback(async (resolve: any, reject: any) => {
  1047. try {
  1048. const imResponse = await this.TUICore.tim.getFriendList();
  1049. resolve(imResponse);
  1050. } catch (error) {
  1051. reject(error);
  1052. }
  1053. });
  1054. }
  1055. /**
  1056. * 校验好友关系
  1057. * check friend
  1058. *
  1059. * @param {string} userID 用户账号
  1060. * @returns {Promise}
  1061. */
  1062. public async checkFriend(userID: string, type: string): Promise<void> {
  1063. return this.handlePromiseCallback(async (resolve: any, reject: any) => {
  1064. try {
  1065. const imResponse = await this.TUICore.tim.checkFriend({
  1066. userIDList: [userID],
  1067. type,
  1068. });
  1069. const isFriendShip = imResponse?.data?.successUserIDList[0]?.relation;
  1070. resolve(isFriendShip);
  1071. } catch (error) {
  1072. reject(error);
  1073. }
  1074. });
  1075. }
  1076. /**
  1077. * 获取群消息已读成员列表
  1078. * Get the list of memebers who have read the group message.
  1079. *
  1080. * @param {message} message 消息实例/message
  1081. * @param {string} cursor 分页拉取的游标,第一次拉取传''/Paging pull the cursor,first pull pass ''
  1082. * @param {number} count 分页拉取的个数/The number of page pulls
  1083. * @returns {Promise}
  1084. */
  1085. public async getGroupReadMemberList(message: any, cursor = "", count = 15): Promise<any> {
  1086. return this.handlePromiseCallback(async (resolve: any, reject: any) => {
  1087. try {
  1088. const imResponse = await this.TUICore.tim.getGroupMessageReadMemberList({
  1089. message,
  1090. filter: 0,
  1091. cursor,
  1092. count,
  1093. });
  1094. resolve(imResponse);
  1095. } catch (error) {
  1096. reject(error);
  1097. }
  1098. });
  1099. }
  1100. /**
  1101. * 获取群消息未读成员列表
  1102. * Get the list of memebers who have not read the group message.
  1103. *
  1104. * @param {message} message 消息实例/message
  1105. * @param {string} cursor 分页拉取的游标,第一次拉取传''/Paging pull the cursor,first pull pass ''
  1106. * @param {number} count 分页拉取的个数/The number of page pulls
  1107. * @returns {Promise}
  1108. */
  1109. public async getGroupUnreadMemberList(message: any, cursor = "", count = 15): Promise<any> {
  1110. return this.handlePromiseCallback(async (resolve: any, reject: any) => {
  1111. try {
  1112. const imResponse = await this.TUICore.tim.getGroupMessageReadMemberList({
  1113. message,
  1114. filter: 1,
  1115. cursor,
  1116. count,
  1117. });
  1118. resolve(imResponse);
  1119. } catch (error) {
  1120. reject(error);
  1121. }
  1122. });
  1123. }
  1124. /**
  1125. * 自己发送消息上屏显示
  1126. *
  1127. * @param {message} message 消息实例/message
  1128. */
  1129. public async handleMessageSentByMeToView(message: any) {
  1130. if (message?.conversationID === this?.store?.conversation?.conversationID) {
  1131. this.currentStore.messageList.push(message);
  1132. }
  1133. return;
  1134. }
  1135. /**
  1136. * /////////////////////////////////////////////////////////////////////////////////
  1137. * //
  1138. * // UI 数据绑定server数据同步
  1139. * // UI data binding server data synchronization
  1140. * //
  1141. * /////////////////////////////////////////////////////////////////////////////////
  1142. */
  1143. /**
  1144. * 赋值
  1145. * bind
  1146. *
  1147. * @param {Object} params 使用的数据/params
  1148. * @returns {Object} 数据/data
  1149. */
  1150. public bind(params: any) {
  1151. return (this.currentStore = params);
  1152. }
  1153. }