├── .gitignore
├── .prettierrc
├── LICENSE
├── README.md
├── package-lock.json
├── package.json
├── src
├── api
│ ├── conversation.ts
│ ├── friend.ts
│ ├── group.ts
│ ├── index.ts
│ ├── message.ts
│ └── user.ts
├── constant
│ ├── api.ts
│ └── callback.ts
├── index.ts
├── types
│ ├── entity.ts
│ ├── enum.ts
│ ├── params.ts
│ └── upload.ts
└── utils
│ ├── emitter.ts
│ ├── textCoder.ts
│ ├── upload.ts
│ ├── uuid.ts
│ └── webSocketManager.ts
└── tsconfig.json
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | pnpm-debug.log*
8 | lerna-debug.log*
9 |
10 | node_modules
11 | dist
12 | dist-ssr
13 | lib
14 | demo
15 | *.local
16 |
17 | # Editor directories and files
18 | .vscode/*
19 | !.vscode/extensions.json
20 | .idea
21 | .DS_Store
22 | *.suo
23 | *.ntvs*
24 | *.njsproj
25 | *.sln
26 | *.sw?
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "singleQuote": true,
3 | "trailingComma": "es5",
4 | "arrowParens": "avoid",
5 | "printWidth": 80
6 | }
7 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | GNU AFFERO GENERAL PUBLIC LICENSE
2 | Version 3, 19 November 2007
3 |
4 | Copyright (C) 2007 Free Software Foundation, Inc.
5 | Everyone is permitted to copy and distribute verbatim copies
6 | of this license document, but changing it is not allowed.
7 |
8 | Preamble
9 |
10 | The GNU Affero General Public License is a free, copyleft license for
11 | software and other kinds of works, specifically designed to ensure
12 | cooperation with the community in the case of network server software.
13 |
14 | The licenses for most software and other practical works are designed
15 | to take away your freedom to share and change the works. By contrast,
16 | our General Public Licenses are intended to guarantee your freedom to
17 | share and change all versions of a program--to make sure it remains free
18 | software for all its users.
19 |
20 | When we speak of free software, we are referring to freedom, not
21 | price. Our General Public Licenses are designed to make sure that you
22 | have the freedom to distribute copies of free software (and charge for
23 | them if you wish), that you receive source code or can get it if you
24 | want it, that you can change the software or use pieces of it in new
25 | free programs, and that you know you can do these things.
26 |
27 | Developers that use our General Public Licenses protect your rights
28 | with two steps: (1) assert copyright on the software, and (2) offer
29 | you this License which gives you legal permission to copy, distribute
30 | and/or modify the software.
31 |
32 | A secondary benefit of defending all users' freedom is that
33 | improvements made in alternate versions of the program, if they
34 | receive widespread use, become available for other developers to
35 | incorporate. Many developers of free software are heartened and
36 | encouraged by the resulting cooperation. However, in the case of
37 | software used on network servers, this result may fail to come about.
38 | The GNU General Public License permits making a modified version and
39 | letting the public access it on a server without ever releasing its
40 | source code to the public.
41 |
42 | The GNU Affero General Public License is designed specifically to
43 | ensure that, in such cases, the modified source code becomes available
44 | to the community. It requires the operator of a network server to
45 | provide the source code of the modified version running there to the
46 | users of that server. Therefore, public use of a modified version, on
47 | a publicly accessible server, gives the public access to the source
48 | code of the modified version.
49 |
50 | An older license, called the Affero General Public License and
51 | published by Affero, was designed to accomplish similar goals. This is
52 | a different license, not a version of the Affero GPL, but Affero has
53 | released a new version of the Affero GPL which permits relicensing under
54 | this license.
55 |
56 | The precise terms and conditions for copying, distribution and
57 | modification follow.
58 |
59 | TERMS AND CONDITIONS
60 |
61 | 0. Definitions.
62 |
63 | "This License" refers to version 3 of the GNU Affero General Public License.
64 |
65 | "Copyright" also means copyright-like laws that apply to other kinds of
66 | works, such as semiconductor masks.
67 |
68 | "The Program" refers to any copyrightable work licensed under this
69 | License. Each licensee is addressed as "you". "Licensees" and
70 | "recipients" may be individuals or organizations.
71 |
72 | To "modify" a work means to copy from or adapt all or part of the work
73 | in a fashion requiring copyright permission, other than the making of an
74 | exact copy. The resulting work is called a "modified version" of the
75 | earlier work or a work "based on" the earlier work.
76 |
77 | A "covered work" means either the unmodified Program or a work based
78 | on the Program.
79 |
80 | To "propagate" a work means to do anything with it that, without
81 | permission, would make you directly or secondarily liable for
82 | infringement under applicable copyright law, except executing it on a
83 | computer or modifying a private copy. Propagation includes copying,
84 | distribution (with or without modification), making available to the
85 | public, and in some countries other activities as well.
86 |
87 | To "convey" a work means any kind of propagation that enables other
88 | parties to make or receive copies. Mere interaction with a user through
89 | a computer network, with no transfer of a copy, is not conveying.
90 |
91 | An interactive user interface displays "Appropriate Legal Notices"
92 | to the extent that it includes a convenient and prominently visible
93 | feature that (1) displays an appropriate copyright notice, and (2)
94 | tells the user that there is no warranty for the work (except to the
95 | extent that warranties are provided), that licensees may convey the
96 | work under this License, and how to view a copy of this License. If
97 | the interface presents a list of user commands or options, such as a
98 | menu, a prominent item in the list meets this criterion.
99 |
100 | 1. Source Code.
101 |
102 | The "source code" for a work means the preferred form of the work
103 | for making modifications to it. "Object code" means any non-source
104 | form of a work.
105 |
106 | A "Standard Interface" means an interface that either is an official
107 | standard defined by a recognized standards body, or, in the case of
108 | interfaces specified for a particular programming language, one that
109 | is widely used among developers working in that language.
110 |
111 | The "System Libraries" of an executable work include anything, other
112 | than the work as a whole, that (a) is included in the normal form of
113 | packaging a Major Component, but which is not part of that Major
114 | Component, and (b) serves only to enable use of the work with that
115 | Major Component, or to implement a Standard Interface for which an
116 | implementation is available to the public in source code form. A
117 | "Major Component", in this context, means a major essential component
118 | (kernel, window system, and so on) of the specific operating system
119 | (if any) on which the executable work runs, or a compiler used to
120 | produce the work, or an object code interpreter used to run it.
121 |
122 | The "Corresponding Source" for a work in object code form means all
123 | the source code needed to generate, install, and (for an executable
124 | work) run the object code and to modify the work, including scripts to
125 | control those activities. However, it does not include the work's
126 | System Libraries, or general-purpose tools or generally available free
127 | programs which are used unmodified in performing those activities but
128 | which are not part of the work. For example, Corresponding Source
129 | includes interface definition files associated with source files for
130 | the work, and the source code for shared libraries and dynamically
131 | linked subprograms that the work is specifically designed to require,
132 | such as by intimate data communication or control flow between those
133 | subprograms and other parts of the work.
134 |
135 | The Corresponding Source need not include anything that users
136 | can regenerate automatically from other parts of the Corresponding
137 | Source.
138 |
139 | The Corresponding Source for a work in source code form is that
140 | same work.
141 |
142 | 2. Basic Permissions.
143 |
144 | All rights granted under this License are granted for the term of
145 | copyright on the Program, and are irrevocable provided the stated
146 | conditions are met. This License explicitly affirms your unlimited
147 | permission to run the unmodified Program. The output from running a
148 | covered work is covered by this License only if the output, given its
149 | content, constitutes a covered work. This License acknowledges your
150 | rights of fair use or other equivalent, as provided by copyright law.
151 |
152 | You may make, run and propagate covered works that you do not
153 | convey, without conditions so long as your license otherwise remains
154 | in force. You may convey covered works to others for the sole purpose
155 | of having them make modifications exclusively for you, or provide you
156 | with facilities for running those works, provided that you comply with
157 | the terms of this License in conveying all material for which you do
158 | not control copyright. Those thus making or running the covered works
159 | for you must do so exclusively on your behalf, under your direction
160 | and control, on terms that prohibit them from making any copies of
161 | your copyrighted material outside their relationship with you.
162 |
163 | Conveying under any other circumstances is permitted solely under
164 | the conditions stated below. Sublicensing is not allowed; section 10
165 | makes it unnecessary.
166 |
167 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
168 |
169 | No covered work shall be deemed part of an effective technological
170 | measure under any applicable law fulfilling obligations under article
171 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or
172 | similar laws prohibiting or restricting circumvention of such
173 | measures.
174 |
175 | When you convey a covered work, you waive any legal power to forbid
176 | circumvention of technological measures to the extent such circumvention
177 | is effected by exercising rights under this License with respect to
178 | the covered work, and you disclaim any intention to limit operation or
179 | modification of the work as a means of enforcing, against the work's
180 | users, your or third parties' legal rights to forbid circumvention of
181 | technological measures.
182 |
183 | 4. Conveying Verbatim Copies.
184 |
185 | You may convey verbatim copies of the Program's source code as you
186 | receive it, in any medium, provided that you conspicuously and
187 | appropriately publish on each copy an appropriate copyright notice;
188 | keep intact all notices stating that this License and any
189 | non-permissive terms added in accord with section 7 apply to the code;
190 | keep intact all notices of the absence of any warranty; and give all
191 | recipients a copy of this License along with the Program.
192 |
193 | You may charge any price or no price for each copy that you convey,
194 | and you may offer support or warranty protection for a fee.
195 |
196 | 5. Conveying Modified Source Versions.
197 |
198 | You may convey a work based on the Program, or the modifications to
199 | produce it from the Program, in the form of source code under the
200 | terms of section 4, provided that you also meet all of these conditions:
201 |
202 | a) The work must carry prominent notices stating that you modified
203 | it, and giving a relevant date.
204 |
205 | b) The work must carry prominent notices stating that it is
206 | released under this License and any conditions added under section
207 | 7. This requirement modifies the requirement in section 4 to
208 | "keep intact all notices".
209 |
210 | c) You must license the entire work, as a whole, under this
211 | License to anyone who comes into possession of a copy. This
212 | License will therefore apply, along with any applicable section 7
213 | additional terms, to the whole of the work, and all its parts,
214 | regardless of how they are packaged. This License gives no
215 | permission to license the work in any other way, but it does not
216 | invalidate such permission if you have separately received it.
217 |
218 | d) If the work has interactive user interfaces, each must display
219 | Appropriate Legal Notices; however, if the Program has interactive
220 | interfaces that do not display Appropriate Legal Notices, your
221 | work need not make them do so.
222 |
223 | A compilation of a covered work with other separate and independent
224 | works, which are not by their nature extensions of the covered work,
225 | and which are not combined with it such as to form a larger program,
226 | in or on a volume of a storage or distribution medium, is called an
227 | "aggregate" if the compilation and its resulting copyright are not
228 | used to limit the access or legal rights of the compilation's users
229 | beyond what the individual works permit. Inclusion of a covered work
230 | in an aggregate does not cause this License to apply to the other
231 | parts of the aggregate.
232 |
233 | 6. Conveying Non-Source Forms.
234 |
235 | You may convey a covered work in object code form under the terms
236 | of sections 4 and 5, provided that you also convey the
237 | machine-readable Corresponding Source under the terms of this License,
238 | in one of these ways:
239 |
240 | a) Convey the object code in, or embodied in, a physical product
241 | (including a physical distribution medium), accompanied by the
242 | Corresponding Source fixed on a durable physical medium
243 | customarily used for software interchange.
244 |
245 | b) Convey the object code in, or embodied in, a physical product
246 | (including a physical distribution medium), accompanied by a
247 | written offer, valid for at least three years and valid for as
248 | long as you offer spare parts or customer support for that product
249 | model, to give anyone who possesses the object code either (1) a
250 | copy of the Corresponding Source for all the software in the
251 | product that is covered by this License, on a durable physical
252 | medium customarily used for software interchange, for a price no
253 | more than your reasonable cost of physically performing this
254 | conveying of source, or (2) access to copy the
255 | Corresponding Source from a network server at no charge.
256 |
257 | c) Convey individual copies of the object code with a copy of the
258 | written offer to provide the Corresponding Source. This
259 | alternative is allowed only occasionally and noncommercially, and
260 | only if you received the object code with such an offer, in accord
261 | with subsection 6b.
262 |
263 | d) Convey the object code by offering access from a designated
264 | place (gratis or for a charge), and offer equivalent access to the
265 | Corresponding Source in the same way through the same place at no
266 | further charge. You need not require recipients to copy the
267 | Corresponding Source along with the object code. If the place to
268 | copy the object code is a network server, the Corresponding Source
269 | may be on a different server (operated by you or a third party)
270 | that supports equivalent copying facilities, provided you maintain
271 | clear directions next to the object code saying where to find the
272 | Corresponding Source. Regardless of what server hosts the
273 | Corresponding Source, you remain obligated to ensure that it is
274 | available for as long as needed to satisfy these requirements.
275 |
276 | e) Convey the object code using peer-to-peer transmission, provided
277 | you inform other peers where the object code and Corresponding
278 | Source of the work are being offered to the general public at no
279 | charge under subsection 6d.
280 |
281 | A separable portion of the object code, whose source code is excluded
282 | from the Corresponding Source as a System Library, need not be
283 | included in conveying the object code work.
284 |
285 | A "User Product" is either (1) a "consumer product", which means any
286 | tangible personal property which is normally used for personal, family,
287 | or household purposes, or (2) anything designed or sold for incorporation
288 | into a dwelling. In determining whether a product is a consumer product,
289 | doubtful cases shall be resolved in favor of coverage. For a particular
290 | product received by a particular user, "normally used" refers to a
291 | typical or common use of that class of product, regardless of the status
292 | of the particular user or of the way in which the particular user
293 | actually uses, or expects or is expected to use, the product. A product
294 | is a consumer product regardless of whether the product has substantial
295 | commercial, industrial or non-consumer uses, unless such uses represent
296 | the only significant mode of use of the product.
297 |
298 | "Installation Information" for a User Product means any methods,
299 | procedures, authorization keys, or other information required to install
300 | and execute modified versions of a covered work in that User Product from
301 | a modified version of its Corresponding Source. The information must
302 | suffice to ensure that the continued functioning of the modified object
303 | code is in no case prevented or interfered with solely because
304 | modification has been made.
305 |
306 | If you convey an object code work under this section in, or with, or
307 | specifically for use in, a User Product, and the conveying occurs as
308 | part of a transaction in which the right of possession and use of the
309 | User Product is transferred to the recipient in perpetuity or for a
310 | fixed term (regardless of how the transaction is characterized), the
311 | Corresponding Source conveyed under this section must be accompanied
312 | by the Installation Information. But this requirement does not apply
313 | if neither you nor any third party retains the ability to install
314 | modified object code on the User Product (for example, the work has
315 | been installed in ROM).
316 |
317 | The requirement to provide Installation Information does not include a
318 | requirement to continue to provide support service, warranty, or updates
319 | for a work that has been modified or installed by the recipient, or for
320 | the User Product in which it has been modified or installed. Access to a
321 | network may be denied when the modification itself materially and
322 | adversely affects the operation of the network or violates the rules and
323 | protocols for communication across the network.
324 |
325 | Corresponding Source conveyed, and Installation Information provided,
326 | in accord with this section must be in a format that is publicly
327 | documented (and with an implementation available to the public in
328 | source code form), and must require no special password or key for
329 | unpacking, reading or copying.
330 |
331 | 7. Additional Terms.
332 |
333 | "Additional permissions" are terms that supplement the terms of this
334 | License by making exceptions from one or more of its conditions.
335 | Additional permissions that are applicable to the entire Program shall
336 | be treated as though they were included in this License, to the extent
337 | that they are valid under applicable law. If additional permissions
338 | apply only to part of the Program, that part may be used separately
339 | under those permissions, but the entire Program remains governed by
340 | this License without regard to the additional permissions.
341 |
342 | When you convey a copy of a covered work, you may at your option
343 | remove any additional permissions from that copy, or from any part of
344 | it. (Additional permissions may be written to require their own
345 | removal in certain cases when you modify the work.) You may place
346 | additional permissions on material, added by you to a covered work,
347 | for which you have or can give appropriate copyright permission.
348 |
349 | Notwithstanding any other provision of this License, for material you
350 | add to a covered work, you may (if authorized by the copyright holders of
351 | that material) supplement the terms of this License with terms:
352 |
353 | a) Disclaiming warranty or limiting liability differently from the
354 | terms of sections 15 and 16 of this License; or
355 |
356 | b) Requiring preservation of specified reasonable legal notices or
357 | author attributions in that material or in the Appropriate Legal
358 | Notices displayed by works containing it; or
359 |
360 | c) Prohibiting misrepresentation of the origin of that material, or
361 | requiring that modified versions of such material be marked in
362 | reasonable ways as different from the original version; or
363 |
364 | d) Limiting the use for publicity purposes of names of licensors or
365 | authors of the material; or
366 |
367 | e) Declining to grant rights under trademark law for use of some
368 | trade names, trademarks, or service marks; or
369 |
370 | f) Requiring indemnification of licensors and authors of that
371 | material by anyone who conveys the material (or modified versions of
372 | it) with contractual assumptions of liability to the recipient, for
373 | any liability that these contractual assumptions directly impose on
374 | those licensors and authors.
375 |
376 | All other non-permissive additional terms are considered "further
377 | restrictions" within the meaning of section 10. If the Program as you
378 | received it, or any part of it, contains a notice stating that it is
379 | governed by this License along with a term that is a further
380 | restriction, you may remove that term. If a license document contains
381 | a further restriction but permits relicensing or conveying under this
382 | License, you may add to a covered work material governed by the terms
383 | of that license document, provided that the further restriction does
384 | not survive such relicensing or conveying.
385 |
386 | If you add terms to a covered work in accord with this section, you
387 | must place, in the relevant source files, a statement of the
388 | additional terms that apply to those files, or a notice indicating
389 | where to find the applicable terms.
390 |
391 | Additional terms, permissive or non-permissive, may be stated in the
392 | form of a separately written license, or stated as exceptions;
393 | the above requirements apply either way.
394 |
395 | 8. Termination.
396 |
397 | You may not propagate or modify a covered work except as expressly
398 | provided under this License. Any attempt otherwise to propagate or
399 | modify it is void, and will automatically terminate your rights under
400 | this License (including any patent licenses granted under the third
401 | paragraph of section 11).
402 |
403 | However, if you cease all violation of this License, then your
404 | license from a particular copyright holder is reinstated (a)
405 | provisionally, unless and until the copyright holder explicitly and
406 | finally terminates your license, and (b) permanently, if the copyright
407 | holder fails to notify you of the violation by some reasonable means
408 | prior to 60 days after the cessation.
409 |
410 | Moreover, your license from a particular copyright holder is
411 | reinstated permanently if the copyright holder notifies you of the
412 | violation by some reasonable means, this is the first time you have
413 | received notice of violation of this License (for any work) from that
414 | copyright holder, and you cure the violation prior to 30 days after
415 | your receipt of the notice.
416 |
417 | Termination of your rights under this section does not terminate the
418 | licenses of parties who have received copies or rights from you under
419 | this License. If your rights have been terminated and not permanently
420 | reinstated, you do not qualify to receive new licenses for the same
421 | material under section 10.
422 |
423 | 9. Acceptance Not Required for Having Copies.
424 |
425 | You are not required to accept this License in order to receive or
426 | run a copy of the Program. Ancillary propagation of a covered work
427 | occurring solely as a consequence of using peer-to-peer transmission
428 | to receive a copy likewise does not require acceptance. However,
429 | nothing other than this License grants you permission to propagate or
430 | modify any covered work. These actions infringe copyright if you do
431 | not accept this License. Therefore, by modifying or propagating a
432 | covered work, you indicate your acceptance of this License to do so.
433 |
434 | 10. Automatic Licensing of Downstream Recipients.
435 |
436 | Each time you convey a covered work, the recipient automatically
437 | receives a license from the original licensors, to run, modify and
438 | propagate that work, subject to this License. You are not responsible
439 | for enforcing compliance by third parties with this License.
440 |
441 | An "entity transaction" is a transaction transferring control of an
442 | organization, or substantially all assets of one, or subdividing an
443 | organization, or merging organizations. If propagation of a covered
444 | work results from an entity transaction, each party to that
445 | transaction who receives a copy of the work also receives whatever
446 | licenses to the work the party's predecessor in interest had or could
447 | give under the previous paragraph, plus a right to possession of the
448 | Corresponding Source of the work from the predecessor in interest, if
449 | the predecessor has it or can get it with reasonable efforts.
450 |
451 | You may not impose any further restrictions on the exercise of the
452 | rights granted or affirmed under this License. For example, you may
453 | not impose a license fee, royalty, or other charge for exercise of
454 | rights granted under this License, and you may not initiate litigation
455 | (including a cross-claim or counterclaim in a lawsuit) alleging that
456 | any patent claim is infringed by making, using, selling, offering for
457 | sale, or importing the Program or any portion of it.
458 |
459 | 11. Patents.
460 |
461 | A "contributor" is a copyright holder who authorizes use under this
462 | License of the Program or a work on which the Program is based. The
463 | work thus licensed is called the contributor's "contributor version".
464 |
465 | A contributor's "essential patent claims" are all patent claims
466 | owned or controlled by the contributor, whether already acquired or
467 | hereafter acquired, that would be infringed by some manner, permitted
468 | by this License, of making, using, or selling its contributor version,
469 | but do not include claims that would be infringed only as a
470 | consequence of further modification of the contributor version. For
471 | purposes of this definition, "control" includes the right to grant
472 | patent sublicenses in a manner consistent with the requirements of
473 | this License.
474 |
475 | Each contributor grants you a non-exclusive, worldwide, royalty-free
476 | patent license under the contributor's essential patent claims, to
477 | make, use, sell, offer for sale, import and otherwise run, modify and
478 | propagate the contents of its contributor version.
479 |
480 | In the following three paragraphs, a "patent license" is any express
481 | agreement or commitment, however denominated, not to enforce a patent
482 | (such as an express permission to practice a patent or covenant not to
483 | sue for patent infringement). To "grant" such a patent license to a
484 | party means to make such an agreement or commitment not to enforce a
485 | patent against the party.
486 |
487 | If you convey a covered work, knowingly relying on a patent license,
488 | and the Corresponding Source of the work is not available for anyone
489 | to copy, free of charge and under the terms of this License, through a
490 | publicly available network server or other readily accessible means,
491 | then you must either (1) cause the Corresponding Source to be so
492 | available, or (2) arrange to deprive yourself of the benefit of the
493 | patent license for this particular work, or (3) arrange, in a manner
494 | consistent with the requirements of this License, to extend the patent
495 | license to downstream recipients. "Knowingly relying" means you have
496 | actual knowledge that, but for the patent license, your conveying the
497 | covered work in a country, or your recipient's use of the covered work
498 | in a country, would infringe one or more identifiable patents in that
499 | country that you have reason to believe are valid.
500 |
501 | If, pursuant to or in connection with a single transaction or
502 | arrangement, you convey, or propagate by procuring conveyance of, a
503 | covered work, and grant a patent license to some of the parties
504 | receiving the covered work authorizing them to use, propagate, modify
505 | or convey a specific copy of the covered work, then the patent license
506 | you grant is automatically extended to all recipients of the covered
507 | work and works based on it.
508 |
509 | A patent license is "discriminatory" if it does not include within
510 | the scope of its coverage, prohibits the exercise of, or is
511 | conditioned on the non-exercise of one or more of the rights that are
512 | specifically granted under this License. You may not convey a covered
513 | work if you are a party to an arrangement with a third party that is
514 | in the business of distributing software, under which you make payment
515 | to the third party based on the extent of your activity of conveying
516 | the work, and under which the third party grants, to any of the
517 | parties who would receive the covered work from you, a discriminatory
518 | patent license (a) in connection with copies of the covered work
519 | conveyed by you (or copies made from those copies), or (b) primarily
520 | for and in connection with specific products or compilations that
521 | contain the covered work, unless you entered into that arrangement,
522 | or that patent license was granted, prior to 28 March 2007.
523 |
524 | Nothing in this License shall be construed as excluding or limiting
525 | any implied license or other defenses to infringement that may
526 | otherwise be available to you under applicable patent law.
527 |
528 | 12. No Surrender of Others' Freedom.
529 |
530 | If conditions are imposed on you (whether by court order, agreement or
531 | otherwise) that contradict the conditions of this License, they do not
532 | excuse you from the conditions of this License. If you cannot convey a
533 | covered work so as to satisfy simultaneously your obligations under this
534 | License and any other pertinent obligations, then as a consequence you may
535 | not convey it at all. For example, if you agree to terms that obligate you
536 | to collect a royalty for further conveying from those to whom you convey
537 | the Program, the only way you could satisfy both those terms and this
538 | License would be to refrain entirely from conveying the Program.
539 |
540 | 13. Remote Network Interaction; Use with the GNU General Public License.
541 |
542 | Notwithstanding any other provision of this License, if you modify the
543 | Program, your modified version must prominently offer all users
544 | interacting with it remotely through a computer network (if your version
545 | supports such interaction) an opportunity to receive the Corresponding
546 | Source of your version by providing access to the Corresponding Source
547 | from a network server at no charge, through some standard or customary
548 | means of facilitating copying of software. This Corresponding Source
549 | shall include the Corresponding Source for any work covered by version 3
550 | of the GNU General Public License that is incorporated pursuant to the
551 | following paragraph.
552 |
553 | Notwithstanding any other provision of this License, you have
554 | permission to link or combine any covered work with a work licensed
555 | under version 3 of the GNU General Public License into a single
556 | combined work, and to convey the resulting work. The terms of this
557 | License will continue to apply to the part which is the covered work,
558 | but the work with which it is combined will remain governed by version
559 | 3 of the GNU General Public License.
560 |
561 | 14. Revised Versions of this License.
562 |
563 | The Free Software Foundation may publish revised and/or new versions of
564 | the GNU Affero General Public License from time to time. Such new versions
565 | will be similar in spirit to the present version, but may differ in detail to
566 | address new problems or concerns.
567 |
568 | Each version is given a distinguishing version number. If the
569 | Program specifies that a certain numbered version of the GNU Affero General
570 | Public License "or any later version" applies to it, you have the
571 | option of following the terms and conditions either of that numbered
572 | version or of any later version published by the Free Software
573 | Foundation. If the Program does not specify a version number of the
574 | GNU Affero General Public License, you may choose any version ever published
575 | by the Free Software Foundation.
576 |
577 | If the Program specifies that a proxy can decide which future
578 | versions of the GNU Affero General Public License can be used, that proxy's
579 | public statement of acceptance of a version permanently authorizes you
580 | to choose that version for the Program.
581 |
582 | Later license versions may give you additional or different
583 | permissions. However, no additional obligations are imposed on any
584 | author or copyright holder as a result of your choosing to follow a
585 | later version.
586 |
587 | 15. Disclaimer of Warranty.
588 |
589 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
590 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
591 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
592 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
593 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
594 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
595 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
596 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
597 |
598 | 16. Limitation of Liability.
599 |
600 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
601 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
602 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
603 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
604 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
605 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
606 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
607 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
608 | SUCH DAMAGES.
609 |
610 | 17. Interpretation of Sections 15 and 16.
611 |
612 | If the disclaimer of warranty and limitation of liability provided
613 | above cannot be given local legal effect according to their terms,
614 | reviewing courts shall apply local law that most closely approximates
615 | an absolute waiver of all civil liability in connection with the
616 | Program, unless a warranty or assumption of liability accompanies a
617 | copy of the Program in return for a fee.
618 |
619 | END OF TERMS AND CONDITIONS
620 |
621 | How to Apply These Terms to Your New Programs
622 |
623 | If you develop a new program, and you want it to be of the greatest
624 | possible use to the public, the best way to achieve this is to make it
625 | free software which everyone can redistribute and change under these terms.
626 |
627 | To do so, attach the following notices to the program. It is safest
628 | to attach them to the start of each source file to most effectively
629 | state the exclusion of warranty; and each file should have at least
630 | the "copyright" line and a pointer to where the full notice is found.
631 |
632 |
633 | Copyright (C)
634 |
635 | This program is free software: you can redistribute it and/or modify
636 | it under the terms of the GNU Affero General Public License as published
637 | by the Free Software Foundation, either version 3 of the License, or
638 | (at your option) any later version.
639 |
640 | This program is distributed in the hope that it will be useful,
641 | but WITHOUT ANY WARRANTY; without even the implied warranty of
642 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
643 | GNU Affero General Public License for more details.
644 |
645 | You should have received a copy of the GNU Affero General Public License
646 | along with this program. If not, see .
647 |
648 | Also add information on how to contact you by electronic and paper mail.
649 |
650 | If your software can interact with users remotely through a computer
651 | network, you should also make sure that it provides a way for users to
652 | get its source. For example, if your program is a web application, its
653 | interface could display a "Source" link that leads users to an archive
654 | of the code. There are many ways you could offer source, and different
655 | solutions will be better for different programs; see section 13 for the
656 | specific requirements.
657 |
658 | You should also get your employer (if you work as a programmer) or school,
659 | if any, to sign a "copyright disclaimer" for the program, if necessary.
660 | For more information on this, and how to apply and follow the GNU AGPL, see
661 | .
662 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ⚠️ Project No Longer Maintained
2 | This project is no longer maintained and may contain unpatched vulnerabilities or compatibility issues with modern environments. Feel free to fork and modify the project if needed.
3 | If you need to use jssdk in the new version (v3.8.2+), you can introduce [@openim/client-sdk](https://www.npmjs.com/package/@openim/client-sdk) to directly communicate with IM Server without the need to deploy this project.
4 |
5 | # MiniProgram SDK for OpenIM 👨💻💬
6 |
7 | Use this SDK to add instant messaging capabilities to your application. By connecting to a self-hosted [OpenIM](https://www.openim.io/) server, you can quickly integrate instant messaging capabilities into your app with just a few lines of code.
8 |
9 | `open-im-sdk` is a pure javascript library. It doesn't store any information inside browser, instead it connects to [oimws](https://github.com/openim-sigs/oimws) the proxy layer. This proxy layer is [OpenIM SDK Core](https://github.com/openimsdk/openim-sdk-core.git)'s websocket proxy(listening on port `10003` by deafult). open-im-sdk and open-im-sdk-wasm's interfaces are completely the same. Without modifying any code, your website can run in mini-app.
10 |
11 | ## Documentation 📚
12 |
13 | Visit [https://docs.openim.io/](https://docs.openim.io/) for detailed documentation and guides.
14 |
15 | For the SDK reference, see [https://docs.openim.io/sdks/quickstart/browser](https://docs.openim.io/sdks/quickstart/browser).
16 |
17 | ## Installation 💻
18 |
19 | ### Adding Dependencies
20 |
21 | ```shell
22 | npm install open-im-sdk --save
23 | ```
24 |
25 | ## Usage 🚀
26 |
27 | The following examples demonstrate how to use the SDK. TypeScript is used, providing complete type hints.
28 |
29 | ### Importing the SDK
30 |
31 | ```typescript
32 | import { OpenIMSDK } from 'open-im-sdk';
33 |
34 | const OpenIM = new OpenIMSDK();
35 | ```
36 |
37 | ### Logging In and Listening for Connection Status
38 |
39 | > Note: You need to [deploy](https://github.com/openimsdk/open-im-server#rocket-quick-start) OpenIM Server first, the default port of OpenIM Server is 10001, 10002, 10003.
40 |
41 | ```typescript
42 | import { CbEvents } from 'open-im-sdk';
43 | import type { WsResponse } from 'open-im-sdk';
44 |
45 | OpenIM.on(CbEvents.OnConnecting, handleConnecting);
46 | OpenIM.on(CbEvents.OnConnectFailed, handleConnectFailed);
47 | OpenIM.on(CbEvents.OnConnectSuccess, handleConnectSuccess);
48 |
49 | OpenIM.login({
50 | userID: 'IM user ID',
51 | token: 'IM user token',
52 | platformID: 5,
53 | wsAddr: 'ws://your-server-ip:10003',
54 | apiAddr: 'http://your-server-ip:10002',
55 | });
56 |
57 | function handleConnecting() {
58 | // Connecting...
59 | }
60 |
61 | function handleConnectFailed({ errCode, errMsg }: WsResponse) {
62 | // Connection failed ❌
63 | console.log(errCode, errMsg);
64 | }
65 |
66 | function handleConnectSuccess() {
67 | // Connection successful ✅
68 | }
69 | ```
70 |
71 | To log into the IM server, you need to create an account and obtain a user ID and token. Refer to the [access token documentation](https://docs.openim.io/restapi/userManagement/userRegister) for details.
72 |
73 | ### Receiving and Sending Messages 💬
74 |
75 | OpenIM makes it easy to send and receive messages. By default, there is no restriction on having a friend relationship to send messages (although you can configure other policies on the server). If you know the user ID of the recipient, you can conveniently send a message to them.
76 |
77 | ```typescript
78 | import { CbEvents } from 'open-im-sdk';
79 | import type { WsResponse, MessageItem } from 'open-im-sdk';
80 |
81 | // Listenfor new messages 📩
82 | OpenIM.on(CbEvents.OnRecvNewMessages, handleNewMessages);
83 |
84 | const message = (await OpenIM.createTextMessage('hello openim')).data;
85 |
86 | OpenIM.sendMessage({
87 | recvID: 'recipient user ID',
88 | groupID: '',
89 | message,
90 | })
91 | .then(() => {
92 | // Message sent successfully ✉️
93 | })
94 | .catch(err => {
95 | // Failed to send message ❌
96 | console.log(err);
97 | });
98 |
99 | function handleNewMessages({ data }: WsResponse) {
100 | // New message list 📨
101 | console.log(data);
102 | }
103 | ```
104 |
105 | ## Examples 🌟
106 |
107 | You can find a demo web app that uses the SDK in the [openim-pc-web-demo](https://github.com/openimsdk/open-im-pc-web-demo) repository.
108 |
109 | ## Community :busts_in_silhouette:
110 |
111 | - 📚 [OpenIM Community](https://github.com/OpenIMSDK/community)
112 | - 💕 [OpenIM Interest Group](https://github.com/Openim-sigs)
113 | - 🚀 [Join our Slack community](https://join.slack.com/t/openimsdk/shared_invite/zt-2ijy1ys1f-O0aEDCr7ExRZ7mwsHAVg9A)
114 | - :eyes: [Join our wechat (微信群)](https://openim-1253691595.cos.ap-nanjing.myqcloud.com/WechatIMG20.jpeg)
115 |
116 | ## Community Meetings :calendar:
117 |
118 | We want anyone to get involved in our community and contributing code, we offer gifts and rewards, and we welcome you to join us every Thursday night.
119 |
120 | Our conference is in the [OpenIM Slack](https://join.slack.com/t/openimsdk/shared_invite/zt-2ijy1ys1f-O0aEDCr7ExRZ7mwsHAVg9A) 🎯, then you can search the Open-IM-Server pipeline to join
121 |
122 | We take notes of each [biweekly meeting](https://github.com/orgs/OpenIMSDK/discussions/categories/meeting) in [GitHub discussions](https://github.com/openimsdk/open-im-server/discussions/categories/meeting), Our historical meeting notes, as well as replays of the meetings are available at [Google Docs :bookmark_tabs:](https://docs.google.com/document/d/1nx8MDpuG74NASx081JcCpxPgDITNTpIIos0DS6Vr9GU/edit?usp=sharing).
123 |
124 | ## Who are using OpenIM :eyes:
125 |
126 | Check out our [user case studies](https://github.com/OpenIMSDK/community/blob/main/ADOPTERS.md) page for a list of the project users. Don't hesitate to leave a [📝comment](https://github.com/openimsdk/open-im-server/issues/379) and share your use case.
127 |
128 | ## License :page_facing_up:
129 |
130 | OpenIM is licensed under the Apache 2.0 license. See [LICENSE](https://github.com/openimsdk/open-im-server/tree/main/LICENSE) for the full license text.
131 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "open-im-sdk",
3 | "version": "v3.8.0-rc.2",
4 | "description": "OpenIM SDK for MiniProgram",
5 | "source": "src/index.ts",
6 | "main": "lib/index.js",
7 | "unpkg": "lib/index.umd.js",
8 | "module": "lib/index.es.js",
9 | "jsdelivr": "lib/index.umd.js",
10 | "types": "lib/index.d.ts",
11 | "exports": {
12 | ".": {
13 | "import": "./lib/index.es.js",
14 | "require": "./lib/index.js",
15 | "types": "./lib/index.d.ts"
16 | }
17 | },
18 | "scripts": {
19 | "build": "rimraf lib && microbundle --no-sourcemap && tsc-alias",
20 | "test": "jest",
21 | "lint": "eslint . --ext .ts",
22 | "format": "prettier --write .",
23 | "release": "semantic-release"
24 | },
25 | "files": [
26 | "lib/**/*",
27 | "src/**/*"
28 | ],
29 | "repository": {
30 | "type": "git",
31 | "url": "git+https://github.com/openimsdk/open-im-sdk.git"
32 | },
33 | "keywords": [
34 | "OpenIM",
35 | "IM",
36 | "chat",
37 | "miniprogram"
38 | ],
39 | "author": "blooming ",
40 | "license": "Apache-2.0",
41 | "bugs": {
42 | "url": "https://github.com/openimsdk/open-im-sdk/issues"
43 | },
44 | "homepage": "https://github.com/openimsdk/open-im-sdk#readme",
45 | "devDependencies": {
46 | "@types/spark-md5": "^3.0.3",
47 | "@typescript-eslint/parser": "^6.9.1",
48 | "microbundle": "^0.15.1",
49 | "prettier": "^3.0.3",
50 | "semantic-release": "^22.0.6",
51 | "spark-md5": "^3.0.2",
52 | "tsc-alias": "^1.8.8",
53 | "typescript": "^5.2.2"
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/src/api/conversation.ts:
--------------------------------------------------------------------------------
1 | import { RequestApi } from '@/constant/api';
2 | import OpenIMSDK from '.';
3 | import type {
4 | GetOneConversationParams,
5 | PinConversationParams,
6 | SetBurnDurationParams,
7 | SetConversationDraftParams,
8 | SetConversationPrivateParams,
9 | SetConversationRecvOptParams,
10 | SplitConversationParams,
11 | } from '@/types/params';
12 | import type { ConversationItem, WsResponse } from '@/types/entity';
13 |
14 | export function setupConversation(openIMSDK: OpenIMSDK) {
15 | return {
16 | getAllConversationList: openIMSDK.createRequestFunctionWithoutParams<
17 | ConversationItem[]
18 | >(RequestApi.GetAllConversationList),
19 | getConversationListSplit: openIMSDK.createRequestFunction<
20 | SplitConversationParams,
21 | ConversationItem[]
22 | >(RequestApi.GetConversationListSplit, data =>
23 | JSON.stringify([data.offset, data.count])
24 | ),
25 | getOneConversation: openIMSDK.createRequestFunction<
26 | GetOneConversationParams,
27 | ConversationItem
28 | >(RequestApi.GetOneConversation, data =>
29 | JSON.stringify([data.sessionType, data.sourceID])
30 | ),
31 | getMultipleConversation: openIMSDK.createRequestFunction<
32 | string,
33 | ConversationItem[]
34 | >(RequestApi.GetMultipleConversation),
35 | getConversationIDBySessionType: openIMSDK.createRequestFunction<
36 | GetOneConversationParams,
37 | ConversationItem
38 | >(RequestApi.GetConversationIDBySessionType, data =>
39 | JSON.stringify([data.sourceID, data.sessionType])
40 | ),
41 | getTotalUnreadMsgCount:
42 | openIMSDK.createRequestFunctionWithoutParams(
43 | RequestApi.GetTotalUnreadMsgCount
44 | ),
45 | markConversationMessageAsRead: openIMSDK.createRequestFunction(
46 | RequestApi.MarkConversationMessageAsRead
47 | ),
48 | setConversationDraft:
49 | openIMSDK.createRequestFunction(
50 | RequestApi.SetConversationDraft,
51 | data => JSON.stringify([data.conversationID, data.draftText])
52 | ),
53 | pinConversation: openIMSDK.createRequestFunction(
54 | RequestApi.PinConversation,
55 | data => JSON.stringify([data.conversationID, data.isPinned])
56 | ),
57 | setConversationRecvMessageOpt:
58 | openIMSDK.createRequestFunction(
59 | RequestApi.SetConversationRecvMessageOpt,
60 | data => JSON.stringify([data.conversationID, data.opt])
61 | ),
62 | setConversationPrivateChat:
63 | openIMSDK.createRequestFunction(
64 | RequestApi.SetConversationPrivateChat,
65 | data => JSON.stringify([data.conversationID, data.isPrivate])
66 | ),
67 | setConversationBurnDuration:
68 | openIMSDK.createRequestFunction(
69 | RequestApi.SetConversationBurnDuration,
70 | data => JSON.stringify([data.conversationID, data.burnDuration])
71 | ),
72 | resetConversationGroupAtType: openIMSDK.createRequestFunction(
73 | RequestApi.ResetConversationGroupAtType
74 | ),
75 | hideConversation: openIMSDK.createRequestFunction(
76 | RequestApi.HideConversation
77 | ),
78 | hideAllConversation: openIMSDK.createRequestFunctionWithoutParams(
79 | RequestApi.HideAllConversation
80 | ),
81 | clearConversationAndDeleteAllMsg: openIMSDK.createRequestFunction(
82 | RequestApi.ClearConversationAndDeleteAllMsg
83 | ),
84 | deleteConversationAndDeleteAllMsg: openIMSDK.createRequestFunction(
85 | RequestApi.DeleteConversationAndDeleteAllMsg
86 | ),
87 | };
88 | }
89 |
90 | export interface ConversationApi {
91 | getAllConversationList: (
92 | operationID?: string
93 | ) => Promise>;
94 | getConversationListSplit: (
95 | params: SplitConversationParams,
96 | operationID?: string
97 | ) => Promise>;
98 | getOneConversation: (
99 | params: GetOneConversationParams,
100 | operationID?: string
101 | ) => Promise>;
102 | getMultipleConversation: (
103 | params: string,
104 | operationID?: string
105 | ) => Promise>;
106 | getConversationIDBySessionType: (
107 | params: GetOneConversationParams,
108 | operationID?: string
109 | ) => Promise>;
110 | getTotalUnreadMsgCount: (operationID?: string) => Promise>;
111 | markConversationMessageAsRead: (
112 | params: string,
113 | operationID?: string
114 | ) => Promise>;
115 | setConversationDraft: (
116 | params: SetConversationDraftParams,
117 | operationID?: string
118 | ) => Promise>;
119 | pinConversation: (
120 | params: PinConversationParams,
121 | operationID?: string
122 | ) => Promise>;
123 | setConversationRecvMessageOpt: (
124 | params: SetConversationRecvOptParams,
125 | operationID?: string
126 | ) => Promise>;
127 | setConversationPrivateChat: (
128 | params: SetConversationPrivateParams,
129 | operationID?: string
130 | ) => Promise>;
131 | setConversationBurnDuration: (
132 | params: SetBurnDurationParams,
133 | operationID?: string
134 | ) => Promise>;
135 | resetConversationGroupAtType: (
136 | params: string,
137 | operationID?: string
138 | ) => Promise>;
139 | hideConversation: (
140 | params: string,
141 | operationID?: string
142 | ) => Promise>;
143 | hideAllConversation: (operationID?: string) => Promise>;
144 | clearConversationAndDeleteAllMsg: (
145 | params: string,
146 | operationID?: string
147 | ) => Promise>;
148 | deleteConversationAndDeleteAllMsg: (
149 | params: string,
150 | operationID?: string
151 | ) => Promise>;
152 | }
153 |
--------------------------------------------------------------------------------
/src/api/friend.ts:
--------------------------------------------------------------------------------
1 | import type {
2 | AccessFriendParams,
3 | AddBlackParams,
4 | RemarkFriendParams,
5 | SearchFriendParams,
6 | } from '@/types/params';
7 | import OpenIMSDK from '.';
8 | import { RequestApi } from '@/constant/api';
9 | import type {
10 | BlackUserItem,
11 | FriendApplicationItem,
12 | FriendshipInfo,
13 | WsResponse,
14 | SearchedFriendsInfo,
15 | FullUserItem,
16 | Pagination,
17 | } from '@/types/entity';
18 |
19 | export function setupFriend(openIMSDK: OpenIMSDK) {
20 | return {
21 | acceptFriendApplication:
22 | openIMSDK.createRequestFunction(
23 | RequestApi.AcceptFriendApplication
24 | ),
25 | addBlack: openIMSDK.createRequestFunction(
26 | RequestApi.AddBlack,
27 | data => JSON.stringify([data.toUserID, data.ex ?? ''])
28 | ),
29 | addFriend: openIMSDK.createRequestFunction(RequestApi.AddFriend),
30 | checkFriend: openIMSDK.createRequestFunction(
31 | RequestApi.CheckFriend
32 | ),
33 | deleteFriend: openIMSDK.createRequestFunction(
34 | RequestApi.DeleteFriend
35 | ),
36 | getBlackList: openIMSDK.createRequestFunctionWithoutParams(
37 | RequestApi.GetBlackList
38 | ),
39 | getFriendApplicationListAsApplicant:
40 | openIMSDK.createRequestFunctionWithoutParams(
41 | RequestApi.GetFriendApplicationListAsApplicant
42 | ),
43 | getFriendApplicationListAsRecipient:
44 | openIMSDK.createRequestFunctionWithoutParams(
45 | RequestApi.GetFriendApplicationListAsRecipient
46 | ),
47 | getFriendList: openIMSDK.createRequestFunctionWithoutParams(
48 | RequestApi.GetFriendList
49 | ),
50 | getFriendListPage: openIMSDK.createRequestFunctionWithoutParams<
51 | FullUserItem[]
52 | >(RequestApi.GetFriendListPage),
53 | getSpecifiedFriendsInfo: openIMSDK.createRequestFunction<
54 | string[],
55 | FullUserItem[]
56 | >(RequestApi.GetSpecifiedFriendsInfo),
57 | refuseFriendApplication:
58 | openIMSDK.createRequestFunction(
59 | RequestApi.RefuseFriendApplication
60 | ),
61 | removeBlack: openIMSDK.createRequestFunction(
62 | RequestApi.RemoveBlack
63 | ),
64 | searchFriends: openIMSDK.createRequestFunction<
65 | SearchFriendParams,
66 | SearchedFriendsInfo[]
67 | >(RequestApi.SearchFriends),
68 | setFriendRemark: openIMSDK.createRequestFunction(
69 | RequestApi.SetFriendRemark
70 | ),
71 | };
72 | }
73 |
74 | export interface FriendApi {
75 | acceptFriendApplication: (
76 | params: AccessFriendParams,
77 | operationID?: string
78 | ) => Promise>;
79 | addBlack: (
80 | params: AddBlackParams,
81 | operationID?: string
82 | ) => Promise>;
83 | addFriend: (
84 | params: string,
85 | operationID?: string
86 | ) => Promise>;
87 | checkFriend: (
88 | params: string[],
89 | operationID?: string
90 | ) => Promise>;
91 | deleteFriend: (
92 | params: string,
93 | operationID?: string
94 | ) => Promise>;
95 | getBlackList: (operationID?: string) => Promise>;
96 | getFriendApplicationListAsApplicant: (
97 | operationID?: string
98 | ) => Promise>;
99 | getFriendApplicationListAsRecipient: (
100 | operationID?: string
101 | ) => Promise>;
102 | getFriendList: (operationID?: string) => Promise>;
103 | getFriendListPage: (
104 | params: Pagination,
105 | operationID?: string
106 | ) => Promise>;
107 | getSpecifiedFriendsInfo: (
108 | params: string[],
109 | operationID?: string
110 | ) => Promise>;
111 | refuseFriendApplication: (
112 | params: AccessFriendParams,
113 | operationID?: string
114 | ) => Promise>;
115 | removeBlack: (
116 | params: string,
117 | operationID?: string
118 | ) => Promise>;
119 | searchFriends: (
120 | params: SearchFriendParams,
121 | operationID?: string
122 | ) => Promise>;
123 | setFriendRemark: (
124 | params: RemarkFriendParams,
125 | operationID?: string
126 | ) => Promise>;
127 | }
128 |
--------------------------------------------------------------------------------
/src/api/group.ts:
--------------------------------------------------------------------------------
1 | import type {
2 | AccessGroupParams,
3 | ChangeGroupMemberMuteParams,
4 | ChangeGroupMuteParams,
5 | CreateGroupParams,
6 | GetGroupMemberByTimeParams,
7 | GetGroupMemberParams,
8 | JoinGroupParams,
9 | OpreateGroupParams,
10 | SearchGroupMemberParams,
11 | SearchGroupParams,
12 | SetGroupinfoParams,
13 | TransferGroupParams,
14 | UpdateMemberInfoParams,
15 | getGroupMembersInfoParams,
16 | } from '@/types/params';
17 | import OpenIMSDK from '.';
18 | import { RequestApi } from '@/constant/api';
19 | import type {
20 | GroupApplicationItem,
21 | GroupItem,
22 | GroupMemberItem,
23 | Pagination,
24 | WsResponse,
25 | } from '@/types/entity';
26 |
27 | export function setupGroup(openIMSDK: OpenIMSDK) {
28 | return {
29 | createGroup: openIMSDK.createRequestFunction(
30 | RequestApi.CreateGroup
31 | ),
32 | joinGroup: openIMSDK.createRequestFunction(
33 | RequestApi.JoinGroup,
34 | data =>
35 | JSON.stringify([
36 | data.groupID,
37 | data.reqMsg,
38 | data.joinSource,
39 | data.ex ?? '',
40 | ])
41 | ),
42 | inviteUserToGroup: openIMSDK.createRequestFunction(
43 | RequestApi.InviteUserToGroup,
44 | data =>
45 | JSON.stringify([
46 | data.groupID,
47 | data.reason,
48 | JSON.stringify(data.userIDList),
49 | ])
50 | ),
51 | getJoinedGroupList: openIMSDK.createRequestFunctionWithoutParams<
52 | GroupItem[]
53 | >(RequestApi.GetJoinedGroupList),
54 | searchGroups: openIMSDK.createRequestFunction<
55 | SearchGroupParams,
56 | GroupItem[]
57 | >(RequestApi.SearchGroups),
58 | getSpecifiedGroupsInfo: openIMSDK.createRequestFunction<
59 | string[],
60 | GroupItem[]
61 | >(RequestApi.GetSpecifiedGroupsInfo),
62 | setGroupInfo: openIMSDK.createRequestFunction(
63 | RequestApi.SetGroupInfo
64 | ),
65 | getGroupApplicationListAsRecipient:
66 | openIMSDK.createRequestFunctionWithoutParams(
67 | RequestApi.GetGroupApplicationListAsRecipient
68 | ),
69 | getGroupApplicationListAsApplicant:
70 | openIMSDK.createRequestFunctionWithoutParams(
71 | RequestApi.GetGroupApplicationListAsApplicant
72 | ),
73 | acceptGroupApplication: openIMSDK.createRequestFunction(
74 | RequestApi.AcceptGroupApplication,
75 | data => JSON.stringify([data.groupID, data.fromUserID, data.handleMsg])
76 | ),
77 | refuseGroupApplication: openIMSDK.createRequestFunction(
78 | RequestApi.RefuseGroupApplication,
79 | data => JSON.stringify([data.groupID, data.fromUserID, data.handleMsg])
80 | ),
81 | getGroupMemberList: openIMSDK.createRequestFunction<
82 | GetGroupMemberParams,
83 | GroupMemberItem[]
84 | >(RequestApi.GetGroupMemberList, data =>
85 | JSON.stringify([data.groupID, data.filter, data.offset, data.count])
86 | ),
87 | getSpecifiedGroupMembersInfo: openIMSDK.createRequestFunction<
88 | getGroupMembersInfoParams,
89 | GroupMemberItem[]
90 | >(RequestApi.GetSpecifiedGroupMembersInfo, data =>
91 | JSON.stringify([data.groupID, JSON.stringify(data.userIDList)])
92 | ),
93 | searchGroupMembers: openIMSDK.createRequestFunction<
94 | SearchGroupMemberParams,
95 | GroupMemberItem[]
96 | >(RequestApi.SearchGroupMembers),
97 | setGroupMemberInfo: openIMSDK.createRequestFunction(
98 | RequestApi.SetGroupMemberInfo
99 | ),
100 | getGroupMemberOwnerAndAdmin: openIMSDK.createRequestFunction<
101 | string,
102 | GroupMemberItem[]
103 | >(RequestApi.GetGroupMemberOwnerAndAdmin),
104 | getGroupMemberListByJoinTimeFilter: openIMSDK.createRequestFunction<
105 | GetGroupMemberByTimeParams,
106 | GroupMemberItem[]
107 | >(RequestApi.GetGroupMemberListByJoinTimeFilter, data =>
108 | JSON.stringify([
109 | data.groupID,
110 | data.offset,
111 | data.count,
112 | data.joinTimeBegin,
113 | data.joinTimeEnd,
114 | JSON.stringify(data.filterUserIDList),
115 | ])
116 | ),
117 | kickGroupMember: openIMSDK.createRequestFunction(
118 | RequestApi.KickGroupMember,
119 | data =>
120 | JSON.stringify([
121 | data.groupID,
122 | data.reason,
123 | JSON.stringify(data.userIDList),
124 | ])
125 | ),
126 | changeGroupMemberMute:
127 | openIMSDK.createRequestFunction(
128 | RequestApi.ChangeGroupMemberMute,
129 | data => JSON.stringify([data.groupID, data.userID, data.mutedSeconds])
130 | ),
131 | changeGroupMute: openIMSDK.createRequestFunction(
132 | RequestApi.ChangeGroupMute,
133 | data => JSON.stringify([data.groupID, data.isMute])
134 | ),
135 | transferGroupOwner: openIMSDK.createRequestFunction(
136 | RequestApi.TransferGroupOwner,
137 | data => JSON.stringify([data.groupID, data.newOwnerUserID])
138 | ),
139 | dismissGroup: openIMSDK.createRequestFunction(
140 | RequestApi.DismissGroup
141 | ),
142 | quitGroup: openIMSDK.createRequestFunction(RequestApi.QuitGroup),
143 | };
144 | }
145 |
146 | export interface GroupApi {
147 | createGroup: (
148 | params: CreateGroupParams,
149 | operationID?: string
150 | ) => Promise>;
151 | joinGroup: (
152 | params: JoinGroupParams,
153 | operationID?: string
154 | ) => Promise>;
155 | inviteUserToGroup: (
156 | params: OpreateGroupParams,
157 | operationID?: string
158 | ) => Promise>;
159 | getJoinedGroupList: (
160 | operationID?: string
161 | ) => Promise>;
162 | getJoinedGroupListPage: (
163 | params: Pagination,
164 | operationID?: string
165 | ) => Promise>;
166 | searchGroups: (
167 | params: SearchGroupParams,
168 | operationID?: string
169 | ) => Promise>;
170 | getSpecifiedGroupsInfo: (
171 | params: string[],
172 | operationID?: string
173 | ) => Promise>;
174 | setGroupInfo: (
175 | params: SetGroupinfoParams,
176 | operationID?: string
177 | ) => Promise>;
178 | getGroupApplicationListAsRecipient: (
179 | operationID?: string
180 | ) => Promise>;
181 | getGroupApplicationListAsApplicant: (
182 | operationID?: string
183 | ) => Promise>;
184 | acceptGroupApplication: (
185 | params: AccessGroupParams,
186 | operationID?: string
187 | ) => Promise>;
188 | refuseGroupApplication: (
189 | params: AccessGroupParams,
190 | operationID?: string
191 | ) => Promise>;
192 | getGroupMemberList: (
193 | operationID?: string
194 | ) => Promise>;
195 | getSpecifiedGroupMembersInfo: (
196 | params: getGroupMembersInfoParams,
197 | operationID?: string
198 | ) => Promise>;
199 | searchGroupMembers: (
200 | params: SearchGroupMemberParams,
201 | operationID?: string
202 | ) => Promise>;
203 | setGroupMemberInfo: (
204 | params: UpdateMemberInfoParams,
205 | operationID?: string
206 | ) => Promise>;
207 | getGroupMemberOwnerAndAdmin: (
208 | params: string,
209 | operationID?: string
210 | ) => Promise>;
211 | getGroupMemberListByJoinTimeFilter: (
212 | params: GetGroupMemberByTimeParams,
213 | operationID?: string
214 | ) => Promise>;
215 | kickGroupMember: (
216 | params: OpreateGroupParams,
217 | operationID?: string
218 | ) => Promise>;
219 | changeGroupMemberMute: (
220 | params: ChangeGroupMemberMuteParams,
221 | operationID?: string
222 | ) => Promise>;
223 | changeGroupMute: (
224 | params: ChangeGroupMuteParams,
225 | operationID?: string
226 | ) => Promise>;
227 | transferGroupOwner: (
228 | params: TransferGroupParams,
229 | operationID?: string
230 | ) => Promise>;
231 | dismissGroup: (
232 | params: string,
233 | operationID?: string
234 | ) => Promise>;
235 | quitGroup: (
236 | params: string,
237 | operationID?: string
238 | ) => Promise>;
239 | }
240 |
--------------------------------------------------------------------------------
/src/api/index.ts:
--------------------------------------------------------------------------------
1 | import type {
2 | MessageItem,
3 | PromiseMap,
4 | WsRequest,
5 | WsResponse,
6 | } from '@/types/entity';
7 | import type {
8 | FileMsgParams,
9 | ImageMsgParams,
10 | LoginParams,
11 | SoundMsgParams,
12 | UploadFileParams,
13 | VideoMsgParams,
14 | } from '@/types/params';
15 | import WebSocketManager from '@/utils/webSocketManager';
16 | import { ErrorCode, RequestApi } from '@/constant/api';
17 | import { UserApi, setupUser } from './user';
18 | import { FriendApi, setupFriend } from './friend';
19 | import { GroupApi, setupGroup } from './group';
20 | import { MessageApi, setupMessage } from './message';
21 | import { ConversationApi, setupConversation } from './conversation';
22 | import Emitter from '@/utils/emitter';
23 | import { CbEvents } from '@/constant/callback';
24 | import SparkMD5 from 'spark-md5';
25 | import {
26 | confirmUpload,
27 | getMimeType,
28 | getUploadPartsize,
29 | getUploadUrl,
30 | } from '@/utils/upload';
31 | import { LoginStatus } from '@/types/enum';
32 | import { uuid } from '@/utils/uuid';
33 |
34 | const forceCloseEvents = [
35 | RequestApi.Logout,
36 | CbEvents.OnKickedOffline,
37 | CbEvents.OnUserTokenInvalid,
38 | CbEvents.OnUserTokenExpired,
39 | ];
40 |
41 | function isEventInCallbackEvents(event: string): event is CbEvents {
42 | return Object.values(CbEvents).includes(event as CbEvents);
43 | }
44 |
45 | class OpenIMSDK
46 | extends Emitter
47 | implements UserApi, FriendApi, GroupApi, MessageApi, ConversationApi
48 | {
49 | private userID?: string;
50 | private token?: string;
51 | private apiAddr?: string;
52 | private wsManager?: WebSocketManager;
53 | private requestMap = new Map();
54 |
55 | constructor() {
56 | super();
57 | Object.assign(this, setupUser(this));
58 | Object.assign(this, setupFriend(this));
59 | Object.assign(this, setupGroup(this));
60 | Object.assign(this, setupMessage(this));
61 | Object.assign(this, setupConversation(this));
62 | }
63 |
64 | private sendRequest = (requestObj: WsRequest): Promise> => {
65 | return new Promise((resolve, reject) => {
66 | if (!this.wsManager) {
67 | reject({
68 | data: '',
69 | operationID: requestObj.operationID,
70 | errMsg: 'please login first',
71 | errCode: ErrorCode.ResourceLoadNotCompleteError,
72 | event: requestObj.reqFuncName,
73 | });
74 | return;
75 | }
76 |
77 | this.requestMap.set(requestObj.operationID, {
78 | resolve: resolve as unknown as (value: WsResponse) => void,
79 | reject,
80 | });
81 | this.wsManager?.sendMessage(requestObj);
82 | });
83 | };
84 |
85 | private defaultDataFormatter = (params: T) => {
86 | if (typeof params === 'object') {
87 | params = JSON.stringify(params) as unknown as T;
88 | }
89 | return JSON.stringify([params]);
90 | };
91 |
92 | createRequestFunction = (
93 | reqFuncName: RequestApi,
94 | dataFormatter = this.defaultDataFormatter
95 | ) => {
96 | return (params: T, operationID = uuid()) => {
97 | const data = dataFormatter(params as T);
98 | return this.sendRequest({
99 | data,
100 | operationID,
101 | userID: this.userID!,
102 | reqFuncName: reqFuncName,
103 | });
104 | };
105 | };
106 |
107 | createRequestFunctionWithoutParams = (
108 | reqFuncName: RequestApi
109 | ) => {
110 | return (operationID = uuid()) =>
111 | this.sendRequest({
112 | data: '[]',
113 | operationID,
114 | userID: this.userID!,
115 | reqFuncName: reqFuncName,
116 | });
117 | };
118 |
119 | private handleMessage = (data: WsResponse) => {
120 | if (data.event === RequestApi.InitSDK) {
121 | if (data.errCode !== 0) console.error(data);
122 | return;
123 | }
124 |
125 | try {
126 | data.data = JSON.parse(data.data as string);
127 | } catch (error) {}
128 |
129 | if (forceCloseEvents.includes(data.event)) {
130 | this.wsManager?.close();
131 | this.wsManager = undefined;
132 | }
133 |
134 | if (isEventInCallbackEvents(data.event)) {
135 | this.emit(data.event, data);
136 | if (forceCloseEvents.includes(data.event)) {
137 | this.requestMap.clear();
138 | }
139 | return;
140 | }
141 | const promiseHandlers = this.requestMap.get(data.operationID);
142 | if (promiseHandlers) {
143 | const promiseHandler =
144 | data.errCode === 0 ? promiseHandlers.resolve : promiseHandlers.reject;
145 | promiseHandler(data);
146 | this.requestMap.delete(data.operationID);
147 | }
148 | if (forceCloseEvents.includes(data.event)) {
149 | this.requestMap.clear();
150 | }
151 | };
152 |
153 | private handleReconnectSuccess = () => {
154 | if (!this.userID) return;
155 |
156 | this.sendRequest({
157 | data: JSON.stringify([this.userID, this.token]),
158 | operationID: uuid(),
159 | userID: this.userID,
160 | reqFuncName: RequestApi.Login,
161 | });
162 | };
163 |
164 | login = async (
165 | params: LoginParams,
166 | operationID = uuid()
167 | ): Promise => {
168 | if (this.wsManager) {
169 | return Promise.resolve({
170 | data: '',
171 | operationID,
172 | errMsg: 'login repeat',
173 | errCode: ErrorCode.LoginRepeatError,
174 | event: RequestApi.Login,
175 | });
176 | }
177 | const internalWsUrl = `${params.wsAddr}?sendID=${params.userID}&token=${params.token}&platformID=${params.platformID}&operationID=${operationID}`;
178 | this.userID = params.userID;
179 | this.token = params.token;
180 | this.apiAddr = params.apiAddr;
181 | this.wsManager = new WebSocketManager(
182 | internalWsUrl,
183 | this.handleMessage,
184 | this.handleReconnectSuccess
185 | );
186 | try {
187 | await this.wsManager.connect();
188 | } catch (error) {
189 | return Promise.reject({
190 | data: '',
191 | operationID,
192 | errMsg: (error as Error).message,
193 | errCode: ErrorCode.ConnectionEstablishmentFailed,
194 | event: RequestApi.Login,
195 | });
196 | }
197 | return this.sendRequest({
198 | data: JSON.stringify([params.userID, params.token]),
199 | operationID,
200 | userID: this.userID,
201 | reqFuncName: RequestApi.Login,
202 | });
203 | };
204 |
205 | logout = this.createRequestFunctionWithoutParams(RequestApi.Logout);
206 |
207 | getLoginStatus = this.createRequestFunctionWithoutParams(
208 | RequestApi.GetLoginStatus
209 | );
210 |
211 | getLoginUserID = this.createRequestFunctionWithoutParams(
212 | RequestApi.GetLoginUserID
213 | );
214 |
215 | // third
216 | private internalUploadFile = async (
217 | file: File,
218 | operationID: string
219 | ): Promise<{ url?: string; error?: Error }> => {
220 | try {
221 | const fileName = `${this.userID}/${file.name}`;
222 | const contentType = getMimeType(file.name);
223 | const commonOptions = { operationID, token: this.token! };
224 | const { size: partSize } = await getUploadPartsize(
225 | this.apiAddr!,
226 | file.size,
227 | commonOptions
228 | );
229 | const chunks = Math.ceil(file.size / partSize);
230 | const chunkGapList: { start: number; end: number }[] = [];
231 | const chunkHashList: string[] = [];
232 | const fileSpark = new SparkMD5.ArrayBuffer();
233 | let currentChunk = 0;
234 |
235 | while (currentChunk < chunks) {
236 | const start = currentChunk * partSize;
237 | const end = Math.min(start + partSize, file.size);
238 | const chunk = file.slice(start, end);
239 | chunkGapList.push({ start, end });
240 |
241 | // Use a self-invoking function to capture the currentChunk index
242 | const chunkHash = await new Promise((resolve, reject) => {
243 | const reader = new FileReader();
244 | reader.readAsArrayBuffer(chunk);
245 | reader.onload = e => {
246 | if (e.target) {
247 | fileSpark.append(e.target.result as ArrayBuffer);
248 | resolve(fileSpark.end());
249 | }
250 | };
251 | reader.onerror = err => reject(err);
252 | });
253 | chunkHashList.push(chunkHash);
254 | currentChunk++;
255 | }
256 |
257 | const totalFileHash = chunkHashList.join(',');
258 | fileSpark.destroy();
259 | const textSpark = new SparkMD5();
260 | textSpark.append(totalFileHash);
261 | const { url: finishUrl, upload } = await getUploadUrl(
262 | this.apiAddr!,
263 | {
264 | hash: textSpark.end(),
265 | size: file.size,
266 | partSize,
267 | maxParts: -1,
268 | cause: '',
269 | name: fileName,
270 | contentType,
271 | },
272 | commonOptions
273 | );
274 | textSpark.destroy();
275 | if (finishUrl) {
276 | return {
277 | url: finishUrl,
278 | };
279 | }
280 |
281 | let uploadParts = upload.sign.parts;
282 | const signQuery = upload.sign.query;
283 | const signHeader = upload.sign.header;
284 |
285 | // Use Promise.all to wait for all PUT operations to complete
286 | await Promise.all(
287 | uploadParts.map(async (part, idx) => {
288 | const url = part.url || upload.sign.url;
289 | const rawUrl = new URL(url);
290 | if (signQuery) {
291 | const params = new URLSearchParams(rawUrl.search);
292 | signQuery.forEach(item => {
293 | params.set(item.key, item.values[0]);
294 | });
295 | rawUrl.search = params.toString();
296 | }
297 | if (part.query) {
298 | const params = new URLSearchParams(rawUrl.search);
299 | part.query.forEach(item => {
300 | params.set(item.key, item.values[0]);
301 | });
302 | rawUrl.search = params.toString();
303 | }
304 | const putUrl = rawUrl.toString();
305 | const headers = new Headers();
306 | if (signHeader) {
307 | signHeader.forEach(item => {
308 | headers.set(item.key, item.values[0]);
309 | });
310 | }
311 | if (part.header) {
312 | part.header.forEach(item => {
313 | headers.set(item.key, item.values[0]);
314 | });
315 | }
316 | headers.set(
317 | 'Content-Length',
318 | (chunkGapList[idx].end - chunkGapList[idx].start).toString()
319 | );
320 |
321 | // Ensure correct content type is set for the chunk
322 | headers.set('Content-Type', contentType);
323 |
324 | const response = await fetch(putUrl, {
325 | method: 'PUT',
326 | headers,
327 | body: file.slice(chunkGapList[idx].start, chunkGapList[idx].end),
328 | });
329 |
330 | if (!response.ok) {
331 | throw new Error(`Failed to upload chunk ${idx + 1}`);
332 | }
333 | })
334 | );
335 |
336 | const { url } = await confirmUpload(
337 | this.apiAddr!,
338 | {
339 | uploadID: upload.uploadID,
340 | parts: chunkHashList,
341 | cause: '',
342 | name: fileName,
343 | contentType,
344 | },
345 | commonOptions
346 | );
347 | return { url };
348 | } catch (error) {
349 | console.error('Upload failed:', error);
350 | return { error: error as Error };
351 | }
352 | };
353 |
354 | uploadFile = async (
355 | { file }: UploadFileParams,
356 | operationID = uuid()
357 | ): Promise> => {
358 | const { url = '', error } = await this.internalUploadFile(
359 | file,
360 | operationID
361 | );
362 | return {
363 | data: {
364 | url,
365 | },
366 | operationID,
367 | errMsg: error?.message ?? '',
368 | errCode: error ? ErrorCode.UnknownError : 0,
369 | event: RequestApi.UploadFile,
370 | };
371 | };
372 |
373 | // extends message
374 | createImageMessageByFile = async (
375 | params: ImageMsgParams & { file: File },
376 | operationID = uuid()
377 | ) => {
378 | const { url, error } = await this.internalUploadFile(
379 | params.file,
380 | operationID
381 | );
382 | if (error) {
383 | return Promise.reject({
384 | data: '',
385 | operationID,
386 | errMsg: error.message,
387 | errCode: ErrorCode.UnknownError,
388 | event: RequestApi.CreateImageMessageByFile,
389 | });
390 | }
391 | params.sourcePicture.url = url!;
392 | params.bigPicture.url = url!;
393 | params.snapshotPicture.url = `${url}?type=image&width=${params.snapshotPicture.width}&height=${params.snapshotPicture.height}`;
394 | const tmpParams = { ...params };
395 | // @ts-ignore
396 | delete tmpParams.file;
397 | return this.createImageMessageByURL(tmpParams, operationID) as Promise<
398 | WsResponse
399 | >;
400 | };
401 |
402 | createVideoMessageByFile = async (
403 | params: VideoMsgParams & { videoFile: File; snapshotFile: File },
404 | operationID = uuid()
405 | ) => {
406 | try {
407 | const [{ url: snapshotUrl }, { url: videoUrl }] = await Promise.all([
408 | this.internalUploadFile(params.snapshotFile, operationID),
409 | this.internalUploadFile(params.videoFile, operationID),
410 | ]);
411 | params.videoUrl = videoUrl!;
412 | params.snapshotUrl = `${snapshotUrl}?type=image&width=${params.snapshotWidth}&height=${params.snapshotHeight}`;
413 | const tmpParams = { ...params };
414 | // @ts-ignore
415 | delete tmpParams.videoFile;
416 | // @ts-ignore
417 | delete tmpParams.snapshotFile;
418 | return this.createVideoMessageByURL(tmpParams, operationID) as Promise<
419 | WsResponse
420 | >;
421 | } catch (error) {
422 | return Promise.reject({
423 | data: '',
424 | operationID,
425 | errMsg: (error as Error).message,
426 | errCode: ErrorCode.UnknownError,
427 | event: RequestApi.CreateVideoMessageByFile,
428 | });
429 | }
430 | };
431 |
432 | createSoundMessageByFile = async (
433 | params: SoundMsgParams & { file: File },
434 | operationID = uuid()
435 | ) => {
436 | const { url, error } = await this.internalUploadFile(
437 | params.file,
438 | operationID
439 | );
440 | if (error) {
441 | return Promise.reject({
442 | data: '',
443 | operationID,
444 | errMsg: error.message,
445 | errCode: ErrorCode.UnknownError,
446 | event: RequestApi.CreateSoundMessageByFile,
447 | });
448 | }
449 | params.sourceUrl = url!;
450 | const tmpParams = { ...params };
451 | // @ts-ignore
452 | delete tmpParams.file;
453 | return this.createSoundMessageByURL(tmpParams, operationID) as Promise<
454 | WsResponse
455 | >;
456 | };
457 |
458 | createFileMessageByFile = async (
459 | params: FileMsgParams & { file: File },
460 | operationID = uuid()
461 | ) => {
462 | const { url, error } = await this.internalUploadFile(
463 | params.file,
464 | operationID
465 | );
466 | if (error) {
467 | return Promise.reject({
468 | data: '',
469 | operationID,
470 | errMsg: error.message,
471 | errCode: ErrorCode.UnknownError,
472 | event: RequestApi.CreateFileMessageByFile,
473 | });
474 | }
475 | params.sourceUrl = url!;
476 | const tmpParams = { ...params };
477 | // @ts-ignore
478 | delete tmpParams.file;
479 | return this.createFileMessageByURL(tmpParams, operationID) as Promise<
480 | WsResponse
481 | >;
482 | };
483 |
484 | // UserApi
485 | getSelfUserInfo!: UserApi['getSelfUserInfo'];
486 | setSelfInfo!: UserApi['setSelfInfo'];
487 | getUsersInfoWithCache!: UserApi['getUsersInfoWithCache'];
488 | subscribeUsersStatus!: UserApi['subscribeUsersStatus'];
489 | unsubscribeUsersStatus!: UserApi['unsubscribeUsersStatus'];
490 | getSubscribeUsersStatus!: UserApi['getSubscribeUsersStatus'];
491 | setAppBackgroundStatus!: UserApi['setAppBackgroundStatus'];
492 | networkStatusChanged!: UserApi['networkStatusChanged'];
493 | setGlobalRecvMessageOpt!: UserApi['setGlobalRecvMessageOpt'];
494 |
495 | // FriendApi
496 | acceptFriendApplication!: FriendApi['acceptFriendApplication'];
497 | addBlack!: FriendApi['addBlack'];
498 | addFriend!: FriendApi['addFriend'];
499 | checkFriend!: FriendApi['checkFriend'];
500 | deleteFriend!: FriendApi['deleteFriend'];
501 | getBlackList!: FriendApi['getBlackList'];
502 | getFriendApplicationListAsApplicant!: FriendApi['getFriendApplicationListAsApplicant'];
503 | getFriendApplicationListAsRecipient!: FriendApi['getFriendApplicationListAsRecipient'];
504 | getFriendList!: FriendApi['getFriendList'];
505 | getFriendListPage!: FriendApi['getFriendListPage'];
506 | getSpecifiedFriendsInfo!: FriendApi['getSpecifiedFriendsInfo'];
507 | refuseFriendApplication!: FriendApi['refuseFriendApplication'];
508 | removeBlack!: FriendApi['removeBlack'];
509 | searchFriends!: FriendApi['searchFriends'];
510 | setFriendRemark!: FriendApi['setFriendRemark'];
511 |
512 | // GroupApi
513 | createGroup!: GroupApi['createGroup'];
514 | joinGroup!: GroupApi['joinGroup'];
515 | inviteUserToGroup!: GroupApi['inviteUserToGroup'];
516 | getJoinedGroupList!: GroupApi['getJoinedGroupList'];
517 | getJoinedGroupListPage!: GroupApi['getJoinedGroupListPage'];
518 | searchGroups!: GroupApi['searchGroups'];
519 | getSpecifiedGroupsInfo!: GroupApi['getSpecifiedGroupsInfo'];
520 | setGroupInfo!: GroupApi['setGroupInfo'];
521 | getGroupApplicationListAsRecipient!: GroupApi['getGroupApplicationListAsRecipient'];
522 | getGroupApplicationListAsApplicant!: GroupApi['getGroupApplicationListAsApplicant'];
523 | acceptGroupApplication!: GroupApi['acceptGroupApplication'];
524 | refuseGroupApplication!: GroupApi['refuseGroupApplication'];
525 | getGroupMemberList!: GroupApi['getGroupMemberList'];
526 | getSpecifiedGroupMembersInfo!: GroupApi['getSpecifiedGroupMembersInfo'];
527 | searchGroupMembers!: GroupApi['searchGroupMembers'];
528 | setGroupMemberInfo!: GroupApi['setGroupMemberInfo'];
529 | getGroupMemberOwnerAndAdmin!: GroupApi['getGroupMemberOwnerAndAdmin'];
530 | getGroupMemberListByJoinTimeFilter!: GroupApi['getGroupMemberListByJoinTimeFilter'];
531 | kickGroupMember!: GroupApi['kickGroupMember'];
532 | changeGroupMemberMute!: GroupApi['changeGroupMemberMute'];
533 | changeGroupMute!: GroupApi['changeGroupMute'];
534 | transferGroupOwner!: GroupApi['transferGroupOwner'];
535 | dismissGroup!: GroupApi['dismissGroup'];
536 | quitGroup!: GroupApi['quitGroup'];
537 |
538 | // MessageApi
539 | createTextMessage!: MessageApi['createTextMessage'];
540 | createTextAtMessage!: MessageApi['createTextAtMessage'];
541 | createImageMessageByURL!: MessageApi['createImageMessageByURL'];
542 | createSoundMessageByURL!: MessageApi['createSoundMessageByURL'];
543 | createVideoMessageByURL!: MessageApi['createVideoMessageByURL'];
544 | createFileMessageByURL!: MessageApi['createFileMessageByURL'];
545 | createMergerMessage!: MessageApi['createMergerMessage'];
546 | createForwardMessage!: MessageApi['createForwardMessage'];
547 | createLocationMessage!: MessageApi['createLocationMessage'];
548 | createQuoteMessage!: MessageApi['createQuoteMessage'];
549 | createCardMessage!: MessageApi['createCardMessage'];
550 | createCustomMessage!: MessageApi['createCustomMessage'];
551 | createFaceMessage!: MessageApi['createFaceMessage'];
552 | sendMessage!: MessageApi['sendMessage'];
553 | sendMessageNotOss!: MessageApi['sendMessageNotOss'];
554 | typingStatusUpdate!: MessageApi['typingStatusUpdate'];
555 | revokeMessage!: MessageApi['revokeMessage'];
556 | deleteMessage!: MessageApi['deleteMessage'];
557 | deleteMessageFromLocalStorage!: MessageApi['deleteMessageFromLocalStorage'];
558 | deleteAllMsgFromLocal!: MessageApi['deleteAllMsgFromLocal'];
559 | deleteAllMsgFromLocalAndSvr!: MessageApi['deleteAllMsgFromLocalAndSvr'];
560 | searchLocalMessages!: MessageApi['searchLocalMessages'];
561 | getAdvancedHistoryMessageList!: MessageApi['getAdvancedHistoryMessageList'];
562 | getAdvancedHistoryMessageListReverse!: MessageApi['getAdvancedHistoryMessageListReverse'];
563 | findMessageList!: MessageApi['findMessageList'];
564 | insertGroupMessageToLocalStorage!: MessageApi['insertGroupMessageToLocalStorage'];
565 | insertSingleMessageToLocalStorage!: MessageApi['insertSingleMessageToLocalStorage'];
566 | setMessageLocalEx!: MessageApi['setMessageLocalEx'];
567 |
568 | // ConversationApi
569 | getAllConversationList!: ConversationApi['getAllConversationList'];
570 | getConversationListSplit!: ConversationApi['getConversationListSplit'];
571 | getOneConversation!: ConversationApi['getOneConversation'];
572 | getMultipleConversation!: ConversationApi['getMultipleConversation'];
573 | getConversationIDBySessionType!: ConversationApi['getConversationIDBySessionType'];
574 | getTotalUnreadMsgCount!: ConversationApi['getTotalUnreadMsgCount'];
575 | markConversationMessageAsRead!: ConversationApi['markConversationMessageAsRead'];
576 | setConversationDraft!: ConversationApi['setConversationDraft'];
577 | pinConversation!: ConversationApi['pinConversation'];
578 | setConversationRecvMessageOpt!: ConversationApi['setConversationRecvMessageOpt'];
579 | setConversationPrivateChat!: ConversationApi['setConversationPrivateChat'];
580 | setConversationBurnDuration!: ConversationApi['setConversationBurnDuration'];
581 | resetConversationGroupAtType!: ConversationApi['resetConversationGroupAtType'];
582 | hideConversation!: ConversationApi['hideConversation'];
583 | hideAllConversation!: ConversationApi['hideAllConversation'];
584 | clearConversationAndDeleteAllMsg!: ConversationApi['clearConversationAndDeleteAllMsg'];
585 | deleteConversationAndDeleteAllMsg!: ConversationApi['deleteConversationAndDeleteAllMsg'];
586 | }
587 |
588 | export default OpenIMSDK;
589 |
590 | export type MixinApiService = OpenIMSDK &
591 | UserApi &
592 | FriendApi &
593 | GroupApi &
594 | MessageApi &
595 | ConversationApi;
596 |
--------------------------------------------------------------------------------
/src/api/message.ts:
--------------------------------------------------------------------------------
1 | import { RequestApi } from '@/constant/api';
2 | import OpenIMSDK from '.';
3 | import type {
4 | AtMsgParams,
5 | CustomMsgParams,
6 | FaceMessageParams,
7 | FileMsgParams,
8 | FindMessageParams,
9 | GetAdvancedHistoryMsgParams,
10 | ImageMsgParams,
11 | InsertGroupMsgParams,
12 | InsertSingleMsgParams,
13 | LocationMsgParams,
14 | MergerMsgParams,
15 | OpreateMessageParams,
16 | QuoteMsgParams,
17 | SearchLocalParams,
18 | SendMsgParams,
19 | SetMessageLocalExParams,
20 | SoundMsgParams,
21 | TypingUpdateParams,
22 | VideoMsgParams,
23 | } from '@/types/params';
24 | import type {
25 | AdvancedGetMessageResult,
26 | CardElem,
27 | MessageItem,
28 | SearchMessageResult,
29 | WsResponse,
30 | } from '@/types/entity';
31 |
32 | export function setupMessage(openIMSDK: OpenIMSDK) {
33 | return {
34 | createTextMessage: openIMSDK.createRequestFunction(
35 | RequestApi.CreateTextMessage
36 | ),
37 | createTextAtMessage: openIMSDK.createRequestFunction<
38 | AtMsgParams,
39 | MessageItem
40 | >(RequestApi.CreateTextAtMessage, data =>
41 | JSON.stringify([
42 | data.text,
43 | JSON.stringify(data.atUserIDList),
44 | JSON.stringify(data.atUsersInfo),
45 | JSON.stringify(data.message) ?? '',
46 | ])
47 | ),
48 | createImageMessageByURL: openIMSDK.createRequestFunction<
49 | ImageMsgParams,
50 | MessageItem
51 | >(RequestApi.CreateImageMessageByURL, data =>
52 | JSON.stringify([
53 | data.sourcePath,
54 | JSON.stringify(data.sourcePicture),
55 | JSON.stringify(data.bigPicture),
56 | JSON.stringify(data.snapshotPicture),
57 | ])
58 | ),
59 | createSoundMessageByURL: openIMSDK.createRequestFunction<
60 | SoundMsgParams,
61 | MessageItem
62 | >(RequestApi.CreateSoundMessageByURL),
63 | createVideoMessageByURL: openIMSDK.createRequestFunction<
64 | VideoMsgParams,
65 | MessageItem
66 | >(RequestApi.CreateVideoMessageByURL),
67 | createFileMessageByURL: openIMSDK.createRequestFunction<
68 | FileMsgParams,
69 | MessageItem
70 | >(RequestApi.CreateFileMessageByURL),
71 | createMergerMessage: openIMSDK.createRequestFunction<
72 | MergerMsgParams,
73 | MessageItem
74 | >(RequestApi.CreateMergerMessage, data =>
75 | JSON.stringify([
76 | JSON.stringify(data.messageList),
77 | data.title,
78 | JSON.stringify(data.summaryList),
79 | ])
80 | ),
81 | createForwardMessage: openIMSDK.createRequestFunction<
82 | MessageItem,
83 | MessageItem
84 | >(RequestApi.CreateForwardMessage),
85 | createLocationMessage: openIMSDK.createRequestFunction<
86 | LocationMsgParams,
87 | MessageItem
88 | >(RequestApi.CreateLocationMessage, data =>
89 | JSON.stringify([data.description, data.longitude, data.latitude])
90 | ),
91 | createQuoteMessage: openIMSDK.createRequestFunction<
92 | QuoteMsgParams,
93 | MessageItem
94 | >(RequestApi.CreateQuoteMessage, data =>
95 | JSON.stringify([data.text, data.message])
96 | ),
97 | createCardMessage: openIMSDK.createRequestFunction(
98 | RequestApi.CreateCardMessage
99 | ),
100 | createCustomMessage: openIMSDK.createRequestFunction<
101 | CustomMsgParams,
102 | MessageItem
103 | >(RequestApi.CreateCustomMessage, data =>
104 | JSON.stringify([data.data, data.extension, data.description])
105 | ),
106 | createFaceMessage: openIMSDK.createRequestFunction<
107 | FaceMessageParams,
108 | MessageItem
109 | >(RequestApi.CreateFaceMessage, data =>
110 | JSON.stringify([data.index, data.data])
111 | ),
112 | sendMessage: openIMSDK.createRequestFunction(
113 | RequestApi.SendMessageNotOss,
114 | data => {
115 | const offlinePushInfo = data.offlinePushInfo ?? {
116 | title: 'You has a new message.',
117 | desc: 'message',
118 | ex: '',
119 | iOSPushSound: '+1',
120 | iOSBadgeCount: true,
121 | };
122 | return JSON.stringify([
123 | JSON.stringify(data.message),
124 | data.recvID,
125 | data.groupID,
126 | JSON.stringify(offlinePushInfo),
127 | data.isOnlineOnly ?? false,
128 | ]);
129 | }
130 | ),
131 | sendMessageNotOss: openIMSDK.createRequestFunction<
132 | SendMsgParams,
133 | MessageItem
134 | >(RequestApi.SendMessageNotOss, data => {
135 | const offlinePushInfo = data.offlinePushInfo ?? {
136 | title: 'You has a new message.',
137 | desc: 'message',
138 | ex: '',
139 | iOSPushSound: '+1',
140 | iOSBadgeCount: true,
141 | };
142 | return JSON.stringify([
143 | JSON.stringify(data.message),
144 | data.recvID,
145 | data.groupID,
146 | JSON.stringify(offlinePushInfo),
147 | data.isOnlineOnly ?? false,
148 | ]);
149 | }),
150 | typingStatusUpdate: openIMSDK.createRequestFunction(
151 | RequestApi.TypingStatusUpdate,
152 | data => JSON.stringify([data.recvID, data.msgTip])
153 | ),
154 | revokeMessage: openIMSDK.createRequestFunction(
155 | RequestApi.RevokeMessage,
156 | data => JSON.stringify([data.conversationID, data.clientMsgID])
157 | ),
158 | deleteMessage: openIMSDK.createRequestFunction(
159 | RequestApi.DeleteMessage,
160 | data => JSON.stringify([data.conversationID, data.clientMsgID])
161 | ),
162 | deleteMessageFromLocalStorage:
163 | openIMSDK.createRequestFunction(
164 | RequestApi.DeleteMessageFromLocalStorage,
165 | data => JSON.stringify([data.conversationID, data.clientMsgID])
166 | ),
167 | deleteAllMsgFromLocal: openIMSDK.createRequestFunctionWithoutParams(
168 | RequestApi.DeleteAllMsgFromLocal
169 | ),
170 | deleteAllMsgFromLocalAndSvr: openIMSDK.createRequestFunctionWithoutParams(
171 | RequestApi.DeleteAllMsgFromLocalAndSvr
172 | ),
173 | searchLocalMessages: openIMSDK.createRequestFunction<
174 | SearchLocalParams,
175 | SearchMessageResult
176 | >(RequestApi.SearchLocalMessages),
177 | getAdvancedHistoryMessageList: openIMSDK.createRequestFunction<
178 | GetAdvancedHistoryMsgParams,
179 | AdvancedGetMessageResult
180 | >(RequestApi.GetAdvancedHistoryMessageList),
181 | getAdvancedHistoryMessageListReverse: openIMSDK.createRequestFunction<
182 | GetAdvancedHistoryMsgParams,
183 | AdvancedGetMessageResult
184 | >(RequestApi.GetAdvancedHistoryMessageListReverse),
185 | findMessageList: openIMSDK.createRequestFunction<
186 | FindMessageParams[],
187 | MessageItem[]
188 | >(RequestApi.FindMessageList),
189 | insertGroupMessageToLocalStorage:
190 | openIMSDK.createRequestFunction(
191 | RequestApi.InsertGroupMessageToLocalStorage,
192 | data =>
193 | JSON.stringify([
194 | JSON.stringify(data.message),
195 | data.groupID,
196 | data.sendID,
197 | ])
198 | ),
199 | insertSingleMessageToLocalStorage:
200 | openIMSDK.createRequestFunction(
201 | RequestApi.InsertSingleMessageToLocalStorage,
202 | data =>
203 | JSON.stringify([
204 | JSON.stringify(data.message),
205 | data.recvID,
206 | data.sendID,
207 | ])
208 | ),
209 | setMessageLocalEx: openIMSDK.createRequestFunction(
210 | RequestApi.SetMessageLocalEx,
211 | data =>
212 | JSON.stringify([data.conversationID, data.clientMsgID, data.localEx])
213 | ),
214 | };
215 | }
216 |
217 | export interface MessageApi {
218 | createTextMessage: (
219 | params: string,
220 | operationID?: string
221 | ) => Promise>;
222 | createTextAtMessage: (
223 | params: AtMsgParams,
224 | operationID?: string
225 | ) => Promise>;
226 | createImageMessageByURL: (
227 | params: ImageMsgParams,
228 | operationID?: string
229 | ) => Promise>;
230 | createSoundMessageByURL: (
231 | params: SoundMsgParams,
232 | operationID?: string
233 | ) => Promise>;
234 | createVideoMessageByURL: (
235 | params: VideoMsgParams,
236 | operationID?: string
237 | ) => Promise>;
238 | createFileMessageByURL: (
239 | params: FileMsgParams,
240 | operationID?: string
241 | ) => Promise>;
242 | createMergerMessage: (
243 | params: MergerMsgParams,
244 | operationID?: string
245 | ) => Promise>;
246 | createForwardMessage: (
247 | params: MessageItem,
248 | operationID?: string
249 | ) => Promise>;
250 | createLocationMessage: (
251 | params: LocationMsgParams,
252 | operationID?: string
253 | ) => Promise>;
254 | createQuoteMessage: (
255 | params: QuoteMsgParams,
256 | operationID?: string
257 | ) => Promise>;
258 | createCardMessage: (
259 | params: CardElem,
260 | operationID?: string
261 | ) => Promise>;
262 | createCustomMessage: (
263 | params: CustomMsgParams,
264 | operationID?: string
265 | ) => Promise>;
266 | createFaceMessage: (
267 | params: FaceMessageParams,
268 | operationID?: string
269 | ) => Promise>;
270 | sendMessage: (
271 | params: SendMsgParams,
272 | operationID?: string
273 | ) => Promise>;
274 | sendMessageNotOss: (
275 | params: SendMsgParams,
276 | operationID?: string
277 | ) => Promise>;
278 | typingStatusUpdate: (
279 | params: TypingUpdateParams,
280 | operationID?: string
281 | ) => Promise>;
282 | revokeMessage: (
283 | params: OpreateMessageParams,
284 | operationID?: string
285 | ) => Promise>;
286 | deleteMessage: (
287 | params: OpreateMessageParams,
288 | operationID?: string
289 | ) => Promise>;
290 | deleteMessageFromLocalStorage: (
291 | params: OpreateMessageParams,
292 | operationID?: string
293 | ) => Promise>;
294 | deleteAllMsgFromLocal: (operationID?: string) => Promise>;
295 | deleteAllMsgFromLocalAndSvr: (
296 | operationID?: string
297 | ) => Promise>;
298 | searchLocalMessages: (
299 | params: SearchLocalParams,
300 | operationID?: string
301 | ) => Promise>;
302 | getAdvancedHistoryMessageList: (
303 | params: GetAdvancedHistoryMsgParams,
304 | operationID?: string
305 | ) => Promise>;
306 | getAdvancedHistoryMessageListReverse: (
307 | params: GetAdvancedHistoryMsgParams,
308 | operationID?: string
309 | ) => Promise>;
310 | findMessageList: (
311 | params: FindMessageParams[],
312 | operationID?: string
313 | ) => Promise>;
314 | insertGroupMessageToLocalStorage: (
315 | params: InsertGroupMsgParams,
316 | operationID?: string
317 | ) => Promise>;
318 | insertSingleMessageToLocalStorage: (
319 | params: InsertSingleMsgParams,
320 | operationID?: string
321 | ) => Promise>;
322 | setMessageLocalEx: (
323 | params: SetMessageLocalExParams,
324 | operationID?: string
325 | ) => Promise>;
326 | }
327 |
--------------------------------------------------------------------------------
/src/api/user.ts:
--------------------------------------------------------------------------------
1 | import type {
2 | GetUserInfoWithCacheParams,
3 | SetSelfInfoParams,
4 | } from '@/types/params';
5 | import OpenIMSDK from '.';
6 | import { RequestApi } from '@/constant/api';
7 | import type { MessageReceiveOptType } from '@/types/enum';
8 | import type {
9 | FullUserItemWithCache,
10 | SelfUserInfo,
11 | UserOnlineState,
12 | WsResponse,
13 | } from '@/types/entity';
14 |
15 | export function setupUser(openIMSDK: OpenIMSDK) {
16 | return {
17 | getSelfUserInfo: openIMSDK.createRequestFunctionWithoutParams(
18 | RequestApi.GetSelfUserInfo
19 | ),
20 | setSelfInfo: openIMSDK.createRequestFunction(
21 | RequestApi.SetSelfInfo
22 | ),
23 | getUsersInfoWithCache: openIMSDK.createRequestFunction<
24 | GetUserInfoWithCacheParams,
25 | FullUserItemWithCache[]
26 | >(RequestApi.GetUsersInfoWithCache, data =>
27 | JSON.stringify([JSON.stringify(data.userIDList), data.groupID ?? ''])
28 | ),
29 | subscribeUsersStatus: openIMSDK.createRequestFunction<
30 | string[],
31 | UserOnlineState
32 | >(RequestApi.SubscribeUsersStatus),
33 | unsubscribeUsersStatus: openIMSDK.createRequestFunction(
34 | RequestApi.UnsubscribeUsersStatus
35 | ),
36 | getSubscribeUsersStatus: openIMSDK.createRequestFunctionWithoutParams<
37 | UserOnlineState[]
38 | >(RequestApi.GetSubscribeUsersStatus),
39 | setAppBackgroundStatus: openIMSDK.createRequestFunction(
40 | RequestApi.SetAppBackgroundStatus
41 | ),
42 | networkStatusChanged: openIMSDK.createRequestFunctionWithoutParams(
43 | RequestApi.NetworkStatusChanged
44 | ),
45 | setGlobalRecvMessageOpt:
46 | openIMSDK.createRequestFunction(
47 | RequestApi.SetGlobalRecvMessageOpt
48 | ),
49 | };
50 | }
51 |
52 | export interface UserApi {
53 | getSelfUserInfo: (operationID?: string) => Promise>;
54 | setSelfInfo: (
55 | params: Partial,
56 | operationID?: string
57 | ) => Promise>;
58 | getUsersInfoWithCache: (
59 | params: GetUserInfoWithCacheParams,
60 | operationID?: string
61 | ) => Promise>;
62 | subscribeUsersStatus: (
63 | params: string[],
64 | operationID?: string
65 | ) => Promise>;
66 | unsubscribeUsersStatus: (
67 | params: string[],
68 | operationID?: string
69 | ) => Promise>;
70 | getSubscribeUsersStatus: (
71 | operationID?: string
72 | ) => Promise>;
73 | setAppBackgroundStatus: (
74 | params: boolean,
75 | operationID?: string
76 | ) => Promise>;
77 | networkStatusChanged: (operationID?: string) => Promise>;
78 | setGlobalRecvMessageOpt: (
79 | params: MessageReceiveOptType,
80 | operationID?: string
81 | ) => Promise>;
82 | }
83 |
--------------------------------------------------------------------------------
/src/constant/api.ts:
--------------------------------------------------------------------------------
1 | export enum ErrorCode {
2 | ConnectionEstablishmentFailed = 10000,
3 | ResourceLoadNotCompleteError = 10004,
4 | UnknownError = 10005,
5 | LoginRepeatError = 10102,
6 | }
7 |
8 | export enum RequestApi {
9 | InitSDK = 'InitSDK',
10 | // auth
11 | Login = 'Login',
12 | Logout = 'Logout',
13 | GetLoginStatus = 'GetLoginStatus',
14 | GetLoginUserID = 'GetLoginUserID',
15 |
16 | // user
17 | GetSelfUserInfo = 'GetSelfUserInfo',
18 | SetSelfInfo = 'SetSelfInfo',
19 | GetUsersInfoWithCache = 'GetUsersInfoWithCache',
20 | SubscribeUsersStatus = 'SubscribeUsersStatus',
21 | UnsubscribeUsersStatus = 'UnsubscribeUsersStatus',
22 | GetSubscribeUsersStatus = 'GetSubscribeUsersStatus',
23 | SetAppBackgroundStatus = 'SetAppBackgroundStatus',
24 | NetworkStatusChanged = 'NetworkStatusChanged',
25 | SetGlobalRecvMessageOpt = 'SetGlobalRecvMessageOpt',
26 |
27 | // friend
28 | AcceptFriendApplication = 'AcceptFriendApplication',
29 | AddBlack = 'AddBlack',
30 | AddFriend = 'AddFriend',
31 | CheckFriend = 'CheckFriend',
32 | DeleteFriend = 'DeleteFriend',
33 | GetBlackList = 'GetBlackList',
34 | GetFriendApplicationListAsApplicant = 'GetFriendApplicationListAsApplicant',
35 | GetFriendApplicationListAsRecipient = 'GetFriendApplicationListAsRecipient',
36 | GetFriendList = 'GetFriendList',
37 | GetFriendListPage = 'GetFriendListPage',
38 | GetSpecifiedFriendsInfo = 'GetSpecifiedFriendsInfo',
39 | RefuseFriendApplication = 'RefuseFriendApplication',
40 | RemoveBlack = 'RemoveBlack',
41 | SearchFriends = 'SearchFriends',
42 | SetFriendRemark = 'SetFriendRemark',
43 |
44 | // group
45 | CreateGroup = 'CreateGroup',
46 | JoinGroup = 'JoinGroup',
47 | InviteUserToGroup = 'InviteUserToGroup',
48 | GetJoinedGroupList = 'GetJoinedGroupList',
49 | GetJoinedGroupListPage = 'GetJoinedGroupListPage',
50 | SearchGroups = 'SearchGroups',
51 | GetSpecifiedGroupsInfo = 'GetSpecifiedGroupsInfo',
52 | SetGroupInfo = 'SetGroupInfo',
53 | GetGroupApplicationListAsRecipient = 'GetGroupApplicationListAsRecipient',
54 | GetGroupApplicationListAsApplicant = 'GetGroupApplicationListAsApplicant',
55 | AcceptGroupApplication = 'AcceptGroupApplication',
56 | RefuseGroupApplication = 'RefuseGroupApplication',
57 | GetGroupMemberList = 'GetGroupMemberList',
58 | GetSpecifiedGroupMembersInfo = 'GetSpecifiedGroupMembersInfo',
59 | SearchGroupMembers = 'SearchGroupMembers',
60 | SetGroupMemberInfo = 'SetGroupMemberInfo',
61 | GetGroupMemberOwnerAndAdmin = 'GetGroupMemberOwnerAndAdmin',
62 | GetGroupMemberListByJoinTimeFilter = 'GetGroupMemberListByJoinTimeFilter',
63 | KickGroupMember = 'KickGroupMember',
64 | ChangeGroupMemberMute = 'ChangeGroupMemberMute',
65 | ChangeGroupMute = 'ChangeGroupMute',
66 | TransferGroupOwner = 'TransferGroupOwner',
67 | DismissGroup = 'DismissGroup',
68 | QuitGroup = 'QuitGroup',
69 |
70 | // conversation
71 | GetAllConversationList = 'GetAllConversationList',
72 | GetConversationListSplit = 'GetConversationListSplit',
73 | GetOneConversation = 'GetOneConversation',
74 | GetMultipleConversation = 'GetMultipleConversation',
75 | GetConversationIDBySessionType = 'GetConversationIDBySessionType',
76 | GetTotalUnreadMsgCount = 'GetTotalUnreadMsgCount',
77 | MarkConversationMessageAsRead = 'MarkConversationMessageAsRead',
78 | SetConversationDraft = 'SetConversationDraft',
79 | PinConversation = 'PinConversation',
80 | SetConversationRecvMessageOpt = 'SetConversationRecvMessageOpt',
81 | SetConversationPrivateChat = 'SetConversationPrivateChat',
82 | SetConversationBurnDuration = 'SetConversationBurnDuration',
83 | ResetConversationGroupAtType = 'ResetConversationGroupAtType',
84 | HideConversation = 'HideConversation',
85 | HideAllConversation = 'HideAllConversation',
86 | ClearConversationAndDeleteAllMsg = 'ClearConversationAndDeleteAllMsg',
87 | DeleteConversationAndDeleteAllMsg = 'DeleteConversationAndDeleteAllMsg',
88 |
89 | // message
90 | CreateTextMessage = 'CreateTextMessage',
91 | CreateTextAtMessage = 'CreateTextAtMessage',
92 | CreateImageMessageByFile = 'CreateImageMessageByFile',
93 | CreateImageMessageByURL = 'CreateImageMessageByURL',
94 | CreateSoundMessageByFile = 'CreateSoundMessageByFile',
95 | CreateSoundMessageByURL = 'CreateSoundMessageByURL',
96 | CreateVideoMessageByFile = 'CreateVideoMessageByFile',
97 | CreateVideoMessageByURL = 'CreateVideoMessageByURL',
98 | CreateFileMessageByFile = 'CreateFileMessageByFile',
99 | CreateFileMessageByURL = 'CreateFileMessageByURL',
100 | CreateMergerMessage = 'CreateMergerMessage',
101 | CreateForwardMessage = 'CreateForwardMessage',
102 | CreateLocationMessage = 'CreateLocationMessage',
103 | CreateQuoteMessage = 'CreateQuoteMessage',
104 | CreateCardMessage = 'CreateCardMessage',
105 | CreateCustomMessage = 'CreateCustomMessage',
106 | CreateFaceMessage = 'CreateFaceMessage',
107 | SendMessage = 'SendMessage',
108 | SendMessageNotOss = 'SendMessageNotOss',
109 | UploadFile = 'UploadFile',
110 | TypingStatusUpdate = 'TypingStatusUpdate',
111 | RevokeMessage = 'RevokeMessage',
112 | DeleteMessage = 'DeleteMessage',
113 | DeleteMessageFromLocalStorage = 'DeleteMessageFromLocalStorage',
114 | DeleteAllMsgFromLocal = 'DeleteAllMsgFromLocal',
115 | DeleteAllMsgFromLocalAndSvr = 'DeleteAllMsgFromLocalAndSvr',
116 | SearchLocalMessages = 'SearchLocalMessages',
117 | GetAdvancedHistoryMessageList = 'GetAdvancedHistoryMessageList',
118 | GetAdvancedHistoryMessageListReverse = 'GetAdvancedHistoryMessageListReverse',
119 | FindMessageList = 'FindMessageList',
120 | InsertGroupMessageToLocalStorage = 'InsertGroupMessageToLocalStorage',
121 | InsertSingleMessageToLocalStorage = 'InsertSingleMessageToLocalStorage',
122 | SetMessageLocalEx = 'SetMessageLocalEx',
123 | }
124 |
--------------------------------------------------------------------------------
/src/constant/callback.ts:
--------------------------------------------------------------------------------
1 | export enum CbEvents {
2 | OnConnectFailed = 'OnConnectFailed',
3 | OnConnectSuccess = 'OnConnectSuccess',
4 | OnConnecting = 'OnConnecting',
5 | OnKickedOffline = 'OnKickedOffline',
6 | OnUserTokenInvalid = 'OnUserTokenInvalid',
7 | OnSelfInfoUpdated = 'OnSelfInfoUpdated',
8 | OnUserTokenExpired = 'OnUserTokenExpired',
9 | OnProgress = 'OnProgress',
10 | OnRecvNewMessage = 'OnRecvNewMessage',
11 | OnRecvNewMessages = 'OnRecvNewMessages',
12 | OnRecvMessageRevoked = 'OnRecvMessageRevoked',
13 | OnNewRecvMessageRevoked = 'OnNewRecvMessageRevoked',
14 | OnRecvC2CReadReceipt = 'OnRecvC2CReadReceipt',
15 | OnRecvGroupReadReceipt = 'OnRecvGroupReadReceipt',
16 | OnConversationChanged = 'OnConversationChanged',
17 | OnNewConversation = 'OnNewConversation',
18 | OnSyncServerFailed = 'OnSyncServerFailed',
19 | OnSyncServerFinish = 'OnSyncServerFinish',
20 | OnSyncServerStart = 'OnSyncServerStart',
21 | OnSyncServerProgress = 'OnSyncServerProgress',
22 | OnTotalUnreadMessageCountChanged = 'OnTotalUnreadMessageCountChanged',
23 | OnBlackAdded = 'OnBlackAdded',
24 | OnBlackDeleted = 'OnBlackDeleted',
25 | OnFriendApplicationAccepted = 'OnFriendApplicationAccepted',
26 | OnFriendApplicationAdded = 'OnFriendApplicationAdded',
27 | OnFriendApplicationDeleted = 'OnFriendApplicationDeleted',
28 | OnFriendApplicationRejected = 'OnFriendApplicationRejected',
29 | OnFriendInfoChanged = 'OnFriendInfoChanged',
30 | OnFriendAdded = 'OnFriendAdded',
31 | OnFriendDeleted = 'OnFriendDeleted',
32 | OnJoinedGroupAdded = 'OnJoinedGroupAdded',
33 | OnJoinedGroupDeleted = 'OnJoinedGroupDeleted',
34 | OnGroupDismissed = 'OnGroupDismissed',
35 | OnGroupMemberAdded = 'OnGroupMemberAdded',
36 | OnGroupMemberDeleted = 'OnGroupMemberDeleted',
37 | OnGroupApplicationAdded = 'OnGroupApplicationAdded',
38 | OnGroupApplicationDeleted = 'OnGroupApplicationDeleted',
39 | OnGroupInfoChanged = 'OnGroupInfoChanged',
40 | OnGroupMemberInfoChanged = 'OnGroupMemberInfoChanged',
41 | OnGroupApplicationAccepted = 'OnGroupApplicationAccepted',
42 | OnGroupApplicationRejected = 'OnGroupApplicationRejected',
43 |
44 | UploadComplete = 'UploadComplete',
45 | OnRecvCustomBusinessMessage = 'OnRecvCustomBusinessMessage',
46 | OnUserStatusChanged = 'OnUserStatusChanged',
47 |
48 | // rtc
49 | OnReceiveNewInvitation = 'OnReceiveNewInvitation',
50 | OnInviteeAccepted = 'OnInviteeAccepted',
51 | OnInviteeRejected = 'OnInviteeRejected',
52 | OnInvitationCancelled = 'OnInvitationCancelled',
53 | OnHangUp = 'OnHangUp',
54 | OnInvitationTimeout = 'OnInvitationTimeout',
55 | OnInviteeAcceptedByOtherDevice = 'OnInviteeAcceptedByOtherDevice',
56 | OnInviteeRejectedByOtherDevice = 'OnInviteeRejectedByOtherDevice',
57 |
58 | // meeting
59 | OnStreamChange = 'OnStreamChange',
60 | OnRoomParticipantConnected = 'OnRoomParticipantConnected',
61 | OnRoomParticipantDisconnected = 'OnRoomParticipantDisconnected',
62 | OnReceiveCustomSignal = 'OnReceiveCustomSignal',
63 | }
64 |
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | import OpenIMSDK from './api';
2 | import { CbEvents } from '@/constant/callback';
3 | import { ErrorCode, RequestApi } from '@/constant/api';
4 | export * from '@/types/enum';
5 | export * from '@/types/entity';
6 | export * from '@/types/params';
7 |
8 | export { OpenIMSDK, CbEvents, ErrorCode, RequestApi };
9 |
--------------------------------------------------------------------------------
/src/types/entity.ts:
--------------------------------------------------------------------------------
1 | import { RequestApi } from '@/constant/api';
2 | import type {
3 | AllowType,
4 | ApplicationHandleResult,
5 | GroupAtType,
6 | GroupJoinSource,
7 | GroupMemberRole,
8 | GroupStatus,
9 | GroupType,
10 | GroupVerificationType,
11 | MessageReceiveOptType,
12 | MessageStatus,
13 | MessageType,
14 | OnlineState,
15 | Platform,
16 | Relationship,
17 | SessionType,
18 | } from './enum';
19 | import { CbEvents } from '..';
20 |
21 | export interface WsRequest {
22 | reqFuncName: RequestApi;
23 | userID: string;
24 | data: string;
25 | operationID: string;
26 | }
27 |
28 | export interface WsResponse {
29 | event: RequestApi | CbEvents;
30 | errCode: number;
31 | errMsg: string;
32 | data: T;
33 | operationID: string;
34 | }
35 |
36 | export interface PromiseMap {
37 | resolve: (response: WsResponse) => void;
38 | reject: (response: WsResponse) => void;
39 | }
40 |
41 | export type Pagination = {
42 | offset: number;
43 | count: number;
44 | }
45 |
46 | export type MessageEntity = {
47 | type: string;
48 | offset: number;
49 | length: number;
50 | url?: string;
51 | info?: string;
52 | };
53 | export type PicBaseInfo = {
54 | uuid: string;
55 | type: string;
56 | size: number;
57 | width: number;
58 | height: number;
59 | url: string;
60 | };
61 | export type AtUsersInfoItem = {
62 | atUserID: string;
63 | groupNickname: string;
64 | };
65 | export type GroupApplicationItem = {
66 | createTime: number;
67 | creatorUserID: string;
68 | ex: string;
69 | groupFaceURL: string;
70 | groupID: string;
71 | groupName: string;
72 | groupType: GroupType;
73 | handleResult: ApplicationHandleResult;
74 | handleUserID: string;
75 | handledMsg: string;
76 | handledTime: number;
77 | introduction: string;
78 | memberCount: number;
79 | nickname: string;
80 | notification: string;
81 | ownerUserID: string;
82 | reqMsg: string;
83 | reqTime: number;
84 | joinSource: GroupJoinSource;
85 | status: GroupStatus;
86 | userFaceURL: string;
87 | userID: string;
88 | };
89 | export type FriendApplicationItem = {
90 | createTime: number;
91 | ex: string;
92 | fromFaceURL: string;
93 | fromNickname: string;
94 | fromUserID: string;
95 | handleMsg: string;
96 | handleResult: ApplicationHandleResult;
97 | handleTime: number;
98 | handlerUserID: string;
99 | reqMsg: string;
100 | toFaceURL: string;
101 | toNickname: string;
102 | toUserID: string;
103 | };
104 | export type FullUserItem = {
105 | blackInfo: BlackUserItem | null;
106 | friendInfo: FriendUserItem | null;
107 | publicInfo: PublicUserItem | null;
108 | };
109 | export type FullUserItemWithCache = {
110 | blackInfo: BlackUserItem | null;
111 | friendInfo: FriendUserItem | null;
112 | publicInfo: PublicUserItem | null;
113 | groupMemberInfo: GroupMemberItem | null;
114 | };
115 | export type PublicUserItem = {
116 | nickname: string;
117 | userID: string;
118 | faceURL: string;
119 | ex: string;
120 | };
121 | export type SelfUserInfo = {
122 | createTime: number;
123 | ex: string;
124 | faceURL: string;
125 | nickname: string;
126 | userID: string;
127 | globalRecvMsgOpt: MessageReceiveOptType;
128 | };
129 | export type PartialUserInfo = {
130 | userID: string;
131 | } & Partial>;
132 | export type FriendUserItem = {
133 | addSource: number;
134 | createTime: number;
135 | ex: string;
136 | faceURL: string;
137 | userID: string;
138 | nickname: string;
139 | operatorUserID: string;
140 | ownerUserID: string;
141 | remark: string;
142 | attachedInfo: string;
143 | };
144 | export type SearchedFriendsInfo = FriendUserItem & {
145 | relationship: Relationship;
146 | };
147 | export type FriendshipInfo = {
148 | result: number;
149 | userID: string;
150 | };
151 | export type BlackUserItem = {
152 | addSource: number;
153 | userID: string;
154 | createTime: number;
155 | ex: string;
156 | faceURL: string;
157 | nickname: string;
158 | operatorUserID: string;
159 | ownerUserID: string;
160 | };
161 | export type GroupItem = {
162 | groupID: string;
163 | groupName: string;
164 | notification: string;
165 | notificationUserID: string;
166 | notificationUpdateTime: number;
167 | introduction: string;
168 | faceURL: string;
169 | ownerUserID: string;
170 | createTime: number;
171 | memberCount: number;
172 | status: GroupStatus;
173 | creatorUserID: string;
174 | groupType: GroupType;
175 | needVerification: GroupVerificationType;
176 | ex: string;
177 | applyMemberFriend: AllowType;
178 | lookMemberInfo: AllowType;
179 | };
180 | export type GroupMemberItem = {
181 | groupID: string;
182 | userID: string;
183 | nickname: string;
184 | faceURL: string;
185 | roleLevel: GroupMemberRole;
186 | muteEndTime: number;
187 | joinTime: number;
188 | joinSource: GroupJoinSource;
189 | inviterUserID: string;
190 | operatorUserID: string;
191 | ex: string;
192 | };
193 | export type ConversationItem = {
194 | conversationID: string;
195 | conversationType: SessionType;
196 | userID: string;
197 | groupID: string;
198 | showName: string;
199 | faceURL: string;
200 | recvMsgOpt: MessageReceiveOptType;
201 | unreadCount: number;
202 | groupAtType: GroupAtType;
203 | latestMsg: string;
204 | latestMsgSendTime: number;
205 | draftText: string;
206 | draftTextTime: number;
207 | burnDuration: number;
208 | msgDestructTime: number;
209 | isPinned: boolean;
210 | isNotInGroup: boolean;
211 | isPrivateChat: boolean;
212 | isMsgDestruct: boolean;
213 | attachedInfo: string;
214 | ex: string;
215 | };
216 | export type MessageItem = {
217 | clientMsgID: string;
218 | serverMsgID: string;
219 | createTime: number;
220 | sendTime: number;
221 | sessionType: SessionType;
222 | sendID: string;
223 | recvID: string;
224 | msgFrom: number;
225 | contentType: MessageType;
226 | senderPlatformID: Platform;
227 | senderNickname: string;
228 | senderFaceUrl: string;
229 | groupID: string;
230 | content: string;
231 | seq: number;
232 | isRead: boolean;
233 | status: MessageStatus;
234 | isReact: boolean;
235 | isExternalExtensions: boolean;
236 | offlinePush: OfflinePush;
237 | attachedInfo: string;
238 | ex: string;
239 | localEx: string;
240 | textElem: TextElem;
241 | cardElem: CardElem;
242 | pictureElem: PictureElem;
243 | soundElem: SoundElem;
244 | videoElem: VideoElem;
245 | fileElem: FileElem;
246 | mergeElem: MergeElem;
247 | atTextElem: AtTextElem;
248 | faceElem: FaceElem;
249 | locationElem: LocationElem;
250 | customElem: CustomElem;
251 | quoteElem: QuoteElem;
252 | notificationElem: NotificationElem;
253 | advancedTextElem: AdvancedTextElem;
254 | typingElem: TypingElem;
255 | attachedInfoElem: AttachedInfoElem;
256 | };
257 | export type TextElem = {
258 | content: string;
259 | };
260 | export type CardElem = {
261 | userID: string;
262 | nickname: string;
263 | faceURL: string;
264 | ex: string;
265 | };
266 | export type AtTextElem = {
267 | text: string;
268 | atUserList: string[];
269 | atUsersInfo?: AtUsersInfoItem[];
270 | quoteMessage?: MessageItem;
271 | isAtSelf?: boolean;
272 | };
273 | export type NotificationElem = {
274 | detail: string;
275 | };
276 | export type AdvancedTextElem = {
277 | text: string;
278 | messageEntityList: MessageEntity[];
279 | };
280 | export type TypingElem = {
281 | msgTips: string;
282 | };
283 | export type CustomElem = {
284 | data: string;
285 | description: string;
286 | extension: string;
287 | };
288 | export type FileElem = {
289 | filePath: string;
290 | uuid: string;
291 | sourceUrl: string;
292 | fileName: string;
293 | fileSize: number;
294 | };
295 | export type FaceElem = {
296 | index: number;
297 | data: string;
298 | };
299 | export type LocationElem = {
300 | description: string;
301 | longitude: number;
302 | latitude: number;
303 | };
304 | export type MergeElem = {
305 | title: string;
306 | abstractList: string[];
307 | multiMessage: MessageItem[];
308 | messageEntityList: MessageEntity[];
309 | };
310 | export type OfflinePush = {
311 | title: string;
312 | desc: string;
313 | ex: string;
314 | iOSPushSound: string;
315 | iOSBadgeCount: boolean;
316 | };
317 | export type PictureElem = {
318 | sourcePath: string;
319 | sourcePicture: Picture;
320 | bigPicture: Picture;
321 | snapshotPicture: Picture;
322 | };
323 | export type AttachedInfoElem = {
324 | groupHasReadInfo: GroupHasReadInfo;
325 | isPrivateChat: boolean;
326 | isEncryption: boolean;
327 | inEncryptStatus: boolean;
328 | burnDuration: number;
329 | hasReadTime: number;
330 | notSenderNotificationPush: boolean;
331 | messageEntityList: MessageEntity[];
332 | uploadProgress: UploadProgress;
333 | };
334 | export type UploadProgress = {
335 | total: number;
336 | save: number;
337 | current: number;
338 | };
339 | export type GroupHasReadInfo = {
340 | hasReadCount: number;
341 | unreadCount: number;
342 | hasReadUserIDList: string[];
343 | groupMemberCount: number;
344 | };
345 | export type Picture = {
346 | uuid: string;
347 | type: string;
348 | size: number;
349 | width: number;
350 | height: number;
351 | url: string;
352 | };
353 | export type QuoteElem = {
354 | text: string;
355 | quoteMessage: MessageItem;
356 | };
357 | export type SoundElem = {
358 | uuid: string;
359 | soundPath: string;
360 | sourceUrl: string;
361 | dataSize: number;
362 | duration: number;
363 | };
364 | export type VideoElem = {
365 | videoPath: string;
366 | videoUUID: string;
367 | videoUrl: string;
368 | videoType: string;
369 | videoSize: number;
370 | duration: number;
371 | snapshotPath: string;
372 | snapshotUUID: string;
373 | snapshotSize: number;
374 | snapshotUrl: string;
375 | snapshotWidth: number;
376 | snapshotHeight: number;
377 | };
378 | export type AdvancedRevokeContent = {
379 | clientMsgID: string;
380 | revokeTime: number;
381 | revokerID: string;
382 | revokerNickname: string;
383 | revokerRole: number;
384 | seq: number;
385 | sessionType: SessionType;
386 | sourceMessageSendID: string;
387 | sourceMessageSendTime: number;
388 | sourceMessageSenderNickname: string;
389 | };
390 |
391 | export type RevokedInfo = {
392 | revokerID: string;
393 | revokerRole: number;
394 | clientMsgID: string;
395 | revokerNickname: string;
396 | revokeTime: number;
397 | sourceMessageSendTime: number;
398 | sourceMessageSendID: string;
399 | sourceMessageSenderNickname: string;
400 | sessionType: number;
401 | seq: number;
402 | ex: string;
403 | };
404 |
405 | export type ReceiptInfo = {
406 | userID: string;
407 | groupID: string;
408 | msgIDList: string[];
409 | readTime: number;
410 | msgFrom: number;
411 | contentType: MessageType;
412 | sessionType: SessionType;
413 | };
414 |
415 | export type SearchMessageResult = {
416 | totalCount: number;
417 | searchResultItems?: SearchMessageResultItem[];
418 | findResultItems?: SearchMessageResultItem[];
419 | };
420 |
421 | export type SearchMessageResultItem = {
422 | conversationID: string;
423 | messageCount: number;
424 | conversationType: SessionType;
425 | showName: string;
426 | faceURL: string;
427 | messageList: MessageItem[];
428 | };
429 |
430 | export type AdvancedGetMessageResult = {
431 | isEnd: boolean;
432 | lastMinSeq: number;
433 | errCode: number;
434 | errMsg: string;
435 | messageList: MessageItem[];
436 | };
437 |
438 | export type RtcInvite = {
439 | inviterUserID: string;
440 | inviteeUserIDList: string[];
441 | customData?: string;
442 | groupID: string;
443 | roomID: string;
444 | timeout: number;
445 | mediaType: string;
446 | sessionType: number;
447 | platformID: number;
448 | initiateTime?: number;
449 | busyLineUserIDList?: string[];
450 | };
451 |
452 | export type UserOnlineState = {
453 | platformIDs?: Platform[];
454 | status: OnlineState;
455 | userID: string;
456 | };
457 |
458 | export type GroupMessageReceiptInfo = {
459 | conversationID: string;
460 | groupMessageReadInfo: GroupMessageReadInfo[];
461 | };
462 | export type GroupMessageReadInfo = {
463 | clientMsgID: string;
464 | hasReadCount: number;
465 | unreadCount: number;
466 | readMembers: GroupMemberItem[];
467 | };
468 |
--------------------------------------------------------------------------------
/src/types/enum.ts:
--------------------------------------------------------------------------------
1 | export enum MessageReceiveOptType {
2 | Nomal = 0,
3 | NotReceive = 1,
4 | NotNotify = 2,
5 | }
6 | export enum AllowType {
7 | Allowed = 0,
8 | NotAllowed = 1,
9 | }
10 | export enum GroupType {
11 | Group = 2,
12 | WorkingGroup = 2,
13 | }
14 | export enum GroupJoinSource {
15 | Invitation = 2,
16 | Search = 3,
17 | QrCode = 4,
18 | }
19 | export enum GroupMemberRole {
20 | Nomal = 20,
21 | Admin = 60,
22 | Owner = 100,
23 | }
24 | export enum GroupVerificationType {
25 | ApplyNeedInviteNot = 0,
26 | AllNeed = 1,
27 | AllNot = 2,
28 | }
29 | export enum MessageStatus {
30 | Sending = 1,
31 | Succeed = 2,
32 | Failed = 3,
33 | }
34 | export enum Platform {
35 | iOS = 1,
36 | Android = 2,
37 | Windows = 3,
38 | MacOSX = 4,
39 | Web = 5,
40 | Linux = 7,
41 | AndroidPad = 8,
42 | iPad = 9,
43 | }
44 | export enum LogLevel {
45 | Debug = 5,
46 | Info = 4,
47 | Warn = 3,
48 | Error = 2,
49 | Fatal = 1,
50 | Panic = 0,
51 | }
52 | export enum ApplicationHandleResult {
53 | Unprocessed = 0,
54 | Agree = 1,
55 | Reject = -1,
56 | }
57 | export enum MessageType {
58 | TextMessage = 101,
59 | PictureMessage = 102,
60 | VoiceMessage = 103,
61 | VideoMessage = 104,
62 | FileMessage = 105,
63 | AtTextMessage = 106,
64 | MergeMessage = 107,
65 | CardMessage = 108,
66 | LocationMessage = 109,
67 | CustomMessage = 110,
68 | TypingMessage = 113,
69 | QuoteMessage = 114,
70 | FaceMessage = 115,
71 | FriendAdded = 1201,
72 | OANotification = 1400,
73 |
74 | GroupCreated = 1501,
75 | MemberQuit = 1504,
76 | GroupOwnerTransferred = 1507,
77 | MemberKicked = 1508,
78 | MemberInvited = 1509,
79 | MemberEnter = 1510,
80 | GroupDismissed = 1511,
81 | GroupMemberMuted = 1512,
82 | GroupMemberCancelMuted = 1513,
83 | GroupMuted = 1514,
84 | GroupCancelMuted = 1515,
85 | GroupAnnouncementUpdated = 1519,
86 | GroupNameUpdated = 1520,
87 | BurnMessageChange = 1701,
88 |
89 | // notification
90 | RevokeMessage = 2101,
91 | }
92 | export enum SessionType {
93 | Single = 1,
94 | Group = 3,
95 | WorkingGroup = 3,
96 | Notification = 4,
97 | }
98 | export enum GroupStatus {
99 | Nomal = 0,
100 | Baned = 1,
101 | Dismissed = 2,
102 | Muted = 3,
103 | }
104 | export enum GroupAtType {
105 | AtNormal = 0,
106 | AtMe = 1,
107 | AtAll = 2,
108 | AtAllAtMe = 3,
109 | AtGroupNotice = 4,
110 | }
111 | export enum GroupMemberFilter {
112 | All = 0,
113 | Owner = 1,
114 | Admin = 2,
115 | Nomal = 3,
116 | AdminAndNomal = 4,
117 | AdminAndOwner = 5,
118 | }
119 | export enum Relationship {
120 | isBlack = 0,
121 | isFriend = 1,
122 | }
123 | export enum LoginStatus {
124 | Logout = 1,
125 | Logging = 2,
126 | Logged = 3,
127 | }
128 | export enum OnlineState {
129 | Online = 1,
130 | Offline = 0,
131 | }
132 | export enum GroupMessageReaderFilter {
133 | Readed = 0,
134 | UnRead = 1,
135 | }
136 |
--------------------------------------------------------------------------------
/src/types/params.ts:
--------------------------------------------------------------------------------
1 | import type {
2 | AtUsersInfoItem,
3 | GroupItem,
4 | MessageItem,
5 | OfflinePush,
6 | PicBaseInfo,
7 | SelfUserInfo,
8 | } from './entity';
9 | import type {
10 | GroupJoinSource,
11 | GroupMemberFilter,
12 | GroupMemberRole,
13 | MessageReceiveOptType,
14 | MessageType,
15 | } from './enum';
16 |
17 | export type LoginParams = {
18 | userID: string;
19 | token: string;
20 | wsAddr: string;
21 | apiAddr: string;
22 | platformID: number;
23 | };
24 |
25 | export type SetSelfInfoParams = Partial;
26 |
27 | export type GetUserInfoWithCacheParams = {
28 | userIDList: string[];
29 | groupID?: string;
30 | };
31 |
32 | export type SplitConversationParams = {
33 | offset: number;
34 | count: number;
35 | };
36 |
37 | export type GetOneConversationParams = {
38 | sourceID: string;
39 | sessionType: number;
40 | };
41 |
42 | export type SetConversationDraftParams = {
43 | conversationID: string;
44 | draftText: string;
45 | };
46 |
47 | export type PinConversationParams = {
48 | conversationID: string;
49 | isPinned: boolean;
50 | };
51 |
52 | export type SetConversationRecvOptParams = {
53 | conversationID: string;
54 | opt: MessageReceiveOptType;
55 | };
56 |
57 | export type SetConversationPrivateParams = {
58 | conversationID: string;
59 | isPrivate: boolean;
60 | };
61 |
62 | export type SetBurnDurationParams = {
63 | conversationID: string;
64 | burnDuration: number;
65 | };
66 |
67 | export type AccessFriendParams = {
68 | toUserID: string;
69 | handleMsg: string;
70 | };
71 |
72 | export type AddBlackParams = {
73 | toUserID: string;
74 | ex?: string;
75 | };
76 |
77 | export type SearchFriendParams = {
78 | keywordList: string[];
79 | isSearchUserID: boolean;
80 | isSearchNickname: boolean;
81 | isSearchRemark: boolean;
82 | };
83 |
84 | export type RemarkFriendParams = {
85 | toUserID: string;
86 | remark: string;
87 | };
88 |
89 | export type CreateGroupParams = {
90 | memberUserIDs: string[];
91 | groupInfo: Partial;
92 | adminUserIDs?: string[];
93 | ownerUserID?: string;
94 | };
95 |
96 | export type JoinGroupParams = {
97 | groupID: string;
98 | reqMsg: string;
99 | joinSource: GroupJoinSource;
100 | ex?: string;
101 | };
102 |
103 | export type OpreateGroupParams = {
104 | groupID: string;
105 | reason: string;
106 | userIDList: string[];
107 | };
108 |
109 | export type SearchGroupParams = {
110 | keywordList: string[];
111 | isSearchGroupID: boolean;
112 | isSearchGroupName: boolean;
113 | };
114 |
115 | export type SetGroupinfoParams = Partial & { groupID: string };
116 |
117 | export type AccessGroupParams = {
118 | groupID: string;
119 | fromUserID: string;
120 | handleMsg: string;
121 | };
122 |
123 | export declare type GetGroupMemberParams = {
124 | groupID: string;
125 | filter: GroupMemberFilter;
126 | offset: number;
127 | count: number;
128 | };
129 |
130 | export type getGroupMembersInfoParams = {
131 | groupID: string;
132 | userIDList: string[];
133 | };
134 |
135 | export type SearchGroupMemberParams = {
136 | groupID: string;
137 | keywordList: string[];
138 | isSearchUserID: boolean;
139 | isSearchMemberNickname: boolean;
140 | offset: number;
141 | count: number;
142 | };
143 |
144 | export type UpdateMemberInfoParams = {
145 | groupID: string;
146 | userID: string;
147 | nickname?: string;
148 | faceURL?: string;
149 | roleLevel?: GroupMemberRole;
150 | ex?: string;
151 | };
152 |
153 | export type GetGroupMemberByTimeParams = {
154 | groupID: string;
155 | filterUserIDList: string[];
156 | offset: number;
157 | count: number;
158 | joinTimeBegin: number;
159 | joinTimeEnd: number;
160 | };
161 |
162 | export type ChangeGroupMemberMuteParams = {
163 | groupID: string;
164 | userID: string;
165 | mutedSeconds: number;
166 | };
167 |
168 | export type ChangeGroupMuteParams = {
169 | groupID: string;
170 | isMute: boolean;
171 | };
172 |
173 | export type TransferGroupParams = {
174 | groupID: string;
175 | newOwnerUserID: string;
176 | };
177 |
178 | export type AtMsgParams = {
179 | text: string;
180 | atUserIDList: string[];
181 | atUsersInfo?: AtUsersInfoItem[];
182 | message?: MessageItem;
183 | };
184 |
185 | export type ImageMsgParams = {
186 | sourcePicture: PicBaseInfo;
187 | bigPicture: PicBaseInfo;
188 | snapshotPicture: PicBaseInfo;
189 | sourcePath: string;
190 | };
191 |
192 | export type SoundMsgParams = {
193 | uuid: string;
194 | soundPath: string;
195 | sourceUrl: string;
196 | dataSize: number;
197 | duration: number;
198 | soundType?: string;
199 | };
200 |
201 | export type VideoMsgParams = {
202 | videoPath: string;
203 | duration: number;
204 | videoType: string;
205 | snapshotPath: string;
206 | videoUUID: string;
207 | videoUrl: string;
208 | videoSize: number;
209 | snapshotUUID: string;
210 | snapshotSize: number;
211 | snapshotUrl: string;
212 | snapshotWidth: number;
213 | snapshotHeight: number;
214 | snapShotType?: string;
215 | };
216 |
217 | export type FileMsgParams = {
218 | filePath: string;
219 | fileName: string;
220 | uuid: string;
221 | sourceUrl: string;
222 | fileSize: number;
223 | fileType?: string;
224 | };
225 |
226 | export type MergerMsgParams = {
227 | messageList: MessageItem[];
228 | title: string;
229 | summaryList: string[];
230 | };
231 |
232 | export type LocationMsgParams = {
233 | description: string;
234 | longitude: number;
235 | latitude: number;
236 | };
237 |
238 | export type QuoteMsgParams = {
239 | text: string;
240 | message: string;
241 | };
242 |
243 | export type CustomMsgParams = {
244 | data: string;
245 | extension: string;
246 | description: string;
247 | };
248 |
249 | export type FaceMessageParams = {
250 | index: number;
251 | data: string;
252 | };
253 |
254 | export type SendMsgParams = {
255 | recvID: string;
256 | groupID: string;
257 | offlinePushInfo?: OfflinePush;
258 | message: MessageItem;
259 | isOnlineOnly?: boolean;
260 | };
261 |
262 | export type TypingUpdateParams = {
263 | recvID: string;
264 | msgTip: string;
265 | };
266 |
267 | export type OpreateMessageParams = {
268 | conversationID: string;
269 | clientMsgID: string;
270 | };
271 |
272 | export type SearchLocalParams = {
273 | conversationID: string;
274 | keywordList: string[];
275 | keywordListMatchType?: number;
276 | senderUserIDList?: string[];
277 | messageTypeList?: MessageType[];
278 | searchTimePosition?: number;
279 | searchTimePeriod?: number;
280 | pageIndex?: number;
281 | count?: number;
282 | };
283 |
284 | export type GetAdvancedHistoryMsgParams = {
285 | userID?: string;
286 | groupID?: string;
287 | lastMinSeq: number;
288 | count: number;
289 | startClientMsgID: string;
290 | conversationID: string;
291 | };
292 |
293 | export type FindMessageParams = {
294 | conversationID: string;
295 | clientMsgIDList: string[];
296 | };
297 |
298 | export type InsertGroupMsgParams = {
299 | message: MessageItem;
300 | groupID: string;
301 | sendID: string;
302 | };
303 |
304 | export type InsertSingleMsgParams = {
305 | message: MessageItem;
306 | recvID: string;
307 | sendID: string;
308 | };
309 |
310 | export type SetMessageLocalExParams = {
311 | conversationID: string;
312 | clientMsgID: string;
313 | localEx: string;
314 | };
315 |
316 | export declare type UploadFileParams = {
317 | name?: string;
318 | contentType?: string;
319 | uuid?: string;
320 | file: File;
321 | };
322 |
--------------------------------------------------------------------------------
/src/types/upload.ts:
--------------------------------------------------------------------------------
1 | export interface CommonOptions {
2 | operationID: string;
3 | token: string;
4 | }
5 |
6 | export interface UploadParams {
7 | hash: string;
8 | size: number;
9 | partSize: number;
10 | maxParts: number;
11 | cause: string;
12 | name: string;
13 | contentType: string;
14 | }
15 |
16 | export interface ConfirmData {
17 | uploadID: string;
18 | parts: string[];
19 | cause: string;
20 | name: string;
21 | contentType: string;
22 | }
23 |
24 | export interface UploadData {
25 | url: string;
26 | upload: Upload;
27 | }
28 |
29 | export interface Upload {
30 | uploadID: string;
31 | partSize: number;
32 | sign: Sign;
33 | }
34 |
35 | export interface Sign {
36 | url: string;
37 | query?: KeyForValueList[];
38 | header?: KeyForValueList[];
39 | parts: Part[];
40 | }
41 |
42 | export interface Part {
43 | partNumber: number;
44 | url: string;
45 | query?: KeyForValueList[];
46 | header?: KeyForValueList[];
47 | }
48 |
49 | export interface KeyForValueList {
50 | key: string;
51 | values: string[];
52 | }
53 |
--------------------------------------------------------------------------------
/src/utils/emitter.ts:
--------------------------------------------------------------------------------
1 | import { CbEvents } from '@/constant/callback';
2 | import type { WsResponse } from '@/types/entity';
3 |
4 | interface Events {
5 | [key: string]: Cbfn[];
6 | }
7 |
8 | type Cbfn = (data: WsResponse) => void;
9 |
10 | class Emitter {
11 | private events: Events;
12 |
13 | constructor() {
14 | this.events = {};
15 | }
16 |
17 | emit(event: CbEvents, data: WsResponse) {
18 | if (this.events[event]) {
19 | this.events[event].forEach(fn => {
20 | return fn(data);
21 | });
22 | }
23 |
24 | return this;
25 | }
26 |
27 | on(event: CbEvents, fn: Cbfn) {
28 | if (this.events[event]) {
29 | this.events[event].push(fn);
30 | } else {
31 | this.events[event] = [fn];
32 | }
33 |
34 | return this;
35 | }
36 |
37 | off(event: CbEvents, fn: Cbfn) {
38 | if (event && typeof fn === 'function' && this.events[event]) {
39 | const listeners = this.events[event];
40 | if (!listeners || listeners.length === 0) {
41 | return;
42 | }
43 | const index = listeners.findIndex(_fn => {
44 | return _fn === fn;
45 | });
46 | if (index !== -1) {
47 | listeners.splice(index, 1);
48 | }
49 | }
50 |
51 | return this;
52 | }
53 | }
54 |
55 | export default Emitter;
56 |
--------------------------------------------------------------------------------
/src/utils/textCoder.ts:
--------------------------------------------------------------------------------
1 | export function utf8Encode(str: string): ArrayBuffer {
2 | if (typeof TextEncoder !== 'undefined') {
3 | return new TextEncoder().encode(str);
4 | }
5 |
6 | const bytes: number[] = [];
7 | let c: number;
8 |
9 | for (let i = 0; i < str.length; i++) {
10 | c = str.charCodeAt(i);
11 | if (c >= 0x010000 && c <= 0x10ffff) {
12 | bytes.push(((c >> 18) & 0x07) | 0xf0);
13 | bytes.push(((c >> 12) & 0x3f) | 0x80);
14 | bytes.push(((c >> 6) & 0x3f) | 0x80);
15 | bytes.push((c & 0x3f) | 0x80);
16 | } else if (c >= 0x000800 && c <= 0x00ffff) {
17 | bytes.push(((c >> 12) & 0x0f) | 0xe0);
18 | bytes.push(((c >> 6) & 0x3f) | 0x80);
19 | bytes.push((c & 0x3f) | 0x80);
20 | } else if (c >= 0x000080 && c <= 0x0007ff) {
21 | bytes.push(((c >> 6) & 0x1f) | 0xc0);
22 | bytes.push((c & 0x3f) | 0x80);
23 | } else {
24 | bytes.push(c & 0xff);
25 | }
26 | }
27 |
28 | return new Uint8Array(bytes).buffer;
29 | }
30 |
31 | export function utf8Decode(buffer: ArrayBuffer): string {
32 | if (typeof TextDecoder !== 'undefined') {
33 | return new TextDecoder().decode(buffer);
34 | }
35 |
36 | const dataView = new DataView(buffer);
37 | const bytes = new Uint8Array(buffer.byteLength);
38 | for (let i = 0; i < bytes.length; i++) {
39 | bytes[i] = dataView.getUint8(i);
40 | }
41 |
42 | let str = '';
43 | for (let i = 0; i < bytes.length; i++) {
44 | const one = bytes[i].toString(2);
45 | const v = one.match(/^1+?(?=0)/);
46 | if (v && one.length === 8) {
47 | const bytesLength = v[0].length;
48 | let store = bytes[i].toString(2).slice(7 - bytesLength);
49 | for (let st = 1; st < bytesLength; st++) {
50 | store += bytes[st + i].toString(2).slice(2);
51 | }
52 | str += String.fromCharCode(parseInt(store, 2));
53 | i += bytesLength - 1;
54 | } else {
55 | str += String.fromCharCode(bytes[i]);
56 | }
57 | }
58 |
59 | return str;
60 | }
61 |
--------------------------------------------------------------------------------
/src/utils/upload.ts:
--------------------------------------------------------------------------------
1 | import {
2 | CommonOptions,
3 | ConfirmData,
4 | UploadData,
5 | UploadParams,
6 | } from '@/types/upload';
7 |
8 | // api
9 | const handleResponse = async (res: Response) => {
10 | if (!res.ok) {
11 | throw new Error(res.statusText);
12 | }
13 | const data = await res.json();
14 | if (data.errCode !== 0) {
15 | throw new Error(data.errMsg);
16 | }
17 | return data.data;
18 | };
19 |
20 | export const getUploadPartsize = (
21 | baseUrl: string,
22 | size: number,
23 | commonOptions: CommonOptions
24 | ): Promise<{ size: number }> =>
25 | fetch(`${baseUrl}/object/part_size`, {
26 | method: 'POST',
27 | headers: {
28 | ...commonOptions,
29 | },
30 | body: JSON.stringify({
31 | size,
32 | }),
33 | }).then(handleResponse);
34 |
35 | export const getUploadUrl = (
36 | baseUrl: string,
37 | params: UploadParams,
38 | commonOptions: CommonOptions
39 | ): Promise =>
40 | fetch(`${baseUrl}/object/initiate_multipart_upload`, {
41 | method: 'POST',
42 | headers: {
43 | ...commonOptions,
44 | },
45 | body: JSON.stringify(params),
46 | }).then(handleResponse);
47 |
48 | export const confirmUpload = (
49 | baseUrl: string,
50 | params: ConfirmData,
51 | commonOptions: CommonOptions
52 | ): Promise<{ url: string }> =>
53 | fetch(`${baseUrl}/object/complete_multipart_upload`, {
54 | method: 'POST',
55 | headers: {
56 | ...commonOptions,
57 | },
58 | body: JSON.stringify(params),
59 | }).then(handleResponse);
60 |
61 | // common
62 | const mimeTypesMap: Record = {
63 | txt: 'text/plain',
64 | html: 'text/html',
65 | css: 'text/css',
66 | js: 'text/javascript',
67 | json: 'application/json',
68 | csv: 'text/csv',
69 | jpg: 'image/jpeg',
70 | jpeg: 'image/jpeg',
71 | png: 'image/png',
72 | gif: 'image/gif',
73 | bmp: 'image/bmp',
74 | svg: 'image/svg+xml',
75 | mp3: 'audio/mpeg',
76 | mp4: 'video/mp4',
77 | wav: 'audio/wav',
78 | pdf: 'application/pdf',
79 | doc: 'application/msword',
80 | docx: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
81 | xls: 'application/vnd.ms-excel',
82 | xlsx: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
83 | ppt: 'application/vnd.ms-powerpoint',
84 | pptx: 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
85 | xml: 'application/xml',
86 | zip: 'application/zip',
87 | tar: 'application/x-tar',
88 | '7z': 'application/x-7z-compressed',
89 | rar: 'application/vnd.rar',
90 | ogg: 'audio/ogg',
91 | midi: 'audio/midi',
92 | webm: 'audio/webm',
93 | avi: 'video/x-msvideo',
94 | mpeg: 'video/mpeg',
95 | ts: 'video/mp2t',
96 | mov: 'video/quicktime',
97 | wmv: 'video/x-ms-wmv',
98 | flv: 'video/x-flv',
99 | mkv: 'video/x-matroska',
100 | webp: 'image/webp',
101 | heic: 'image/heic',
102 | psd: 'image/vnd.adobe.photoshop',
103 | ai: 'application/postscript',
104 | eps: 'application/postscript',
105 | ttf: 'font/ttf',
106 | otf: 'font/otf',
107 | woff: 'font/woff',
108 | woff2: 'font/woff2',
109 | jsonld: 'application/ld+json',
110 | ics: 'text/calendar',
111 | sh: 'application/x-sh',
112 | php: 'application/x-httpd-php',
113 | jar: 'application/java-archive',
114 | };
115 |
116 | export const getMimeType = (fileName: string) => {
117 | const extension = fileName.split('.').pop()?.toLowerCase() ?? '';
118 | return mimeTypesMap[extension] || 'application/octet-stream';
119 | };
120 |
--------------------------------------------------------------------------------
/src/utils/uuid.ts:
--------------------------------------------------------------------------------
1 | export const uuid = (): string =>
2 | (Math.random() * 36).toString(36).slice(2) + new Date().getTime().toString();
3 |
--------------------------------------------------------------------------------
/src/utils/webSocketManager.ts:
--------------------------------------------------------------------------------
1 | import type { WsRequest, WsResponse } from '@/types/entity';
2 | import { utf8Decode, utf8Encode } from './textCoder';
3 | import { RequestApi } from '@/constant/api';
4 |
5 | type AppPlatform = 'unknow' | 'web' | 'uni' | 'wx' |'my';
6 |
7 | enum WsOpenState {
8 | CONNECTING = 0,
9 | OPEN = 1,
10 | CLOSING = 2,
11 | CLOSED = 3,
12 | }
13 |
14 | class WebSocketManager {
15 | private ws?: WebSocket;
16 | private url: string;
17 | private reconnectInterval: number; // ms
18 | private maxReconnectAttempts: number;
19 | private reconnectAttempts: number;
20 | private shouldReconnect: boolean;
21 | private isProcessingMessage: boolean = false;
22 | private platformNamespace: AppPlatform;
23 |
24 | constructor(
25 | url: string,
26 | private onMessage: (data: WsResponse) => void,
27 | private onReconnectSuccess: () => void,
28 | reconnectInterval = 5000,
29 | maxReconnectAttempts = 10
30 | ) {
31 | this.url = url;
32 | this.reconnectInterval = reconnectInterval;
33 | this.maxReconnectAttempts = maxReconnectAttempts;
34 | this.reconnectAttempts = 0;
35 | this.shouldReconnect = false;
36 | this.platformNamespace = this.checkPlatform();
37 | }
38 |
39 | private checkPlatform = () => {
40 | if (typeof WebSocket !== 'undefined') {
41 | return 'web';
42 | }
43 | // my优先于uni 其onBinaryMessage中返回值相对于其他较为特殊
44 | // @ts-ignore
45 | if (typeof my !== 'undefined') {
46 | return 'my';
47 | }
48 | // @ts-ignore
49 | if (typeof uni !== 'undefined') {
50 | return 'uni';
51 | }
52 | // @ts-ignore
53 | if (typeof wx !== 'undefined') {
54 | return 'wx';
55 | }
56 |
57 | return 'unknow';
58 | };
59 |
60 | public connect = (): Promise => {
61 | if (this.platformNamespace === 'unknow') {
62 | return Promise.reject(new Error('WebSocket is not supported'));
63 | }
64 | return new Promise((resolve, reject) => {
65 | if (!this.ws || this.ws.readyState === WsOpenState.CLOSED) {
66 | const onWsOpen = () => {
67 | if (this.reconnectAttempts) {
68 | this.onReconnectSuccess();
69 | }
70 | this.reconnectAttempts = 0;
71 | resolve();
72 | };
73 | const onWsError = (event: Event) => reject(event);
74 | if (this.platformNamespace === 'web') {
75 | this.ws = new WebSocket(this.url);
76 | this.ws.onopen = onWsOpen;
77 | this.ws.onerror = onWsError;
78 | } else {
79 | const connectOptions = {
80 | url: this.url,
81 | complete: () => {},
82 | };
83 | // @ts-ignore
84 | my && (connectOptions.multiple = true)
85 | if (this.platformNamespace === 'uni') {
86 | // @ts-ignore
87 | this.ws = uni.connectSocket(connectOptions);
88 | }
89 | if (this.platformNamespace === 'wx') {
90 | // @ts-ignore
91 | this.ws = wx.connectSocket(connectOptions);
92 | }
93 | if (this.platformNamespace === 'my') {
94 | // @ts-ignore
95 | this.ws = my.connectSocket(connectOptions);
96 | }
97 | // @ts-ignore
98 | this.ws.onOpen(onWsOpen);
99 | // @ts-ignore
100 | this.ws.onError(onWsError);
101 | }
102 |
103 | this.setupEventListeners();
104 | } else if (this.ws.readyState === this.ws.OPEN) {
105 | resolve();
106 | } else {
107 | reject(new Error('WebSocket is in an unknown state'));
108 | }
109 | });
110 | };
111 |
112 | private setupEventListeners = () => {
113 | if (!this.ws) return;
114 |
115 | const onWsMessage = (event: MessageEvent) =>
116 | this.onBinaryMessage(event.data);
117 | const onWsClose = (event?: CloseEvent) => {
118 | if (
119 | this.shouldReconnect &&
120 | this.reconnectAttempts < this.maxReconnectAttempts
121 | ) {
122 | if (this.isProcessingMessage) {
123 | setTimeout(() => onWsClose(), 100);
124 | return;
125 | }
126 | setTimeout(() => this.connect(), this.reconnectInterval);
127 | this.reconnectAttempts++;
128 | }
129 | };
130 |
131 | if (this.platformNamespace === 'web') {
132 | this.ws.onmessage = onWsMessage;
133 | this.ws.onclose = onWsClose;
134 | } else {
135 | // @ts-ignore
136 | this.ws.onMessage(onWsMessage);
137 | // @ts-ignore
138 | this.ws.onClose(onWsClose);
139 | }
140 | };
141 |
142 | private onBinaryMessage = async (message: string|{data:string}) => {
143 | this.isProcessingMessage = true;
144 | // if (this.platformNamespace === 'web' && data instanceof Blob) {
145 | // data = await data.arrayBuffer();
146 | // }
147 | // const message = utf8Decode(data);
148 | if(typeof message !== 'string' && this.platformNamespace === 'my'){
149 | message = message.data
150 | }
151 | const json: WsResponse = JSON.parse(message as string)
152 | // const json: WsResponse = JSON.parse(message);
153 | this.onMessage(json);
154 | if (json.event === RequestApi.Login && json.errCode === 0) {
155 | this.shouldReconnect = true;
156 | }
157 | this.isProcessingMessage = false;
158 | };
159 |
160 | private encodeMessage = (messageObject: WsRequest): ArrayBuffer => {
161 | const messageString = JSON.stringify(messageObject);
162 | return utf8Encode(messageString);
163 | };
164 |
165 | public sendMessage = (message: WsRequest) => {
166 | if (this.ws?.readyState === WsOpenState.OPEN) {
167 | if (this.platformNamespace === 'web') {
168 | this.ws.send(JSON.stringify(message));
169 | } else {
170 | this.ws.send({
171 | //@ts-ignore
172 | data: JSON.stringify(message),
173 | });
174 | }
175 | } else {
176 | console.error('WebSocket is not open. Message not sent.');
177 | }
178 | };
179 |
180 | public close = () => {
181 | this.shouldReconnect = false;
182 | if (this.ws?.readyState === WsOpenState.OPEN) {
183 | this.ws.close();
184 | }
185 | };
186 | }
187 |
188 | export default WebSocketManager;
189 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "esnext",
4 | "module": "esnext",
5 | "declaration": true,
6 | "outDir": "lib",
7 | "strict": true,
8 | "moduleResolution": "node",
9 | "esModuleInterop": true,
10 | "skipLibCheck": true,
11 | "forceConsistentCasingInFileNames": true,
12 | "baseUrl": "./",
13 | "paths": {
14 | "@/*": ["src/*"]
15 | }
16 | },
17 | "include": ["src/**/*.ts"]
18 | }
19 |
--------------------------------------------------------------------------------