mirror of
https://github.com/NapNeko/NapCatQQ.git
synced 2025-07-19 12:03:37 +00:00
Compare commits
296 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
1070278eaf | ||
![]() |
16b1a6b153 | ||
![]() |
096a7534e0 | ||
![]() |
b0897187d2 | ||
![]() |
885d94882d | ||
![]() |
11a3341e13 | ||
![]() |
231890f78a | ||
![]() |
a272feda6a | ||
![]() |
bc936a0ca7 | ||
![]() |
28030c2d13 | ||
![]() |
7fe9176286 | ||
![]() |
1105f9b8d6 | ||
![]() |
e4653defa8 | ||
![]() |
1c62a1e839 | ||
![]() |
3a3bbfe201 | ||
![]() |
1c38833998 | ||
![]() |
38894177ee | ||
![]() |
dce8416942 | ||
![]() |
14219e9b42 | ||
![]() |
7b459e7502 | ||
![]() |
31824c0504 | ||
![]() |
e203abae85 | ||
![]() |
faf83b680b | ||
![]() |
67dcbcb842 | ||
![]() |
6533a25404 | ||
![]() |
4dc760b0e9 | ||
![]() |
25933b9043 | ||
![]() |
a53aaa456e | ||
![]() |
e8a7ea07a5 | ||
![]() |
8817dc6b10 | ||
![]() |
491ec04b46 | ||
![]() |
8a5d4a683b | ||
![]() |
dfc7c7357a | ||
![]() |
690a2f7d34 | ||
![]() |
58f22b24e4 | ||
![]() |
3cce9f528b | ||
![]() |
20fd5ac8cb | ||
![]() |
9e05e086eb | ||
![]() |
056e0adddf | ||
![]() |
b36388200d | ||
![]() |
9793e5741a | ||
![]() |
143380c012 | ||
![]() |
4b92254945 | ||
![]() |
f9c1d8b4a6 | ||
![]() |
c0c469339b | ||
![]() |
0ca6343ed7 | ||
![]() |
3db74c3427 | ||
![]() |
48d5cb53bd | ||
![]() |
fd7d2dbf53 | ||
![]() |
6609697752 | ||
![]() |
dcd6e1973e | ||
![]() |
3614a6e932 | ||
![]() |
931a0210e5 | ||
![]() |
f9e7de4b42 | ||
![]() |
8e0b79594e | ||
![]() |
17122c4360 | ||
![]() |
154f7b6a30 | ||
![]() |
52e5543d0b | ||
![]() |
3c304bd2ae | ||
![]() |
26609bb8fd | ||
![]() |
de3fa9aaa4 | ||
![]() |
788665f84c | ||
![]() |
3943782971 | ||
![]() |
8f899c40f2 | ||
![]() |
a1f582399e | ||
![]() |
440b63f662 | ||
![]() |
7d2cc3b56b | ||
![]() |
5fe3422469 | ||
![]() |
6c02cedb1e | ||
![]() |
3cc2f1dcad | ||
![]() |
773cdc5877 | ||
![]() |
361a7329d7 | ||
![]() |
29910f1236 | ||
![]() |
4a164016f5 | ||
![]() |
cebd3e62a4 | ||
![]() |
2562a38fa1 | ||
![]() |
d46c922bbf | ||
![]() |
66b59982f7 | ||
![]() |
ad397ccf7f | ||
![]() |
bdef80ede7 | ||
![]() |
385dcbc75a | ||
![]() |
74cf501c8f | ||
![]() |
0c200d6748 | ||
![]() |
e65a36c517 | ||
![]() |
126b54ad40 | ||
![]() |
78637751af | ||
![]() |
f96526ee3a | ||
![]() |
b3c7a91f3d | ||
![]() |
b8daeef0c4 | ||
![]() |
2b662944cf | ||
![]() |
3d516df01e | ||
![]() |
26b4a9b15b | ||
![]() |
0f8af273ae | ||
![]() |
fa29a31da9 | ||
![]() |
9e0d2606d8 | ||
![]() |
338dedd6e0 | ||
![]() |
1f893b1393 | ||
![]() |
b783d6f928 | ||
![]() |
8ca0d40f05 | ||
![]() |
2f40a80434 | ||
![]() |
812d8eb5bb | ||
![]() |
bf5f548349 | ||
![]() |
ebf90e72b9 | ||
![]() |
31d7d42edf | ||
![]() |
833875b42f | ||
![]() |
b901c10f3c | ||
![]() |
8ab678bd97 | ||
![]() |
55d5072f46 | ||
![]() |
a379ffd0f2 | ||
![]() |
4501d73134 | ||
![]() |
5f15774ec7 | ||
![]() |
c9e057599e | ||
![]() |
66851f5625 | ||
![]() |
b33c235b4d | ||
![]() |
d6693b6114 | ||
![]() |
36b4d26c78 | ||
![]() |
617592d90a | ||
![]() |
15a77b8070 | ||
![]() |
606eccd22b | ||
![]() |
5613450313 | ||
![]() |
c59b5564af | ||
![]() |
330b086b8b | ||
![]() |
9837ef4f36 | ||
![]() |
add46b3251 | ||
![]() |
e169199107 | ||
![]() |
92fe654850 | ||
![]() |
b257486404 | ||
![]() |
bdf2e33f40 | ||
![]() |
224d361923 | ||
![]() |
3452fa56df | ||
![]() |
cd256235da | ||
![]() |
361a164f2a | ||
![]() |
0e60d4b198 | ||
![]() |
67b47e39b4 | ||
![]() |
8f54310f63 | ||
![]() |
c7a7494d7e | ||
![]() |
af88b3166d | ||
![]() |
b7837b2a14 | ||
![]() |
950ddc749e | ||
![]() |
df081ef0cf | ||
![]() |
7b24f90d9f | ||
![]() |
f2e4579fd8 | ||
![]() |
97cb351827 | ||
![]() |
c1ec53fdbb | ||
![]() |
98214aa429 | ||
![]() |
ce7deac2dd | ||
![]() |
612092b867 | ||
![]() |
92579d5949 | ||
![]() |
9ab07060ae | ||
![]() |
0d45125d79 | ||
![]() |
9ced152778 | ||
![]() |
3685ab2e3e | ||
![]() |
be605f11f2 | ||
![]() |
8cca8df976 | ||
![]() |
990a31e961 | ||
![]() |
5db201c342 | ||
![]() |
a625e30dd4 | ||
![]() |
b236cdd060 | ||
![]() |
2db9899184 | ||
![]() |
fe5d6db986 | ||
![]() |
7c7bf8fecf | ||
![]() |
76e3a46378 | ||
![]() |
16f3897fec | ||
![]() |
045e120854 | ||
![]() |
2b7fcce9b2 | ||
![]() |
9685931694 | ||
![]() |
1dc844435a | ||
![]() |
18892379de | ||
![]() |
620d61c8dc | ||
![]() |
9f91398875 | ||
![]() |
a29b1154a9 | ||
![]() |
34d19a471a | ||
![]() |
2ef6477d7c | ||
![]() |
26e6800836 | ||
![]() |
9dbbcf3872 | ||
![]() |
6b3343e1e4 | ||
![]() |
ef48f754a5 | ||
![]() |
3be1ede847 | ||
![]() |
7bff1b61e8 | ||
![]() |
6affd0eb68 | ||
![]() |
0a112d15e0 | ||
![]() |
4e03f582bb | ||
![]() |
8f186c1c5e | ||
![]() |
cd1bae9a1f | ||
![]() |
60796c26ca | ||
![]() |
28927f950d | ||
![]() |
95f16ebc8c | ||
![]() |
25bca8385d | ||
![]() |
965c7f23b4 | ||
![]() |
33082af9cc | ||
![]() |
1f7f3565b0 | ||
![]() |
f784363696 | ||
![]() |
37bd51e138 | ||
![]() |
c367728c43 | ||
![]() |
61f0f5d884 | ||
![]() |
5f2ebeead7 | ||
![]() |
7646037fc7 | ||
![]() |
eac6d285ff | ||
![]() |
b921d5e734 | ||
![]() |
831d808e63 | ||
![]() |
451b88d7e3 | ||
![]() |
f916682a71 | ||
![]() |
2d76bcf0cf | ||
![]() |
8db294efe6 | ||
![]() |
5cc5149aed | ||
![]() |
7ecb01dc9f | ||
![]() |
8bf1a545d9 | ||
![]() |
efb2be2f94 | ||
![]() |
bd2edda494 | ||
![]() |
991172eae4 | ||
![]() |
fc7631f9aa | ||
![]() |
a21efb7d2f | ||
![]() |
03bc844ad0 | ||
![]() |
f7bdc35ed6 | ||
![]() |
0efdffd857 | ||
![]() |
6a16a42d0c | ||
![]() |
e9c00c72b1 | ||
![]() |
ab22f36b8a | ||
![]() |
c57497cd91 | ||
![]() |
ba123236e5 | ||
![]() |
338b6e4607 | ||
![]() |
f88c717560 | ||
![]() |
f8ffc92db5 | ||
![]() |
98c23c172c | ||
![]() |
781c107d8c | ||
![]() |
186668c075 | ||
![]() |
cf9f785193 | ||
![]() |
72d2d3f224 | ||
![]() |
087c76b394 | ||
![]() |
4f9fb2c8c3 | ||
![]() |
334e43e764 | ||
![]() |
7843256402 | ||
![]() |
0522ba35fe | ||
![]() |
24d3b52e0b | ||
![]() |
3177110f0f | ||
![]() |
e1b8243a67 | ||
![]() |
b1c6ce3885 | ||
![]() |
0b4b25a11e | ||
![]() |
1176fe984a | ||
![]() |
6ca768c3ee | ||
![]() |
3da1659c8d | ||
![]() |
9aa4cd319c | ||
![]() |
5af866cdca | ||
![]() |
2b421fa447 | ||
![]() |
30ec964325 | ||
![]() |
714d7d72eb | ||
![]() |
687aa0f363 | ||
![]() |
8363ab07a7 | ||
![]() |
c46a757339 | ||
![]() |
557864395b | ||
![]() |
3f7a85d80b | ||
![]() |
8d18d2ce1f | ||
![]() |
7141ba1587 | ||
![]() |
44d350a225 | ||
![]() |
239b8e72d9 | ||
![]() |
279bdb6fb1 | ||
![]() |
a0cea819da | ||
![]() |
9ab7f60544 | ||
![]() |
aaf7191bf3 | ||
![]() |
628c9be0c8 | ||
![]() |
733052720c | ||
![]() |
a10f007194 | ||
![]() |
6fa50c58d3 | ||
![]() |
c54a58d6e4 | ||
![]() |
34cb1ea3fd | ||
![]() |
f640b0ca91 | ||
![]() |
60e16da42e | ||
![]() |
0cdceb95d6 | ||
![]() |
70bd22d925 | ||
![]() |
82462dd647 | ||
![]() |
c0466e943d | ||
![]() |
b187b4695d | ||
![]() |
b1956d2a37 | ||
![]() |
590b622e5f | ||
![]() |
3d8174396a | ||
![]() |
b8dc6e9bd9 | ||
![]() |
8ee99109dc | ||
![]() |
902041d4ee | ||
![]() |
cc34aef47e | ||
![]() |
0afbbe7c7a | ||
![]() |
8004553ba7 | ||
![]() |
0023b2846a | ||
![]() |
34775c1816 | ||
![]() |
e0759e704b | ||
![]() |
0aa225ca78 | ||
![]() |
b43b4ee5c0 | ||
![]() |
0cdb8cecbf | ||
![]() |
fd6a306742 | ||
![]() |
7f3b3d2277 | ||
![]() |
8be5b977bf | ||
![]() |
d7ddb15f9c | ||
![]() |
9a6a1798d0 | ||
![]() |
14196fd349 | ||
![]() |
941b89a523 | ||
![]() |
a5f9e5f8c0 | ||
![]() |
80c3356c8f |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -14,4 +14,5 @@ dist/
|
|||||||
|
|
||||||
# Build
|
# Build
|
||||||
*.db
|
*.db
|
||||||
checkVersion.sh
|
checkVersion.sh
|
||||||
|
bun.lockb
|
||||||
|
386
LICENSE
386
LICENSE
@@ -1,21 +1,373 @@
|
|||||||
MIT License
|
Mozilla Public License Version 2.0
|
||||||
|
==================================
|
||||||
|
|
||||||
Copyright (c) 2024 NapCatQQ
|
1. Definitions
|
||||||
|
--------------
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
1.1. "Contributor"
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
means each individual or legal entity that creates, contributes to
|
||||||
in the Software without restriction, including without limitation the rights
|
the creation of, or owns Covered Software.
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
1.2. "Contributor Version"
|
||||||
copies or substantial portions of the Software.
|
means the combination of the Contributions of others (if any) used
|
||||||
|
by a Contributor and that particular Contributor's Contribution.
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
1.3. "Contribution"
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
means Covered Software of a particular Contributor.
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
1.4. "Covered Software"
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
means Source Code Form to which the initial Contributor has attached
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
the notice in Exhibit A, the Executable Form of such Source Code
|
||||||
SOFTWARE.
|
Form, and Modifications of such Source Code Form, in each case
|
||||||
|
including portions thereof.
|
||||||
|
|
||||||
|
1.5. "Incompatible With Secondary Licenses"
|
||||||
|
means
|
||||||
|
|
||||||
|
(a) that the initial Contributor has attached the notice described
|
||||||
|
in Exhibit B to the Covered Software; or
|
||||||
|
|
||||||
|
(b) that the Covered Software was made available under the terms of
|
||||||
|
version 1.1 or earlier of the License, but not also under the
|
||||||
|
terms of a Secondary License.
|
||||||
|
|
||||||
|
1.6. "Executable Form"
|
||||||
|
means any form of the work other than Source Code Form.
|
||||||
|
|
||||||
|
1.7. "Larger Work"
|
||||||
|
means a work that combines Covered Software with other material, in
|
||||||
|
a separate file or files, that is not Covered Software.
|
||||||
|
|
||||||
|
1.8. "License"
|
||||||
|
means this document.
|
||||||
|
|
||||||
|
1.9. "Licensable"
|
||||||
|
means having the right to grant, to the maximum extent possible,
|
||||||
|
whether at the time of the initial grant or subsequently, any and
|
||||||
|
all of the rights conveyed by this License.
|
||||||
|
|
||||||
|
1.10. "Modifications"
|
||||||
|
means any of the following:
|
||||||
|
|
||||||
|
(a) any file in Source Code Form that results from an addition to,
|
||||||
|
deletion from, or modification of the contents of Covered
|
||||||
|
Software; or
|
||||||
|
|
||||||
|
(b) any new file in Source Code Form that contains any Covered
|
||||||
|
Software.
|
||||||
|
|
||||||
|
1.11. "Patent Claims" of a Contributor
|
||||||
|
means any patent claim(s), including without limitation, method,
|
||||||
|
process, and apparatus claims, in any patent Licensable by such
|
||||||
|
Contributor that would be infringed, but for the grant of the
|
||||||
|
License, by the making, using, selling, offering for sale, having
|
||||||
|
made, import, or transfer of either its Contributions or its
|
||||||
|
Contributor Version.
|
||||||
|
|
||||||
|
1.12. "Secondary License"
|
||||||
|
means either the GNU General Public License, Version 2.0, the GNU
|
||||||
|
Lesser General Public License, Version 2.1, the GNU Affero General
|
||||||
|
Public License, Version 3.0, or any later versions of those
|
||||||
|
licenses.
|
||||||
|
|
||||||
|
1.13. "Source Code Form"
|
||||||
|
means the form of the work preferred for making modifications.
|
||||||
|
|
||||||
|
1.14. "You" (or "Your")
|
||||||
|
means an individual or a legal entity exercising rights under this
|
||||||
|
License. For legal entities, "You" includes any entity that
|
||||||
|
controls, is controlled by, or is under common control with You. For
|
||||||
|
purposes of this definition, "control" means (a) the power, direct
|
||||||
|
or indirect, to cause the direction or management of such entity,
|
||||||
|
whether by contract or otherwise, or (b) ownership of more than
|
||||||
|
fifty percent (50%) of the outstanding shares or beneficial
|
||||||
|
ownership of such entity.
|
||||||
|
|
||||||
|
2. License Grants and Conditions
|
||||||
|
--------------------------------
|
||||||
|
|
||||||
|
2.1. Grants
|
||||||
|
|
||||||
|
Each Contributor hereby grants You a world-wide, royalty-free,
|
||||||
|
non-exclusive license:
|
||||||
|
|
||||||
|
(a) under intellectual property rights (other than patent or trademark)
|
||||||
|
Licensable by such Contributor to use, reproduce, make available,
|
||||||
|
modify, display, perform, distribute, and otherwise exploit its
|
||||||
|
Contributions, either on an unmodified basis, with Modifications, or
|
||||||
|
as part of a Larger Work; and
|
||||||
|
|
||||||
|
(b) under Patent Claims of such Contributor to make, use, sell, offer
|
||||||
|
for sale, have made, import, and otherwise transfer either its
|
||||||
|
Contributions or its Contributor Version.
|
||||||
|
|
||||||
|
2.2. Effective Date
|
||||||
|
|
||||||
|
The licenses granted in Section 2.1 with respect to any Contribution
|
||||||
|
become effective for each Contribution on the date the Contributor first
|
||||||
|
distributes such Contribution.
|
||||||
|
|
||||||
|
2.3. Limitations on Grant Scope
|
||||||
|
|
||||||
|
The licenses granted in this Section 2 are the only rights granted under
|
||||||
|
this License. No additional rights or licenses will be implied from the
|
||||||
|
distribution or licensing of Covered Software under this License.
|
||||||
|
Notwithstanding Section 2.1(b) above, no patent license is granted by a
|
||||||
|
Contributor:
|
||||||
|
|
||||||
|
(a) for any code that a Contributor has removed from Covered Software;
|
||||||
|
or
|
||||||
|
|
||||||
|
(b) for infringements caused by: (i) Your and any other third party's
|
||||||
|
modifications of Covered Software, or (ii) the combination of its
|
||||||
|
Contributions with other software (except as part of its Contributor
|
||||||
|
Version); or
|
||||||
|
|
||||||
|
(c) under Patent Claims infringed by Covered Software in the absence of
|
||||||
|
its Contributions.
|
||||||
|
|
||||||
|
This License does not grant any rights in the trademarks, service marks,
|
||||||
|
or logos of any Contributor (except as may be necessary to comply with
|
||||||
|
the notice requirements in Section 3.4).
|
||||||
|
|
||||||
|
2.4. Subsequent Licenses
|
||||||
|
|
||||||
|
No Contributor makes additional grants as a result of Your choice to
|
||||||
|
distribute the Covered Software under a subsequent version of this
|
||||||
|
License (see Section 10.2) or under the terms of a Secondary License (if
|
||||||
|
permitted under the terms of Section 3.3).
|
||||||
|
|
||||||
|
2.5. Representation
|
||||||
|
|
||||||
|
Each Contributor represents that the Contributor believes its
|
||||||
|
Contributions are its original creation(s) or it has sufficient rights
|
||||||
|
to grant the rights to its Contributions conveyed by this License.
|
||||||
|
|
||||||
|
2.6. Fair Use
|
||||||
|
|
||||||
|
This License is not intended to limit any rights You have under
|
||||||
|
applicable copyright doctrines of fair use, fair dealing, or other
|
||||||
|
equivalents.
|
||||||
|
|
||||||
|
2.7. Conditions
|
||||||
|
|
||||||
|
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
|
||||||
|
in Section 2.1.
|
||||||
|
|
||||||
|
3. Responsibilities
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
3.1. Distribution of Source Form
|
||||||
|
|
||||||
|
All distribution of Covered Software in Source Code Form, including any
|
||||||
|
Modifications that You create or to which You contribute, must be under
|
||||||
|
the terms of this License. You must inform recipients that the Source
|
||||||
|
Code Form of the Covered Software is governed by the terms of this
|
||||||
|
License, and how they can obtain a copy of this License. You may not
|
||||||
|
attempt to alter or restrict the recipients' rights in the Source Code
|
||||||
|
Form.
|
||||||
|
|
||||||
|
3.2. Distribution of Executable Form
|
||||||
|
|
||||||
|
If You distribute Covered Software in Executable Form then:
|
||||||
|
|
||||||
|
(a) such Covered Software must also be made available in Source Code
|
||||||
|
Form, as described in Section 3.1, and You must inform recipients of
|
||||||
|
the Executable Form how they can obtain a copy of such Source Code
|
||||||
|
Form by reasonable means in a timely manner, at a charge no more
|
||||||
|
than the cost of distribution to the recipient; and
|
||||||
|
|
||||||
|
(b) You may distribute such Executable Form under the terms of this
|
||||||
|
License, or sublicense it under different terms, provided that the
|
||||||
|
license for the Executable Form does not attempt to limit or alter
|
||||||
|
the recipients' rights in the Source Code Form under this License.
|
||||||
|
|
||||||
|
3.3. Distribution of a Larger Work
|
||||||
|
|
||||||
|
You may create and distribute a Larger Work under terms of Your choice,
|
||||||
|
provided that You also comply with the requirements of this License for
|
||||||
|
the Covered Software. If the Larger Work is a combination of Covered
|
||||||
|
Software with a work governed by one or more Secondary Licenses, and the
|
||||||
|
Covered Software is not Incompatible With Secondary Licenses, this
|
||||||
|
License permits You to additionally distribute such Covered Software
|
||||||
|
under the terms of such Secondary License(s), so that the recipient of
|
||||||
|
the Larger Work may, at their option, further distribute the Covered
|
||||||
|
Software under the terms of either this License or such Secondary
|
||||||
|
License(s).
|
||||||
|
|
||||||
|
3.4. Notices
|
||||||
|
|
||||||
|
You may not remove or alter the substance of any license notices
|
||||||
|
(including copyright notices, patent notices, disclaimers of warranty,
|
||||||
|
or limitations of liability) contained within the Source Code Form of
|
||||||
|
the Covered Software, except that You may alter any license notices to
|
||||||
|
the extent required to remedy known factual inaccuracies.
|
||||||
|
|
||||||
|
3.5. Application of Additional Terms
|
||||||
|
|
||||||
|
You may choose to offer, and to charge a fee for, warranty, support,
|
||||||
|
indemnity or liability obligations to one or more recipients of Covered
|
||||||
|
Software. However, You may do so only on Your own behalf, and not on
|
||||||
|
behalf of any Contributor. You must make it absolutely clear that any
|
||||||
|
such warranty, support, indemnity, or liability obligation is offered by
|
||||||
|
You alone, and You hereby agree to indemnify every Contributor for any
|
||||||
|
liability incurred by such Contributor as a result of warranty, support,
|
||||||
|
indemnity or liability terms You offer. You may include additional
|
||||||
|
disclaimers of warranty and limitations of liability specific to any
|
||||||
|
jurisdiction.
|
||||||
|
|
||||||
|
4. Inability to Comply Due to Statute or Regulation
|
||||||
|
---------------------------------------------------
|
||||||
|
|
||||||
|
If it is impossible for You to comply with any of the terms of this
|
||||||
|
License with respect to some or all of the Covered Software due to
|
||||||
|
statute, judicial order, or regulation then You must: (a) comply with
|
||||||
|
the terms of this License to the maximum extent possible; and (b)
|
||||||
|
describe the limitations and the code they affect. Such description must
|
||||||
|
be placed in a text file included with all distributions of the Covered
|
||||||
|
Software under this License. Except to the extent prohibited by statute
|
||||||
|
or regulation, such description must be sufficiently detailed for a
|
||||||
|
recipient of ordinary skill to be able to understand it.
|
||||||
|
|
||||||
|
5. Termination
|
||||||
|
--------------
|
||||||
|
|
||||||
|
5.1. The rights granted under this License will terminate automatically
|
||||||
|
if You fail to comply with any of its terms. However, if You become
|
||||||
|
compliant, then the rights granted under this License from a particular
|
||||||
|
Contributor are reinstated (a) provisionally, unless and until such
|
||||||
|
Contributor explicitly and finally terminates Your grants, and (b) on an
|
||||||
|
ongoing basis, if such Contributor fails to notify You of the
|
||||||
|
non-compliance by some reasonable means prior to 60 days after You have
|
||||||
|
come back into compliance. Moreover, Your grants from a particular
|
||||||
|
Contributor are reinstated on an ongoing basis if such Contributor
|
||||||
|
notifies You of the non-compliance by some reasonable means, this is the
|
||||||
|
first time You have received notice of non-compliance with this License
|
||||||
|
from such Contributor, and You become compliant prior to 30 days after
|
||||||
|
Your receipt of the notice.
|
||||||
|
|
||||||
|
5.2. If You initiate litigation against any entity by asserting a patent
|
||||||
|
infringement claim (excluding declaratory judgment actions,
|
||||||
|
counter-claims, and cross-claims) alleging that a Contributor Version
|
||||||
|
directly or indirectly infringes any patent, then the rights granted to
|
||||||
|
You by any and all Contributors for the Covered Software under Section
|
||||||
|
2.1 of this License shall terminate.
|
||||||
|
|
||||||
|
5.3. In the event of termination under Sections 5.1 or 5.2 above, all
|
||||||
|
end user license agreements (excluding distributors and resellers) which
|
||||||
|
have been validly granted by You or Your distributors under this License
|
||||||
|
prior to termination shall survive termination.
|
||||||
|
|
||||||
|
************************************************************************
|
||||||
|
* *
|
||||||
|
* 6. Disclaimer of Warranty *
|
||||||
|
* ------------------------- *
|
||||||
|
* *
|
||||||
|
* Covered Software is provided under this License on an "as is" *
|
||||||
|
* basis, without warranty of any kind, either expressed, implied, or *
|
||||||
|
* statutory, including, without limitation, warranties that the *
|
||||||
|
* Covered Software is free of defects, merchantable, fit for a *
|
||||||
|
* particular purpose or non-infringing. The entire risk as to the *
|
||||||
|
* quality and performance of the Covered Software is with You. *
|
||||||
|
* Should any Covered Software prove defective in any respect, You *
|
||||||
|
* (not any Contributor) assume the cost of any necessary servicing, *
|
||||||
|
* repair, or correction. This disclaimer of warranty constitutes an *
|
||||||
|
* essential part of this License. No use of any Covered Software is *
|
||||||
|
* authorized under this License except under this disclaimer. *
|
||||||
|
* *
|
||||||
|
************************************************************************
|
||||||
|
|
||||||
|
************************************************************************
|
||||||
|
* *
|
||||||
|
* 7. Limitation of Liability *
|
||||||
|
* -------------------------- *
|
||||||
|
* *
|
||||||
|
* Under no circumstances and under no legal theory, whether tort *
|
||||||
|
* (including negligence), contract, or otherwise, shall any *
|
||||||
|
* Contributor, or anyone who distributes Covered Software as *
|
||||||
|
* permitted above, be liable to You for any direct, indirect, *
|
||||||
|
* special, incidental, or consequential damages of any character *
|
||||||
|
* including, without limitation, damages for lost profits, loss of *
|
||||||
|
* goodwill, work stoppage, computer failure or malfunction, or any *
|
||||||
|
* and all other commercial damages or losses, even if such party *
|
||||||
|
* shall have been informed of the possibility of such damages. This *
|
||||||
|
* limitation of liability shall not apply to liability for death or *
|
||||||
|
* personal injury resulting from such party's negligence to the *
|
||||||
|
* extent applicable law prohibits such limitation. Some *
|
||||||
|
* jurisdictions do not allow the exclusion or limitation of *
|
||||||
|
* incidental or consequential damages, so this exclusion and *
|
||||||
|
* limitation may not apply to You. *
|
||||||
|
* *
|
||||||
|
************************************************************************
|
||||||
|
|
||||||
|
8. Litigation
|
||||||
|
-------------
|
||||||
|
|
||||||
|
Any litigation relating to this License may be brought only in the
|
||||||
|
courts of a jurisdiction where the defendant maintains its principal
|
||||||
|
place of business and such litigation shall be governed by laws of that
|
||||||
|
jurisdiction, without reference to its conflict-of-law provisions.
|
||||||
|
Nothing in this Section shall prevent a party's ability to bring
|
||||||
|
cross-claims or counter-claims.
|
||||||
|
|
||||||
|
9. Miscellaneous
|
||||||
|
----------------
|
||||||
|
|
||||||
|
This License represents the complete agreement concerning the subject
|
||||||
|
matter hereof. If any provision of this License is held to be
|
||||||
|
unenforceable, such provision shall be reformed only to the extent
|
||||||
|
necessary to make it enforceable. Any law or regulation which provides
|
||||||
|
that the language of a contract shall be construed against the drafter
|
||||||
|
shall not be used to construe this License against a Contributor.
|
||||||
|
|
||||||
|
10. Versions of the License
|
||||||
|
---------------------------
|
||||||
|
|
||||||
|
10.1. New Versions
|
||||||
|
|
||||||
|
Mozilla Foundation is the license steward. Except as provided in Section
|
||||||
|
10.3, no one other than the license steward has the right to modify or
|
||||||
|
publish new versions of this License. Each version will be given a
|
||||||
|
distinguishing version number.
|
||||||
|
|
||||||
|
10.2. Effect of New Versions
|
||||||
|
|
||||||
|
You may distribute the Covered Software under the terms of the version
|
||||||
|
of the License under which You originally received the Covered Software,
|
||||||
|
or under the terms of any subsequent version published by the license
|
||||||
|
steward.
|
||||||
|
|
||||||
|
10.3. Modified Versions
|
||||||
|
|
||||||
|
If you create software not governed by this License, and you want to
|
||||||
|
create a new license for such software, you may create and use a
|
||||||
|
modified version of this License if you rename the license and remove
|
||||||
|
any references to the name of the license steward (except to note that
|
||||||
|
such modified license differs from this License).
|
||||||
|
|
||||||
|
10.4. Distributing Source Code Form that is Incompatible With Secondary
|
||||||
|
Licenses
|
||||||
|
|
||||||
|
If You choose to distribute Source Code Form that is Incompatible With
|
||||||
|
Secondary Licenses under the terms of this version of the License, the
|
||||||
|
notice described in Exhibit B of this License must be attached.
|
||||||
|
|
||||||
|
Exhibit A - Source Code Form License Notice
|
||||||
|
-------------------------------------------
|
||||||
|
|
||||||
|
This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
|
If it is not possible or desirable to put the notice in a particular
|
||||||
|
file, then You may include the notice in a location (such as a LICENSE
|
||||||
|
file in a relevant directory) where a recipient would be likely to look
|
||||||
|
for such a notice.
|
||||||
|
|
||||||
|
You may add additional accurate notices of copyright ownership.
|
||||||
|
|
||||||
|
Exhibit B - "Incompatible With Secondary Licenses" Notice
|
||||||
|
---------------------------------------------------------
|
||||||
|
|
||||||
|
This Source Code Form is "Incompatible With Secondary Licenses", as
|
||||||
|
defined by the Mozilla Public License, v. 2.0.
|
||||||
|
21
docs/changelogs/CHANGELOG.v1.7.9.md
Normal file
21
docs/changelogs/CHANGELOG.v1.7.9.md
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
# v1.8.0
|
||||||
|
|
||||||
|
QQ Version: Windows 9.9.15-26702 / Linux 3.2.12-26702
|
||||||
|
|
||||||
|
## 启动的方式
|
||||||
|
Way03/Way05
|
||||||
|
|
||||||
|
## 新增与调整
|
||||||
|
1. 消息ID映射到UINT32空间
|
||||||
|
2. 回复ID验证与修复
|
||||||
|
3. 新API /fetch_emoji_like
|
||||||
|
|
||||||
|
```json5
|
||||||
|
{
|
||||||
|
"message_id": 1557274996,//消息ID
|
||||||
|
"emojiType": "1",//可以从event事件获取 一般为1或者2
|
||||||
|
"emojiId": "76"//可以从event事件获取 一个表情对应一个ID
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
新增的 API 详细见[API文档](https://napneko.github.io/zh-CN/develop/extends_api)
|
17
docs/changelogs/old/CHANGELOG.v1.6.6.md
Normal file
17
docs/changelogs/old/CHANGELOG.v1.6.6.md
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
# v1.6.6
|
||||||
|
|
||||||
|
QQ Version: Windows 9.9.12-26000 / Linux 3.2.9-26000
|
||||||
|
## 使用前警告
|
||||||
|
1. 在最近版本由于QQ本体大幅变动,为了保证NapCat可用性,NapCat近期启动与安装方式将将大幅变动,请关注文档和社群获取。
|
||||||
|
2. 在Core上完全执行开源,请不要用于违法用途,如此可能造成NapCat完全停止更新。
|
||||||
|
3. 针对原启动方式的围堵,NapCat研发了多种方式,除此其余理论与扩展的分析和思路将部分展示于Docs,以便各位参与开发与维护NapCat。
|
||||||
|
## 其余·备注
|
||||||
|
启动方式: WayBoot.03 (Electron Main进程为Node 直接注入代码 同理项目: LiteLoader)
|
||||||
|
|
||||||
|
## 修复与优化
|
||||||
|
1. 修复了一些问题
|
||||||
|
|
||||||
|
## 新增与调整
|
||||||
|
没有哦
|
||||||
|
|
||||||
|
新增的 API 详细见[API文档](https://napneko.github.io/zh-CN/develop/extends_api)
|
@@ -1,2 +0,0 @@
|
|||||||
# 开始
|
|
||||||
jadx 跳转于 `com.tencent.qqnt.kernel.*`
|
|
@@ -1,42 +0,0 @@
|
|||||||
# Android
|
|
||||||
```java
|
|
||||||
GroupMemberExtReq groupMemberExtReq = new GroupMemberExtReq();
|
|
||||||
groupMemberExtReq.sourceType = MemberExtSourceType.TITLETYPE.ordinal();
|
|
||||||
groupMemberExtReq.groupCode = longOrNull.longValue();
|
|
||||||
groupMemberExtReq.beginUin = "0";
|
|
||||||
groupMemberExtReq.dataTime = "0";
|
|
||||||
Long[] lArr = new Long[1];
|
|
||||||
AppInterface a2 = dVar.a();
|
|
||||||
lArr[0] = Long.valueOf(a2 != null ? a2.getLongAccountUin() : 0L);
|
|
||||||
arrayListOf = CollectionsKt__CollectionsKt.arrayListOf(lArr);
|
|
||||||
groupMemberExtReq.uinList = arrayListOf;
|
|
||||||
MemberExtInfoFilter memberExtInfoFilter = new MemberExtInfoFilter();
|
|
||||||
memberExtInfoFilter.memberLevelInfoUin = 1;
|
|
||||||
memberExtInfoFilter.memberLevelInfoPoint = 1;
|
|
||||||
memberExtInfoFilter.memberLevelInfoActiveDay = 1;
|
|
||||||
memberExtInfoFilter.memberLevelInfoLevel = 1;
|
|
||||||
memberExtInfoFilter.levelName = 1;
|
|
||||||
memberExtInfoFilter.dataTime = 1;
|
|
||||||
memberExtInfoFilter.sysShowFlag = 1;
|
|
||||||
memberExtInfoFilter.userShowFlag = 1;
|
|
||||||
memberExtInfoFilter.userShowFlagNew = 1;
|
|
||||||
memberExtInfoFilter.levelNameNew = 1;
|
|
||||||
Unit unit = Unit.INSTANCE;
|
|
||||||
groupMemberExtReq.memberExtFilter = memberExtInfoFilter;
|
|
||||||
troopLevelFrequencyControl.f(troopUin, new TroopListRepo$fetchTroopLevelInfo$2(b2, groupMemberExtReq, troopUin, new com.tencent.qqnt.troopmemberlist.report.c("fetchTroopLevelInfo")));
|
|
||||||
```
|
|
||||||
# Win
|
|
||||||
参数解析位于 sub_181456A10(24108) -> wrapper.node(24108)+1456A10
|
|
||||||
IGroupService.GetMemberExt(param: object);
|
|
||||||
param展开如下
|
|
||||||
```
|
|
||||||
groupCode string
|
|
||||||
beginUin string
|
|
||||||
dataTime string
|
|
||||||
uinList Array<string>
|
|
||||||
uinNum string
|
|
||||||
groupType string
|
|
||||||
richCardNameVer string
|
|
||||||
sourceType number
|
|
||||||
memberExtFilter object// 参数解析位于 sub_18145A6D0(24108) -> wrapper.node(24108)+145A6D0
|
|
||||||
```
|
|
@@ -1,3 +1,4 @@
|
|||||||
|
|
||||||
public static final int C2C_PIC_DOWNLOAD = 1004;
|
public static final int C2C_PIC_DOWNLOAD = 1004;
|
||||||
public static final String C2C_PIC_DOWNLOAD_DOMAIN = "c2cpicdw.qpic.cn";
|
public static final String C2C_PIC_DOWNLOAD_DOMAIN = "c2cpicdw.qpic.cn";
|
||||||
public static final String C2C_PIC_DOWNLOAD_QUIC_DOMAIN = "c2cpicdw.quic.qpic.cn";
|
public static final String C2C_PIC_DOWNLOAD_QUIC_DOMAIN = "c2cpicdw.quic.qpic.cn";
|
||||||
|
16
docs/develop/NC 1.6.X的计划.md
Normal file
16
docs/develop/NC 1.6.X的计划.md
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
# 开发方向
|
||||||
|
方向一 NativeCall/Hook:
|
||||||
|
1. 崩溃检测机制的实现
|
||||||
|
2. Api_Caller 的Hook 可以拿到Event/Handler 进一步提升NC 即时的拦截与处理一些事件比如ReCall拦截
|
||||||
|
3. Node包装层 进一步分析,拿到脱离自带Listener/Adapter,可以拿到一些更加底层的数据变动 或许包括更多二进制数据
|
||||||
|
|
||||||
|
方向二 全新的无头启动 Way01
|
||||||
|
1. 基于Node启动原理,借助导出符号获取函数地址 再次还原NodeMain
|
||||||
|
|
||||||
|
方向三 发包与收包
|
||||||
|
1. 参考 方向一/3 大概可以收包
|
||||||
|
2. 发包 (暂时没有计划)
|
||||||
|
|
||||||
|
方向四 版本控制
|
||||||
|
1. 根据不同版本进行逻辑控制
|
||||||
|
2. 某些参数的自动提取
|
@@ -1,24 +0,0 @@
|
|||||||
# 前排提示
|
|
||||||
由于Core未处于开源,非组织人员无法参与Core开发,此处为Core开发提示
|
|
||||||
|
|
||||||
# 准备工具
|
|
||||||
frida ida-pro jadx x64dbg ce 内部调试脚本
|
|
||||||
|
|
||||||
## ida-pro
|
|
||||||
1. 用于快速分析入参和返回类型
|
|
||||||
2. 通过静态QLog推测语义
|
|
||||||
3. 提取Listener与Service (常用)
|
|
||||||
## frida
|
|
||||||
1. 用于动态获取QLog推测语义
|
|
||||||
2. 捕捉Native函数 实际入参与数据 分析中间流程
|
|
||||||
|
|
||||||
## jadx
|
|
||||||
1. 通过其它平台实现 静态获取QLog推测语义
|
|
||||||
2. 提供部分未调用代码 参考
|
|
||||||
|
|
||||||
## x64dbg
|
|
||||||
1. 验证IDA的Hook点
|
|
||||||
|
|
||||||
## 内部脚本
|
|
||||||
1. 提取Listener与Service (不调用无类型 不推荐)
|
|
||||||
2. 获取NT调用流程
|
|
@@ -1 +1,8 @@
|
|||||||
|
# Api方向
|
||||||
|
## getMsgUniqueId √ 已应用
|
||||||
getMsgUniqueId 传入时间 产出一个唯一ID 发送消息作为一个参数
|
getMsgUniqueId 传入时间 产出一个唯一ID 发送消息作为一个参数
|
||||||
|
|
||||||
|
# Native方向
|
||||||
|
## magic_load
|
||||||
|
## api_caller
|
||||||
|
## NodeMain
|
||||||
|
14
package.json
14
package.json
@@ -2,7 +2,7 @@
|
|||||||
"name": "napcat",
|
"name": "napcat",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"version": "1.6.5",
|
"version": "1.8.0",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"watch:dev": "vite --mode development",
|
"watch:dev": "vite --mode development",
|
||||||
"watch:prod": "vite --mode production",
|
"watch:prod": "vite --mode production",
|
||||||
@@ -19,10 +19,9 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.24.7",
|
"@babel/core": "^7.24.7",
|
||||||
"@babel/preset-typescript": "^7.24.7",
|
|
||||||
"vite-plugin-babel": "^1.2.0",
|
|
||||||
"@babel/plugin-proposal-class-properties": "^7.18.6",
|
"@babel/plugin-proposal-class-properties": "^7.18.6",
|
||||||
"@babel/plugin-proposal-decorators": "^7.24.7",
|
"@babel/plugin-proposal-decorators": "^7.24.7",
|
||||||
|
"@babel/preset-typescript": "^7.24.7",
|
||||||
"@log4js-node/log4js-api": "^1.0.2",
|
"@log4js-node/log4js-api": "^1.0.2",
|
||||||
"@protobuf-ts/plugin": "^2.9.4",
|
"@protobuf-ts/plugin": "^2.9.4",
|
||||||
"@rollup/plugin-node-resolve": "^15.2.3",
|
"@rollup/plugin-node-resolve": "^15.2.3",
|
||||||
@@ -31,9 +30,9 @@
|
|||||||
"@types/express": "^4.17.21",
|
"@types/express": "^4.17.21",
|
||||||
"@types/figlet": "^1.5.8",
|
"@types/figlet": "^1.5.8",
|
||||||
"@types/fluent-ffmpeg": "^2.1.24",
|
"@types/fluent-ffmpeg": "^2.1.24",
|
||||||
"@types/node": "^20.11.30",
|
"@types/jest": "^29.5.12",
|
||||||
|
"@types/node": "^22.0.0",
|
||||||
"@types/qrcode-terminal": "^0.12.2",
|
"@types/qrcode-terminal": "^0.12.2",
|
||||||
"@types/uuid": "^10.0.0",
|
|
||||||
"@types/ws": "^8.5.10",
|
"@types/ws": "^8.5.10",
|
||||||
"@typescript-eslint/eslint-plugin": "^7.4.0",
|
"@typescript-eslint/eslint-plugin": "^7.4.0",
|
||||||
"@typescript-eslint/parser": "^7.4.0",
|
"@typescript-eslint/parser": "^7.4.0",
|
||||||
@@ -47,6 +46,7 @@
|
|||||||
"rollup-plugin-obfuscator": "^1.1.0",
|
"rollup-plugin-obfuscator": "^1.1.0",
|
||||||
"typescript": "^5.3.3",
|
"typescript": "^5.3.3",
|
||||||
"vite": "^5.2.6",
|
"vite": "^5.2.6",
|
||||||
|
"vite-plugin-babel": "^1.2.0",
|
||||||
"vite-plugin-cp": "^4.0.8",
|
"vite-plugin-cp": "^4.0.8",
|
||||||
"vite-plugin-dts": "^3.8.2",
|
"vite-plugin-dts": "^3.8.2",
|
||||||
"vite-tsconfig-paths": "^4.3.2"
|
"vite-tsconfig-paths": "^4.3.2"
|
||||||
@@ -64,9 +64,7 @@
|
|||||||
"json-schema-to-ts": "^3.1.0",
|
"json-schema-to-ts": "^3.1.0",
|
||||||
"log4js": "^6.9.1",
|
"log4js": "^6.9.1",
|
||||||
"qrcode-terminal": "^0.12.0",
|
"qrcode-terminal": "^0.12.0",
|
||||||
"silk-wasm": "^3.3.4",
|
"silk-wasm": "^3.6.1",
|
||||||
"sqlite3": "^5.1.7",
|
|
||||||
"uuid": "^10.0.0",
|
|
||||||
"ws": "^8.16.0"
|
"ws": "^8.16.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -42,4 +42,4 @@ if (!(Test-Path $QQpath)) {
|
|||||||
$Bootfile = Join-Path $PSScriptRoot "napcat.mjs"
|
$Bootfile = Join-Path $PSScriptRoot "napcat.mjs"
|
||||||
$env:ELECTRON_RUN_AS_NODE = 1
|
$env:ELECTRON_RUN_AS_NODE = 1
|
||||||
$commandInfo = Get-Command $QQpath -ErrorAction Stop
|
$commandInfo = Get-Command $QQpath -ErrorAction Stop
|
||||||
Start-Process powershell -ArgumentList "-noexit", "-noprofile", "-command &{& chcp 65001;& '$($commandInfo.Path)' --enable-logging $params}"
|
Start-Process powershell -ArgumentList "-noexit", "-noprofile", "-command &{& chcp 65001;& '$($commandInfo.Path)' --enable-logging }"
|
123
script/BootWay05.ps1
Normal file
123
script/BootWay05.ps1
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
# 检查当前会话是否具有管理员权限
|
||||||
|
function Test-Administrator {
|
||||||
|
$user = [Security.Principal.WindowsIdentity]::GetCurrent()
|
||||||
|
(New-Object Security.Principal.WindowsPrincipal $user).IsInRole([Security.Principal.WindowsBuiltinRole]::Administrator)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (-not (Test-Administrator)) {
|
||||||
|
# 如果不是管理员,则重新启动脚本以管理员模式运行
|
||||||
|
$scriptPath = $myInvocation.MyCommand.Path
|
||||||
|
if (-not $scriptPath) {
|
||||||
|
$scriptPath = $PSCommandPath
|
||||||
|
}
|
||||||
|
$newProcess = New-Object System.Diagnostics.ProcessStartInfo "powershell";
|
||||||
|
$newProcess.Arguments = "-File `"$scriptPath`" $args"
|
||||||
|
$newProcess.Verb = "runas";
|
||||||
|
[System.Diagnostics.Process]::Start($newProcess);
|
||||||
|
exit
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-QQpath {
|
||||||
|
try {
|
||||||
|
$key = Get-ItemProperty -Path "HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\QQ"
|
||||||
|
$uninstallString = $key.UninstallString
|
||||||
|
return [System.IO.Path]::GetDirectoryName($uninstallString) + "\QQ.exe"
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
throw "get QQ path error: $_"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function Select-QQPath {
|
||||||
|
Add-Type -AssemblyName System.Windows.Forms
|
||||||
|
[System.Windows.Forms.Application]::EnableVisualStyles()
|
||||||
|
|
||||||
|
$dialogTitle = "Select QQ.exe"
|
||||||
|
|
||||||
|
$filePicker = New-Object System.Windows.Forms.OpenFileDialog
|
||||||
|
$filePicker.Title = $dialogTitle
|
||||||
|
$filePicker.Filter = "Executable Files (*.exe)|*.exe|All Files (*.*)|*.*"
|
||||||
|
$filePicker.FilterIndex = 1
|
||||||
|
$null = $filePicker.ShowDialog()
|
||||||
|
if (-not ($filePicker.FileName)) {
|
||||||
|
throw "User did not select an .exe file."
|
||||||
|
}
|
||||||
|
return $filePicker.FileName
|
||||||
|
}
|
||||||
|
|
||||||
|
# 设置当前工作目录
|
||||||
|
$scriptDirectory = Split-Path -Path $MyInvocation.MyCommand.Definition -Parent
|
||||||
|
Set-Location $scriptDirectory
|
||||||
|
|
||||||
|
# 获取当前目录路径
|
||||||
|
$currentPath = Get-Location
|
||||||
|
|
||||||
|
# 替换\为/
|
||||||
|
$currentPath = $currentPath -replace '\\', '/'
|
||||||
|
|
||||||
|
# 生成JavaScript代码
|
||||||
|
$jsCode = @"
|
||||||
|
(async () => {
|
||||||
|
await import('file:///$currentPath/napcat.mjs');
|
||||||
|
})();
|
||||||
|
"@
|
||||||
|
|
||||||
|
# 将JavaScript代码保存到文件中
|
||||||
|
$jsFilePath = Join-Path $currentPath "loadScript.js"
|
||||||
|
$jsCode | Out-File -FilePath $jsFilePath -Encoding UTF8
|
||||||
|
|
||||||
|
Write-Output "JavaScript code has been generated and saved to $jsFilePath"
|
||||||
|
# 设置NAPCAT_PATH环境变量为 当前目录的loadScript.js地址
|
||||||
|
$env:NAPCAT_PATH = $jsFilePath
|
||||||
|
|
||||||
|
$params = $args -join " "
|
||||||
|
Try {
|
||||||
|
$QQpath = Get-QQpath
|
||||||
|
}
|
||||||
|
Catch {
|
||||||
|
$QQpath = Select-QQPath
|
||||||
|
}
|
||||||
|
# 拿不到QQ路径则退出
|
||||||
|
if (!(Test-Path $QQpath)) {
|
||||||
|
Write-Output "provided QQ path is invalid: $QQpath"
|
||||||
|
Read-Host "Press any key to continue..."
|
||||||
|
exit
|
||||||
|
}
|
||||||
|
|
||||||
|
$commandInfo = Get-Command $QQpath -ErrorAction Stop
|
||||||
|
|
||||||
|
# 收集dbghelp.dll路径和HASH信息
|
||||||
|
$QQpath = Split-Path $QQpath
|
||||||
|
$oldDllPath = Join-Path $QQpath "dbghelp.dll"
|
||||||
|
$oldDllHash = Get-FileHash $oldDllPath -Algorithm MD5
|
||||||
|
$newDllPath = Join-Path $currentPath "dbghelp.dll"
|
||||||
|
$newDllHash = Get-FileHash $newDllPath -Algorithm MD5
|
||||||
|
# 如果文件一致则跳过
|
||||||
|
if ($oldDllHash.Hash -ne $newDllHash.Hash) {
|
||||||
|
$processes = Get-Process -Name QQ -ErrorAction SilentlyContinue
|
||||||
|
if ($processes) {
|
||||||
|
# 文件占用则退出
|
||||||
|
Write-Output "dbghelp.dll is in use by the following processes:"
|
||||||
|
$processes | ForEach-Object { Write-Output "$($_.Id) $($_.Name) $($_.Path)" }
|
||||||
|
Write-Output "dbghelp.dll is in use, cannot continue."
|
||||||
|
Read-Host "Press any key to continue..."
|
||||||
|
exit
|
||||||
|
} else {
|
||||||
|
# 文件未占用则尝试覆盖
|
||||||
|
try {
|
||||||
|
Copy-Item -Path "$newDllPath" -Destination "$oldDllPath" -Force
|
||||||
|
Write-Output "dbghelp.dll has been copied to $QQpath"
|
||||||
|
} catch {
|
||||||
|
Write-Output "Failed to copy dbghelp.dll: $_"
|
||||||
|
Read-Host "Press any key to continue..."
|
||||||
|
exit
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# 带参数启动QQ
|
||||||
|
try {
|
||||||
|
Start-Process powershell -ArgumentList '-noexit', '-noprofile', "-command &{& chcp 65001;& '$($commandInfo.Path)' --enable-logging $params}" -NoNewWindow -ErrorAction Stop
|
||||||
|
} catch {
|
||||||
|
Write-Output "Failed to start process as administrator: $_"
|
||||||
|
Read-Host "Press any key to continue..."
|
||||||
|
}
|
3
script/NapCat.Way01.bat
Normal file
3
script/NapCat.Way01.bat
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
REM 全新启动脚本 基于 Hook Native 预计版本1.6.0左右发布
|
||||||
|
@echo off
|
||||||
|
pause
|
BIN
script/dbghelp.dll
Normal file
BIN
script/dbghelp.dll
Normal file
Binary file not shown.
18
script/napcat-9912-utf8.bat
Normal file
18
script/napcat-9912-utf8.bat
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
@echo off
|
||||||
|
setlocal enabledelayedexpansion
|
||||||
|
chcp 65001
|
||||||
|
:loop_read
|
||||||
|
for /f "tokens=2*" %%a in ('reg query "HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\QQ" /v "UninstallString"') do (
|
||||||
|
set "RetString=%%b"
|
||||||
|
goto :napcat_boot
|
||||||
|
)
|
||||||
|
|
||||||
|
:napcat_boot
|
||||||
|
for %%a in ("!RetString!") do (
|
||||||
|
set "pathWithoutUninstall=%%~dpa"
|
||||||
|
)
|
||||||
|
|
||||||
|
set "QQPath=!pathWithoutUninstall!"
|
||||||
|
cd /d !QQPath!
|
||||||
|
echo !QQPath!
|
||||||
|
QQ.exe --enable-logging %*
|
41
script/napcat-9912-utf8.ps1
Normal file
41
script/napcat-9912-utf8.ps1
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
function Get-QQpath {
|
||||||
|
try {
|
||||||
|
$key = Get-ItemProperty -Path "HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\QQ"
|
||||||
|
$uninstallString = $key.UninstallString
|
||||||
|
return [System.IO.Path]::GetDirectoryName($uninstallString) + "\"
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
throw "get QQ path error: $_"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function Select-QQPath {
|
||||||
|
Add-Type -AssemblyName System.Windows.Forms
|
||||||
|
[System.Windows.Forms.Application]::EnableVisualStyles()
|
||||||
|
|
||||||
|
$dialogTitle = "Select QQ.exe"
|
||||||
|
|
||||||
|
$filePicker = New-Object System.Windows.Forms.OpenFileDialog
|
||||||
|
$filePicker.Title = $dialogTitle
|
||||||
|
$filePicker.Filter = "Executable Files (*.exe)|*.exe|All Files (*.*)|*.*"
|
||||||
|
$filePicker.FilterIndex = 1
|
||||||
|
$null = $filePicker.ShowDialog()
|
||||||
|
if (-not ($filePicker.FileName)) {
|
||||||
|
throw "User did not select an .exe file."
|
||||||
|
}
|
||||||
|
return $filePicker.FileName
|
||||||
|
}
|
||||||
|
|
||||||
|
$params = $args -join " "
|
||||||
|
Try {
|
||||||
|
$QQpath = Get-QQpath
|
||||||
|
}
|
||||||
|
Catch {
|
||||||
|
$QQpath = Select-QQPath
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(Test-Path $QQpath)) {
|
||||||
|
throw "provided QQ path is invalid: $QQpath"
|
||||||
|
}
|
||||||
|
|
||||||
|
Set-Location -Path $QQpath
|
||||||
|
Start-Process powershell -ArgumentList "-noexit", "-noprofile", "-command &{& chcp 65001;& ./QQ.exe --enable-logging $params}"
|
17
script/napcat-9912.bat
Normal file
17
script/napcat-9912.bat
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
@echo off
|
||||||
|
setlocal enabledelayedexpansion
|
||||||
|
:loop_read
|
||||||
|
for /f "tokens=2*" %%a in ('reg query "HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\QQ" /v "UninstallString"') do (
|
||||||
|
set "RetString=%%b"
|
||||||
|
goto :napcat_boot
|
||||||
|
)
|
||||||
|
|
||||||
|
:napcat_boot
|
||||||
|
for %%a in ("!RetString!") do (
|
||||||
|
set "pathWithoutUninstall=%%~dpa"
|
||||||
|
)
|
||||||
|
|
||||||
|
set QQPath=!pathWithoutUninstall!
|
||||||
|
cd /d !QQPath!
|
||||||
|
echo !QQPath!
|
||||||
|
QQ.exe --enable-logging %*
|
41
script/napcat-9912.ps1
Normal file
41
script/napcat-9912.ps1
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
function Get-QQpath {
|
||||||
|
try {
|
||||||
|
$key = Get-ItemProperty -Path "HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\QQ"
|
||||||
|
$uninstallString = $key.UninstallString
|
||||||
|
return [System.IO.Path]::GetDirectoryName($uninstallString) + "\"
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
throw "get QQ path error: $_"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function Select-QQPath {
|
||||||
|
Add-Type -AssemblyName System.Windows.Forms
|
||||||
|
[System.Windows.Forms.Application]::EnableVisualStyles()
|
||||||
|
|
||||||
|
$dialogTitle = "Select QQ.exe"
|
||||||
|
|
||||||
|
$filePicker = New-Object System.Windows.Forms.OpenFileDialog
|
||||||
|
$filePicker.Title = $dialogTitle
|
||||||
|
$filePicker.Filter = "Executable Files (*.exe)|*.exe|All Files (*.*)|*.*"
|
||||||
|
$filePicker.FilterIndex = 1
|
||||||
|
$null = $filePicker.ShowDialog()
|
||||||
|
if (-not ($filePicker.FileName)) {
|
||||||
|
throw "User did not select an .exe file."
|
||||||
|
}
|
||||||
|
return $filePicker.FileName
|
||||||
|
}
|
||||||
|
|
||||||
|
$params = $args -join " "
|
||||||
|
Try {
|
||||||
|
$QQpath = Get-QQpath
|
||||||
|
}
|
||||||
|
Catch {
|
||||||
|
$QQpath = Select-QQPath
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(Test-Path $QQpath)) {
|
||||||
|
throw "provided QQ path is invalid: $QQpath"
|
||||||
|
}
|
||||||
|
|
||||||
|
Set-Location -Path $QQpath
|
||||||
|
Start-Process powershell -ArgumentList "-noexit", "-noprofile", "-command &{& ./QQ.exe --enable-logging $params}"
|
@@ -9,7 +9,15 @@ type RegisterHandler = (res: Response, payload: any) => Promise<any>
|
|||||||
export abstract class HttpServerBase {
|
export abstract class HttpServerBase {
|
||||||
name: string = 'NapCatQQ';
|
name: string = 'NapCatQQ';
|
||||||
private readonly expressAPP: Express;
|
private readonly expressAPP: Express;
|
||||||
private server: http.Server | null = null;
|
private _server: http.Server | null = null;
|
||||||
|
|
||||||
|
public get server(): http.Server | null {
|
||||||
|
return this._server;
|
||||||
|
}
|
||||||
|
|
||||||
|
private set server(value: http.Server | null) {
|
||||||
|
this._server = value;
|
||||||
|
}
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.expressAPP = express();
|
this.expressAPP = express();
|
||||||
@@ -78,7 +86,7 @@ export abstract class HttpServerBase {
|
|||||||
this.start(port, host);
|
this.start(port, host);
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract handleFailed(res: Response, payload: any, err: any): void
|
abstract handleFailed(res: Response, payload: any, err: Error): void
|
||||||
|
|
||||||
registerRouter(method: 'post' | 'get' | string, url: string, handler: RegisterHandler) {
|
registerRouter(method: 'post' | 'get' | string, url: string, handler: RegisterHandler) {
|
||||||
if (!url.startsWith('/')) {
|
if (!url.startsWith('/')) {
|
||||||
@@ -103,7 +111,7 @@ export abstract class HttpServerBase {
|
|||||||
try {
|
try {
|
||||||
res.send(await handler(res, payload));
|
res.send(await handler(res, payload));
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
this.handleFailed(res, payload, e.stack.toString());
|
this.handleFailed(res, payload, e);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
import { WebSocket, WebSocketServer } from 'ws';
|
import { WebSocket, WebSocketServer } from 'ws';
|
||||||
|
import http from 'http';
|
||||||
import urlParse from 'url';
|
import urlParse from 'url';
|
||||||
import { IncomingMessage } from 'node:http';
|
import { IncomingMessage } from 'node:http';
|
||||||
import { log } from '@/common/utils/log';
|
import { log } from '@/common/utils/log';
|
||||||
@@ -27,17 +28,36 @@ export class WebsocketServerBase {
|
|||||||
constructor() {
|
constructor() {
|
||||||
}
|
}
|
||||||
|
|
||||||
start(port: number, host: string = '') {
|
start(port: number | http.Server, host: string = '') {
|
||||||
try {
|
if (port instanceof http.Server) {
|
||||||
this.ws = new WebSocketServer({
|
try {
|
||||||
port,
|
const wss = new WebSocketServer({
|
||||||
host: '',
|
noServer: true,
|
||||||
maxPayload: 1024 * 1024 * 1024
|
maxPayload: 1024 * 1024 * 1024
|
||||||
}).on('error', () => {
|
}).on('error', () => {
|
||||||
});
|
});
|
||||||
log(`ws服务启动成功, ${host}:${port}`);
|
this.ws = wss;
|
||||||
} catch (e: any) {
|
port.on('upgrade', function upgrade(request, socket, head) {
|
||||||
throw Error('ws服务启动失败, 请检查监听的ip和端口' + e.toString());
|
wss.handleUpgrade(request, socket, head, function done(ws) {
|
||||||
|
wss.emit('connection', ws, request);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
log('ws服务启动成功, 绑定到HTTP服务');
|
||||||
|
} catch (e: any) {
|
||||||
|
throw Error('ws服务启动失败, 可能是绑定的HTTP服务异常' + e.toString());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
this.ws = new WebSocketServer({
|
||||||
|
port,
|
||||||
|
host: '',
|
||||||
|
maxPayload: 1024 * 1024 * 1024
|
||||||
|
}).on('error', () => {
|
||||||
|
});
|
||||||
|
log(`ws服务启动成功, ${host}:${port}`);
|
||||||
|
} catch (e: any) {
|
||||||
|
throw Error('ws服务启动失败, 请检查监听的ip和端口' + e.toString());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
this.ws.on('connection', (wsClient, req) => {
|
this.ws.on('connection', (wsClient, req) => {
|
||||||
const url: string = req.url!.split('?').shift() || '/';
|
const url: string = req.url!.split('?').shift() || '/';
|
||||||
@@ -50,10 +70,12 @@ export class WebsocketServerBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
stop() {
|
stop() {
|
||||||
this.ws && this.ws.close((err) => {
|
if (this.ws) {
|
||||||
log('ws server close failed!', err);
|
this.ws.close((err) => {
|
||||||
});
|
if (err) log('ws server close failed!', err);
|
||||||
this.ws = null;
|
});
|
||||||
|
this.ws = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
restart(port: number) {
|
restart(port: number) {
|
||||||
|
@@ -1,36 +0,0 @@
|
|||||||
import { sleep } from '@/common/utils/helper';
|
|
||||||
import { logError } from './log';
|
|
||||||
type AsyncQueueTask = (() => void) | (()=>Promise<void>);
|
|
||||||
|
|
||||||
|
|
||||||
export class AsyncQueue {
|
|
||||||
private tasks: (AsyncQueueTask)[] = [];
|
|
||||||
|
|
||||||
public addTask(task: AsyncQueueTask) {
|
|
||||||
this.tasks.push(task);
|
|
||||||
// console.log('addTask', this.tasks.length);
|
|
||||||
if (this.tasks.length === 1) {
|
|
||||||
this.runQueue().then().catch(()=>{});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async runQueue() {
|
|
||||||
// console.log('runQueue', this.tasks.length);
|
|
||||||
while (this.tasks.length > 0) {
|
|
||||||
const task = this.tasks[0];
|
|
||||||
// console.log('typeof task', typeof task);
|
|
||||||
try {
|
|
||||||
const taskRet = task();
|
|
||||||
// console.log('type of taskRet', typeof taskRet, taskRet);
|
|
||||||
if (taskRet instanceof Promise) {
|
|
||||||
await taskRet;
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
// console.error(e);
|
|
||||||
logError(e);
|
|
||||||
}
|
|
||||||
this.tasks.shift();
|
|
||||||
await sleep(100);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -3,6 +3,7 @@ import fs from 'node:fs';
|
|||||||
import { log, logDebug, logError } from '@/common/utils/log';
|
import { log, logDebug, logError } from '@/common/utils/log';
|
||||||
import { dirname } from 'node:path';
|
import { dirname } from 'node:path';
|
||||||
import { fileURLToPath } from 'node:url';
|
import { fileURLToPath } from 'node:url';
|
||||||
|
import { selfInfo } from '@/core/data';
|
||||||
|
|
||||||
|
|
||||||
const __filename = fileURLToPath(import.meta.url);
|
const __filename = fileURLToPath(import.meta.url);
|
||||||
@@ -12,8 +13,9 @@ const configDir = path.resolve(__dirname, 'config');
|
|||||||
fs.mkdirSync(configDir, { recursive: true });
|
fs.mkdirSync(configDir, { recursive: true });
|
||||||
|
|
||||||
|
|
||||||
export class ConfigBase<T>{
|
export class ConfigBase<T> {
|
||||||
|
public name: string = 'default_config';
|
||||||
|
private pathName: string | null = null; // 本次读取的文件路径
|
||||||
constructor() {
|
constructor() {
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -22,19 +24,28 @@ export class ConfigBase<T>{
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
getConfigDir(){
|
getConfigDir() {
|
||||||
const configDir = path.resolve(__dirname, 'config');
|
const configDir = path.resolve(__dirname, 'config');
|
||||||
fs.mkdirSync(configDir, { recursive: true });
|
fs.mkdirSync(configDir, { recursive: true });
|
||||||
return configDir;
|
return configDir;
|
||||||
}
|
}
|
||||||
getConfigPath(): string {
|
getConfigPath(pathName: string | null): string {
|
||||||
throw new Error('Method not implemented.');
|
const suffix = pathName ? `_${pathName}` : '';
|
||||||
|
const filename = `${this.name}${suffix}.json`;
|
||||||
|
return path.join(this.getConfigDir(), filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
read() {
|
read() {
|
||||||
const configPath = this.getConfigPath();
|
// 尝试加载当前账号配置
|
||||||
|
if (this.read_from_file(selfInfo.uin, false)) return this;
|
||||||
|
// 尝试加载默认配置
|
||||||
|
return this.read_from_file('', true);
|
||||||
|
}
|
||||||
|
read_from_file(pathName: string, createIfNotExist: boolean) {
|
||||||
|
const configPath = this.getConfigPath(pathName);
|
||||||
if (!fs.existsSync(configPath)) {
|
if (!fs.existsSync(configPath)) {
|
||||||
try{
|
if (!createIfNotExist) return null;
|
||||||
|
this.pathName = pathName; // 记录有效的设置文件
|
||||||
|
try {
|
||||||
fs.writeFileSync(configPath, JSON.stringify(this, this.getKeys(), 2));
|
fs.writeFileSync(configPath, JSON.stringify(this, this.getKeys(), 2));
|
||||||
log(`配置文件${configPath}已创建\n如果修改此文件后需要重启 NapCat 生效`);
|
log(`配置文件${configPath}已创建\n如果修改此文件后需要重启 NapCat 生效`);
|
||||||
}
|
}
|
||||||
@@ -43,6 +54,7 @@ export class ConfigBase<T>{
|
|||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const data = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
|
const data = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
|
||||||
logDebug(`配置文件${configPath}已加载`, data);
|
logDebug(`配置文件${configPath}已加载`, data);
|
||||||
@@ -61,9 +73,13 @@ export class ConfigBase<T>{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
save(config: T) {
|
save(config: T, overwrite: boolean = false) {
|
||||||
Object.assign(this, config);
|
Object.assign(this, config);
|
||||||
const configPath = this.getConfigPath();
|
if (overwrite) {
|
||||||
|
// 用户要求强制写入,则变更当前文件为目标文件
|
||||||
|
this.pathName = `${selfInfo.uin}`;
|
||||||
|
}
|
||||||
|
const configPath = this.getConfigPath(this.pathName);
|
||||||
try {
|
try {
|
||||||
fs.writeFileSync(configPath, JSON.stringify(this, this.getKeys(), 2));
|
fs.writeFileSync(configPath, JSON.stringify(this, this.getKeys(), 2));
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
|
@@ -5,6 +5,7 @@ interface Internal_MapKey {
|
|||||||
timeout: number,
|
timeout: number,
|
||||||
createtime: number,
|
createtime: number,
|
||||||
func: (...arg: any[]) => any,
|
func: (...arg: any[]) => any,
|
||||||
|
checker: ((...args: any[]) => boolean) | undefined,
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ListenerClassBase {
|
export class ListenerClassBase {
|
||||||
@@ -83,17 +84,19 @@ export class NTEventWrapper {
|
|||||||
}
|
}
|
||||||
//统一回调清理事件
|
//统一回调清理事件
|
||||||
async DispatcherListener(ListenerMainName: string, ListenerSubName: string, ...args: any[]) {
|
async DispatcherListener(ListenerMainName: string, ListenerSubName: string, ...args: any[]) {
|
||||||
//console.log(ListenerMainName, this.EventTask.get(ListenerMainName), ListenerSubName, this.EventTask.get(ListenerMainName)?.get(ListenerSubName));
|
//console.log("[EventDispatcher]",ListenerMainName, ListenerSubName, ...args);
|
||||||
this.EventTask.get(ListenerMainName)?.get(ListenerSubName)?.forEach((task, uuid) => {
|
this.EventTask.get(ListenerMainName)?.get(ListenerSubName)?.forEach((task, uuid) => {
|
||||||
//console.log(task.func, uuid, task.createtime, task.timeout);
|
//console.log(task.func, uuid, task.createtime, task.timeout);
|
||||||
if (task.createtime + task.timeout < Date.now()) {
|
if (task.createtime + task.timeout < Date.now()) {
|
||||||
this.EventTask.get(ListenerMainName)?.get(ListenerSubName)?.delete(uuid);
|
this.EventTask.get(ListenerMainName)?.get(ListenerSubName)?.delete(uuid);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
task.func(...args);
|
if (task.checker && task.checker(...args)) {
|
||||||
|
task.func(...args);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
async CallNoListenerEvent<EventType extends (...args: any[]) => Promise<any>,>(EventName = '', timeout: number = 3000, ...args: Parameters<EventType>) {
|
async CallNoListenerEvent<EventType extends (...args: any[]) => Promise<any> | any>(EventName = '', timeout: number = 3000, ...args: Parameters<EventType>) {
|
||||||
return new Promise<Awaited<ReturnType<EventType>>>(async (resolve, reject) => {
|
return new Promise<Awaited<ReturnType<EventType>>>(async (resolve, reject) => {
|
||||||
const EventFunc = this.CreatEventFunction<EventType>(EventName);
|
const EventFunc = this.CreatEventFunction<EventType>(EventName);
|
||||||
let complete = false;
|
let complete = false;
|
||||||
@@ -107,27 +110,71 @@ export class NTEventWrapper {
|
|||||||
resolve(retData);
|
resolve(retData);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
async CallNormalEvent<EventType extends (...args: any[]) => Promise<any>, ListenerType extends (...args: any[]) => void>(EventName = '', ListenerName = '', waitTimes = 1, timeout: number = 3000, ...args: Parameters<EventType>) {
|
async RegisterListen<ListenerType extends (...args: any[]) => void>(ListenerName = '', waitTimes = 1, timeout = 5000, checker: (...args: Parameters<ListenerType>) => boolean) {
|
||||||
|
return new Promise<Parameters<ListenerType>>((resolve, reject) => {
|
||||||
|
const ListenerNameList = ListenerName.split('/');
|
||||||
|
const ListenerMainName = ListenerNameList[0];
|
||||||
|
const ListenerSubName = ListenerNameList[1];
|
||||||
|
const id = randomUUID();
|
||||||
|
let complete = 0;
|
||||||
|
let retData: Parameters<ListenerType> | undefined = undefined;
|
||||||
|
const databack = () => {
|
||||||
|
if (complete == 0) {
|
||||||
|
reject(new Error(' ListenerName:' + ListenerName + ' timeout'));
|
||||||
|
} else {
|
||||||
|
resolve(retData!);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const Timeouter = setTimeout(databack, timeout);
|
||||||
|
const eventCallbak = {
|
||||||
|
timeout: timeout,
|
||||||
|
createtime: Date.now(),
|
||||||
|
checker: checker,
|
||||||
|
func: (...args: Parameters<ListenerType>) => {
|
||||||
|
complete++;
|
||||||
|
retData = args;
|
||||||
|
if (complete >= waitTimes) {
|
||||||
|
clearTimeout(Timeouter);
|
||||||
|
databack();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if (!this.EventTask.get(ListenerMainName)) {
|
||||||
|
this.EventTask.set(ListenerMainName, new Map());
|
||||||
|
}
|
||||||
|
if (!(this.EventTask.get(ListenerMainName)?.get(ListenerSubName))) {
|
||||||
|
this.EventTask.get(ListenerMainName)?.set(ListenerSubName, new Map());
|
||||||
|
}
|
||||||
|
this.EventTask.get(ListenerMainName)?.get(ListenerSubName)?.set(id, eventCallbak);
|
||||||
|
this.CreatListenerFunction(ListenerMainName);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
async CallNormalEvent<EventType extends (...args: any[]) => Promise<any>, ListenerType extends (...args: any[]) => void>
|
||||||
|
(EventName = '', ListenerName = '', waitTimes = 1, timeout: number = 3000, checker: (...args: Parameters<ListenerType>) => boolean, ...args: Parameters<EventType>) {
|
||||||
return new Promise<[EventRet: Awaited<ReturnType<EventType>>, ...Parameters<ListenerType>]>(async (resolve, reject) => {
|
return new Promise<[EventRet: Awaited<ReturnType<EventType>>, ...Parameters<ListenerType>]>(async (resolve, reject) => {
|
||||||
const id = randomUUID();
|
const id = randomUUID();
|
||||||
let complete = 0;
|
let complete = 0;
|
||||||
let retData: Parameters<ListenerType> | undefined = undefined;
|
let retData: Parameters<ListenerType> | undefined = undefined;
|
||||||
let retEvent: any = {};
|
let retEvent: any = {};
|
||||||
const databack = () => {
|
const databack = () => {
|
||||||
if (complete < waitTimes) {
|
if (complete == 0) {
|
||||||
reject(new Error('NTEvent EventName:' + EventName + ' ListenerName:' + ListenerName + ' timeout'));
|
reject(new Error('Timeout: NTEvent EventName:' + EventName + ' ListenerName:' + ListenerName + ' EventRet:\n' + JSON.stringify(retEvent, null, 4) + '\n'));
|
||||||
} else {
|
} else {
|
||||||
resolve([retEvent as Awaited<ReturnType<EventType>>, ...retData!]);
|
resolve([retEvent as Awaited<ReturnType<EventType>>, ...retData!]);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const Timeouter = setTimeout(databack, timeout);
|
|
||||||
|
|
||||||
const ListenerNameList = ListenerName.split('/');
|
const ListenerNameList = ListenerName.split('/');
|
||||||
const ListenerMainName = ListenerNameList[0];
|
const ListenerMainName = ListenerNameList[0];
|
||||||
const ListenerSubName = ListenerNameList[1];
|
const ListenerSubName = ListenerNameList[1];
|
||||||
|
|
||||||
|
const Timeouter = setTimeout(databack, timeout);
|
||||||
|
|
||||||
const eventCallbak = {
|
const eventCallbak = {
|
||||||
timeout: timeout,
|
timeout: timeout,
|
||||||
createtime: Date.now(),
|
createtime: Date.now(),
|
||||||
|
checker: checker,
|
||||||
func: (...args: any[]) => {
|
func: (...args: any[]) => {
|
||||||
complete++;
|
complete++;
|
||||||
//console.log('func', ...args);
|
//console.log('func', ...args);
|
||||||
@@ -147,7 +194,6 @@ export class NTEventWrapper {
|
|||||||
this.EventTask.get(ListenerMainName)?.get(ListenerSubName)?.set(id, eventCallbak);
|
this.EventTask.get(ListenerMainName)?.get(ListenerSubName)?.set(id, eventCallbak);
|
||||||
this.CreatListenerFunction(ListenerMainName);
|
this.CreatListenerFunction(ListenerMainName);
|
||||||
const EventFunc = this.CreatEventFunction<EventType>(EventName);
|
const EventFunc = this.CreatEventFunction<EventType>(EventName);
|
||||||
//console.log("测试打点", args);
|
|
||||||
retEvent = await EventFunc!(...(args as any[]));
|
retEvent = await EventFunc!(...(args as any[]));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@@ -1,165 +0,0 @@
|
|||||||
import { logError, logDebug } from "@/common/utils/log";
|
|
||||||
|
|
||||||
type group_id = number;
|
|
||||||
type user_id = number;
|
|
||||||
|
|
||||||
class cacheNode<T> {
|
|
||||||
value: T;
|
|
||||||
groupId: group_id;
|
|
||||||
userId: user_id;
|
|
||||||
prev: cacheNode<T> | null;
|
|
||||||
next: cacheNode<T> | null;
|
|
||||||
timestamp: number;
|
|
||||||
|
|
||||||
constructor(groupId: group_id, userId: user_id, value: T) {
|
|
||||||
this.groupId = groupId;
|
|
||||||
this.userId = userId;
|
|
||||||
this.value = value;
|
|
||||||
this.prev = null;
|
|
||||||
this.next = null;
|
|
||||||
this.timestamp = Date.now();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type cache<T> = { [key: group_id]: { [key: user_id]: cacheNode<T> } };
|
|
||||||
class LRU<T> {
|
|
||||||
private maxAge: number;
|
|
||||||
private maxSize: number;
|
|
||||||
private currentSize: number;
|
|
||||||
private cache: cache<T>;
|
|
||||||
private head: cacheNode<T> | null = null;
|
|
||||||
private tail: cacheNode<T> | null = null;
|
|
||||||
private onFuncs: ((node: cacheNode<T>) => void)[] = [];
|
|
||||||
|
|
||||||
constructor(maxAge: number = 6e4, maxSize: number = 5e3) {
|
|
||||||
this.maxAge = maxAge;
|
|
||||||
this.maxSize = maxSize;
|
|
||||||
this.cache = Object.create(null);
|
|
||||||
this.currentSize = 0;
|
|
||||||
|
|
||||||
if (maxSize == 0) return;
|
|
||||||
setInterval(() => this.removeExpired(), this.maxAge);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 移除LRU节点
|
|
||||||
private removeLRUNode(node: cacheNode<T>) {
|
|
||||||
logDebug(
|
|
||||||
"removeLRUNode",
|
|
||||||
node.groupId,
|
|
||||||
node.userId,
|
|
||||||
node.value,
|
|
||||||
this.currentSize
|
|
||||||
);
|
|
||||||
node.prev = node.next = null;
|
|
||||||
delete this.cache[node.groupId][node.userId];
|
|
||||||
this.removeNode(node);
|
|
||||||
this.onFuncs.forEach((func) => func(node));
|
|
||||||
this.currentSize--;
|
|
||||||
}
|
|
||||||
|
|
||||||
public on(func: (node: cacheNode<T>) => void) {
|
|
||||||
this.onFuncs.push(func);
|
|
||||||
}
|
|
||||||
|
|
||||||
private removeExpired() {
|
|
||||||
const now = Date.now();
|
|
||||||
let current = this.tail;
|
|
||||||
const nodesToRemove: cacheNode<T>[] = [];
|
|
||||||
let removedCount = 0;
|
|
||||||
|
|
||||||
// 收集需要删除的节点
|
|
||||||
while (current && now - current.timestamp > this.maxAge) {
|
|
||||||
nodesToRemove.push(current);
|
|
||||||
current = current.prev;
|
|
||||||
removedCount++;
|
|
||||||
if (removedCount >= 100) break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 更新链表指向
|
|
||||||
if (nodesToRemove.length > 0) {
|
|
||||||
const newTail = nodesToRemove[nodesToRemove.length - 1].prev;
|
|
||||||
if (newTail) {
|
|
||||||
newTail.next = null;
|
|
||||||
} else {
|
|
||||||
this.head = null;
|
|
||||||
}
|
|
||||||
this.tail = newTail;
|
|
||||||
}
|
|
||||||
|
|
||||||
nodesToRemove.forEach((node) => {
|
|
||||||
node.prev = node.next = null;
|
|
||||||
delete this.cache[node.groupId][node.userId];
|
|
||||||
|
|
||||||
this.currentSize--;
|
|
||||||
this.onFuncs.forEach((func) => func(node));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private addNode(node: cacheNode<T>) {
|
|
||||||
node.next = this.head;
|
|
||||||
if (this.head) this.head.prev = node;
|
|
||||||
if (!this.tail) this.tail = node;
|
|
||||||
this.head = node;
|
|
||||||
}
|
|
||||||
|
|
||||||
private removeNode(node: cacheNode<T>) {
|
|
||||||
if (node.prev) node.prev.next = node.next;
|
|
||||||
if (node.next) node.next.prev = node.prev;
|
|
||||||
if (node === this.head) this.head = node.next;
|
|
||||||
if (node === this.tail) this.tail = node.prev;
|
|
||||||
}
|
|
||||||
|
|
||||||
private moveToHead(node: cacheNode<T>) {
|
|
||||||
if (this.head === node) return;
|
|
||||||
|
|
||||||
this.removeNode(node);
|
|
||||||
this.addNode(node);
|
|
||||||
node.prev = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public set(groupId: group_id, userId: user_id, value: T) {
|
|
||||||
if (!this.cache[groupId]) {
|
|
||||||
this.cache[groupId] = Object.create(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
const groupObject = this.cache[groupId];
|
|
||||||
|
|
||||||
if (groupObject[userId]) {
|
|
||||||
const node = groupObject[userId];
|
|
||||||
node.value = value;
|
|
||||||
node.timestamp = Date.now();
|
|
||||||
this.moveToHead(node);
|
|
||||||
} else {
|
|
||||||
const node = new cacheNode(groupId, userId, value);
|
|
||||||
groupObject[userId] = node;
|
|
||||||
this.currentSize++;
|
|
||||||
this.addNode(node);
|
|
||||||
if (this.currentSize > this.maxSize) {
|
|
||||||
const tail = this.tail!;
|
|
||||||
this.removeLRUNode(tail);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public get(groupId: group_id): { userId: user_id; value: T }[];
|
|
||||||
public get(groupId: group_id, userId: user_id): null | { userId: user_id; value: T };
|
|
||||||
public get(groupId: group_id, userId?: user_id): any {
|
|
||||||
const groupObject = this.cache[groupId];
|
|
||||||
if(!groupObject) return userId === undefined ? [] : null;
|
|
||||||
|
|
||||||
if (userId === undefined) {
|
|
||||||
return Object.entries(groupObject).map(([userId, { value }]) => ({
|
|
||||||
userId: Number(userId),
|
|
||||||
value,
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (groupObject[userId]) {
|
|
||||||
return { userId, value: groupObject[userId].value };
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default LRU;
|
|
@@ -1,20 +1,40 @@
|
|||||||
import crypto from 'crypto';
|
import { Peer } from '@/core';
|
||||||
|
import crypto, { randomInt, randomUUID } from 'crypto';
|
||||||
|
import { logError } from './log';
|
||||||
|
|
||||||
class LimitedHashTable<K, V> {
|
export class LimitedHashTable<K, V> {
|
||||||
private keyToValue: Map<K, V> = new Map();
|
private keyToValue: Map<K, V> = new Map();
|
||||||
private valueToKey: Map<V, K> = new Map();
|
private valueToKey: Map<V, K> = new Map();
|
||||||
private maxSize: number;
|
private maxSize: number;
|
||||||
private KeyQueneList: K[] = [];
|
|
||||||
private ValueQueneList: V[] = [];
|
|
||||||
constructor(maxSize: number) {
|
constructor(maxSize: number) {
|
||||||
this.maxSize = maxSize;
|
this.maxSize = maxSize;
|
||||||
}
|
}
|
||||||
|
resize(count: number) {
|
||||||
|
this.maxSize = count;
|
||||||
|
}
|
||||||
|
|
||||||
set(key: K, value: V): void {
|
set(key: K, value: V): void {
|
||||||
|
// const isExist = this.keyToValue.get(key);
|
||||||
|
// if (isExist && isExist === value) {
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
this.keyToValue.set(key, value);
|
this.keyToValue.set(key, value);
|
||||||
this.valueToKey.set(value, key);
|
this.valueToKey.set(value, key);
|
||||||
if (this.KeyQueneList.length >= this.maxSize || this.ValueQueneList.length >= this.maxSize) {
|
while (this.keyToValue.size !== this.valueToKey.size) {
|
||||||
this.KeyQueneList.shift();
|
console.log('keyToValue.size !== valueToKey.size Error Atom');
|
||||||
this.ValueQueneList.shift();
|
this.keyToValue.clear();
|
||||||
|
this.valueToKey.clear();
|
||||||
|
}
|
||||||
|
// console.log('---------------');
|
||||||
|
// console.log(this.keyToValue);
|
||||||
|
// console.log(this.valueToKey);
|
||||||
|
// console.log('---------------');
|
||||||
|
while (this.keyToValue.size > this.maxSize || this.valueToKey.size > this.maxSize) {
|
||||||
|
//console.log(this.keyToValue.size > this.maxSize, this.valueToKey.size > this.maxSize);
|
||||||
|
const oldestKey = this.keyToValue.keys().next().value;
|
||||||
|
this.valueToKey.delete(this.keyToValue.get(oldestKey)!);
|
||||||
|
this.keyToValue.delete(oldestKey);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -26,28 +46,99 @@ class LimitedHashTable<K, V> {
|
|||||||
return this.valueToKey.get(value);
|
return this.valueToKey.get(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
delete(key: K): void {
|
deleteByValue(value: V): void {
|
||||||
|
const key = this.valueToKey.get(value);
|
||||||
|
if (key !== undefined) {
|
||||||
|
this.keyToValue.delete(key);
|
||||||
|
this.valueToKey.delete(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteByKey(key: K): void {
|
||||||
const value = this.keyToValue.get(key);
|
const value = this.keyToValue.get(key);
|
||||||
if (value !== undefined) {
|
if (value !== undefined) {
|
||||||
this.keyToValue.delete(key);
|
this.keyToValue.delete(key);
|
||||||
this.valueToKey.delete(value);
|
this.valueToKey.delete(value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getKeyList(): K[] {
|
||||||
|
return Array.from(this.keyToValue.keys());
|
||||||
|
}
|
||||||
|
//获取最近刚写入的几个值
|
||||||
|
getHeads(size: number): { key: K; value: V }[] | undefined {
|
||||||
|
const keyList = this.getKeyList();
|
||||||
|
if (keyList.length === 0) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
const result: { key: K; value: V }[] = [];
|
||||||
|
const listSize = Math.min(size, keyList.length);
|
||||||
|
for (let i = 0; i < listSize; i++) {
|
||||||
|
const key = keyList[listSize - i];
|
||||||
|
result.push({ key, value: this.keyToValue.get(key)! });
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class MessageUniqueWrapper {
|
class MessageUniqueWrapper {
|
||||||
private msgIdMap: LimitedHashTable<number, string> = new LimitedHashTable(1000);
|
private msgDataMap: LimitedHashTable<string, number>;
|
||||||
createMsg(MsgId: string) {
|
private msgIdMap: LimitedHashTable<string, number>;
|
||||||
const ShortId = parseInt(crypto.createHash('sha1').update('2345').digest('hex').slice(0, 8), 16);
|
constructor(maxMap: number = 1000) {
|
||||||
this.msgIdMap.set(ShortId, MsgId);
|
this.msgIdMap = new LimitedHashTable<string, number>(maxMap);
|
||||||
return ShortId;
|
this.msgDataMap = new LimitedHashTable<string, number>(maxMap);
|
||||||
}
|
}
|
||||||
getMsgIdByShortId(ShortId: number) {
|
getRecentMsgIds(Peer: Peer, size: number): string[] {
|
||||||
return this.msgIdMap.getValue(ShortId);
|
const heads = this.msgIdMap.getHeads(size);
|
||||||
|
if (!heads) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
const data = heads.map((t) => MessageUnique.getMsgIdAndPeerByShortId(t.value));
|
||||||
|
const ret = data.filter((t) => t?.Peer.chatType === Peer.chatType && t?.Peer.peerUid === Peer.peerUid);
|
||||||
|
return ret.map((t) => t?.MsgId).filter((t) => t !== undefined);
|
||||||
}
|
}
|
||||||
getShortIdByMsgId(MsgId: string) {
|
createMsg(peer: Peer, msgId: string): number | undefined {
|
||||||
return this.msgIdMap.getKey(MsgId);
|
const key = `${msgId}|${peer.chatType}|${peer.peerUid}`;
|
||||||
|
const hash = crypto.createHash('md5').update(key).digest();
|
||||||
|
//设置第一个bit为0 保证shortId为正数
|
||||||
|
hash[0] &= 0x7f;
|
||||||
|
const shortId = hash.readInt32BE(0);
|
||||||
|
//减少性能损耗
|
||||||
|
// const isExist = this.msgIdMap.getKey(shortId);
|
||||||
|
// if (isExist && isExist === msgId) {
|
||||||
|
// return shortId;
|
||||||
|
// }
|
||||||
|
this.msgIdMap.set(msgId, shortId);
|
||||||
|
this.msgDataMap.set(key, shortId);
|
||||||
|
return shortId;
|
||||||
|
}
|
||||||
|
|
||||||
|
getMsgIdAndPeerByShortId(shortId: number): { MsgId: string; Peer: Peer } | undefined {
|
||||||
|
const data = this.msgDataMap.getKey(shortId);
|
||||||
|
if (data) {
|
||||||
|
const [msgId, chatTypeStr, peerUid] = data.split('|');
|
||||||
|
const peer: Peer = {
|
||||||
|
chatType: parseInt(chatTypeStr),
|
||||||
|
peerUid,
|
||||||
|
guildId: '',
|
||||||
|
};
|
||||||
|
return { MsgId: msgId, Peer: peer };
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
getShortIdByMsgId(msgId: string): number | undefined {
|
||||||
|
return this.msgIdMap.getValue(msgId);
|
||||||
|
}
|
||||||
|
getPeerByMsgId(msgId: string) {
|
||||||
|
const shortId = this.msgIdMap.getValue(msgId);
|
||||||
|
if (!shortId) return undefined;
|
||||||
|
return this.getMsgIdAndPeerByShortId(shortId);
|
||||||
|
}
|
||||||
|
resize(maxSize: number): void {
|
||||||
|
this.msgIdMap.resize(maxSize);
|
||||||
|
this.msgDataMap.resize(maxSize);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const MessageUnique = new MessageUniqueWrapper();
|
export const MessageUnique: MessageUniqueWrapper = new MessageUniqueWrapper();
|
@@ -1,77 +1,56 @@
|
|||||||
import path from 'node:path';
|
import path from 'node:path';
|
||||||
import fs from 'node:fs';
|
import fs from 'node:fs';
|
||||||
import os from 'node:os';
|
|
||||||
import { systemPlatform } from '@/common/utils/system';
|
import { systemPlatform } from '@/common/utils/system';
|
||||||
import { logError } from '@/common/utils/log';
|
import { getDefaultQQVersionConfigInfo, getQQVersionConfigPath } from './helper';
|
||||||
|
import AppidTable from '@/core/external/appid.json';
|
||||||
|
import { log } from './log';
|
||||||
|
|
||||||
export const exePath = process.execPath;
|
//基础目录获取
|
||||||
|
export let QQMainPath = process.execPath;
|
||||||
|
export let QQPackageInfoPath: string = path.join(path.dirname(QQMainPath), 'resources', 'app', 'package.json');
|
||||||
|
export let QQVersionConfigPath: string | undefined = getQQVersionConfigPath(QQMainPath);
|
||||||
|
|
||||||
export const pkgInfoPath = path.join(path.dirname(exePath), 'resources', 'app', 'package.json');
|
//基础信息获取 无快更则启用默认模板填充
|
||||||
let configVersionInfoPath;
|
export let isQuickUpdate: boolean = !!QQVersionConfigPath;
|
||||||
|
export let QQVersionConfig: QQVersionConfigType = isQuickUpdate ? JSON.parse(fs.readFileSync(QQVersionConfigPath!).toString()) : getDefaultQQVersionConfigInfo();
|
||||||
|
export let QQPackageInfo: QQPackageInfoType = JSON.parse(fs.readFileSync(QQPackageInfoPath).toString());
|
||||||
|
export let { appid: QQVersionAppid, qua: QQVersionQua } = getAppidV2();
|
||||||
|
|
||||||
if (os.platform() !== 'linux') {
|
//基础函数
|
||||||
configVersionInfoPath = path.join(path.dirname(exePath), 'resources', 'app', 'versions', 'config.json');
|
export function getQQBuildStr() {
|
||||||
} else {
|
return isQuickUpdate ? QQVersionConfig.buildId : QQPackageInfo.buildVersion;
|
||||||
const userPath = os.homedir();
|
|
||||||
const appDataPath = path.resolve(userPath, './.config/QQ');
|
|
||||||
configVersionInfoPath = path.resolve(appDataPath, './versions/config.json');
|
|
||||||
}
|
}
|
||||||
|
export function getFullQQVesion() {
|
||||||
if (typeof configVersionInfoPath !== 'string') {
|
return isQuickUpdate ? QQVersionConfig.curVersion : QQPackageInfo.version;
|
||||||
throw new Error('Something went wrong when load QQ info path');
|
|
||||||
}
|
}
|
||||||
|
export function requireMinNTQQBuild(buildStr: string) {
|
||||||
export { configVersionInfoPath };
|
return parseInt(getQQBuildStr()) >= parseInt(buildStr);
|
||||||
|
|
||||||
type QQPkgInfo = {
|
|
||||||
version: string;
|
|
||||||
buildVersion: string;
|
|
||||||
platform: string;
|
|
||||||
eleArch: string;
|
|
||||||
}
|
}
|
||||||
type QQVersionConfigInfo = {
|
//此方法不要直接使用
|
||||||
baseVersion: string;
|
export function getQUAInternal() {
|
||||||
curVersion: string;
|
return systemPlatform === 'linux' ? `V1_LNX_NQ_${getFullQQVesion()}_${getQQBuildStr()}_GW_B` : `V1_WIN_NQ_${getFullQQVesion()}_${getQQBuildStr()}_GW_B`;
|
||||||
prevVersion: string;
|
|
||||||
onErrorVersions: Array<any>;
|
|
||||||
buildId: string;
|
|
||||||
}
|
}
|
||||||
|
export function getAppidV2(): { appid: string, qua: string } {
|
||||||
let _qqVersionConfigInfo: QQVersionConfigInfo = {
|
let appidTbale = AppidTable as unknown as QQAppidTableType;
|
||||||
'baseVersion': '9.9.11-24568',
|
|
||||||
'curVersion': '9.9.11-24568',
|
|
||||||
'prevVersion': '',
|
|
||||||
'onErrorVersions': [],
|
|
||||||
'buildId': '24568'
|
|
||||||
};
|
|
||||||
|
|
||||||
if (fs.existsSync(configVersionInfoPath)) {
|
|
||||||
try {
|
try {
|
||||||
const _ =JSON.parse(fs.readFileSync(configVersionInfoPath).toString());
|
let data = appidTbale[getFullQQVesion()];
|
||||||
_qqVersionConfigInfo = Object.assign(_qqVersionConfigInfo, _);
|
if (data) {
|
||||||
} catch (e) {
|
return data;
|
||||||
logError('Load QQ version config info failed, Use default version', e);
|
}
|
||||||
}
|
}
|
||||||
|
catch (e) {
|
||||||
|
log('[QQ版本兼容性检测] 版本兼容性不佳,可能会导致一些功能无法正常使用', e);
|
||||||
|
}
|
||||||
|
// 以下是兜底措施
|
||||||
|
return { appid: systemPlatform === 'linux' ? '537237950' : '537237765', qua: getQUAInternal() };
|
||||||
}
|
}
|
||||||
|
|
||||||
export const qqVersionConfigInfo: QQVersionConfigInfo = _qqVersionConfigInfo;
|
|
||||||
//V1_WIN_NQ_9.9.11_24568_GW_B
|
|
||||||
export const qqPkgInfo: QQPkgInfo = JSON.parse(fs.readFileSync(pkgInfoPath).toString());
|
|
||||||
// platform_type: 3,
|
// platform_type: 3,
|
||||||
// app_type: 4,
|
// app_type: 4,
|
||||||
// app_version: '9.9.9-23159',
|
// app_version: '9.9.12-25765',
|
||||||
// qua: 'V1_WIN_NQ_9.9.9_23159_GW_B',
|
// qua: 'V1_WIN_NQ_9.9.12_25765_GW_B',
|
||||||
// appid: '537213764',
|
// appid: '537234702',
|
||||||
// platVer: '10.0.26100',
|
// platVer: '10.0.26100',
|
||||||
// clientVer: '9.9.9-23159',
|
// clientVer: '9.9.9-25765',
|
||||||
|
|
||||||
// Linux
|
// Linux
|
||||||
// app_version: '3.2.9-24568',
|
// app_version: '3.2.9-25765',
|
||||||
// qua: 'V1_LNX_NQ_3.2.9_24568_GW_B',
|
// qua: 'V1_LNX_NQ_3.2.10_25765_GW_B',
|
||||||
|
|
||||||
let _appid: string = '537226369'; // 默认为 Windows 平台的 appid
|
|
||||||
if (systemPlatform === 'linux') {
|
|
||||||
_appid = '537226441';
|
|
||||||
}
|
|
||||||
// todo: mac 平台的 appid
|
|
||||||
export const appid = _appid;
|
|
||||||
|
@@ -1,9 +1,9 @@
|
|||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import { encode, getDuration, getWavFileInfo, isWav } from 'silk-wasm';
|
import { encode, getDuration, getWavFileInfo, isWav, isSilk } from 'silk-wasm';
|
||||||
import fsPromise from 'fs/promises';
|
import fsPromise from 'fs/promises';
|
||||||
import { log, logError } from './log';
|
import { log, logError } from './log';
|
||||||
import path from 'node:path';
|
import path from 'node:path';
|
||||||
import { v4 as uuidv4 } from 'uuid';
|
import { randomUUID } from 'crypto';
|
||||||
import { spawn } from 'node:child_process';
|
import { spawn } from 'node:child_process';
|
||||||
import { getTempDir } from '@/common/utils/file';
|
import { getTempDir } from '@/common/utils/file';
|
||||||
|
|
||||||
@@ -63,10 +63,11 @@ export async function encodeSilk(filePath: string) {
|
|||||||
// }
|
// }
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const pttPath = path.join(TEMP_DIR, uuidv4());
|
const file = await fsPromise.readFile(filePath);
|
||||||
if (getFileHeader(filePath) !== '02232153494c4b') {
|
const pttPath = path.join(TEMP_DIR, randomUUID());
|
||||||
|
if (!isSilk(file)) {
|
||||||
log(`语音文件${filePath}需要转换成silk`);
|
log(`语音文件${filePath}需要转换成silk`);
|
||||||
const _isWav = await isWavFile(filePath);
|
const _isWav = isWav(file);
|
||||||
const pcmPath = pttPath + '.pcm';
|
const pcmPath = pttPath + '.pcm';
|
||||||
let sampleRate = 0;
|
let sampleRate = 0;
|
||||||
const convert = () => {
|
const convert = () => {
|
||||||
@@ -96,7 +97,7 @@ export async function encodeSilk(filePath: string) {
|
|||||||
if (!_isWav) {
|
if (!_isWav) {
|
||||||
input = await convert();
|
input = await convert();
|
||||||
} else {
|
} else {
|
||||||
input = fs.readFileSync(filePath);
|
input = file;
|
||||||
const allowSampleRate = [8000, 12000, 16000, 24000, 32000, 44100, 48000];
|
const allowSampleRate = [8000, 12000, 16000, 24000, 32000, 44100, 48000];
|
||||||
const { fmt } = getWavFileInfo(input);
|
const { fmt } = getWavFileInfo(input);
|
||||||
// log(`wav文件信息`, fmt)
|
// log(`wav文件信息`, fmt)
|
||||||
@@ -113,7 +114,7 @@ export async function encodeSilk(filePath: string) {
|
|||||||
duration: silk.duration / 1000
|
duration: silk.duration / 1000
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
const silk = fs.readFileSync(filePath);
|
const silk = file;
|
||||||
let duration = 0;
|
let duration = 0;
|
||||||
try {
|
try {
|
||||||
duration = getDuration(silk) / 1000;
|
duration = getDuration(silk) / 1000;
|
||||||
|
@@ -1,451 +0,0 @@
|
|||||||
import { ElementType, FileElement, PicElement, PttElement, RawMessage, VideoElement } from '../../core/src/entities';
|
|
||||||
|
|
||||||
import sqlite3 from 'sqlite3';
|
|
||||||
import { log, logDebug, logError } from '@/common/utils/log';
|
|
||||||
import { NTQQMsgApi } from '@/core';
|
|
||||||
import LRU from '@/common/utils/LRUCache';
|
|
||||||
|
|
||||||
export interface IRember {
|
|
||||||
last_sent_time: number;
|
|
||||||
join_time: number;
|
|
||||||
user_id: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
type DBMsg = {
|
|
||||||
id: number,
|
|
||||||
shortId: number,
|
|
||||||
longId: string,
|
|
||||||
seq: number,
|
|
||||||
peerUid: string,
|
|
||||||
chatType: number,
|
|
||||||
}
|
|
||||||
|
|
||||||
type DBFile = {
|
|
||||||
name: string; // 文件名
|
|
||||||
path: string;
|
|
||||||
url: string;
|
|
||||||
size: number;
|
|
||||||
uuid: string;
|
|
||||||
msgId: string;
|
|
||||||
elementId: string;
|
|
||||||
element: PicElement | VideoElement | FileElement | PttElement;
|
|
||||||
elementType: ElementType.PIC | ElementType.VIDEO | ElementType.FILE | ElementType.PTT;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class DBUtilBase {
|
|
||||||
protected db: sqlite3.Database | undefined;
|
|
||||||
|
|
||||||
async init(dbPath: string) {
|
|
||||||
if (this.db) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
return new Promise<void>((resolve, reject) => {
|
|
||||||
this.db = new sqlite3.Database(dbPath, sqlite3.OPEN_READWRITE | sqlite3.OPEN_CREATE, (err) => {
|
|
||||||
if (err) {
|
|
||||||
logError('Could not connect to database', err);
|
|
||||||
reject(err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.createTable();
|
|
||||||
resolve();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
protected createTable() {
|
|
||||||
throw new Error('Method not implemented.');
|
|
||||||
}
|
|
||||||
|
|
||||||
close() {
|
|
||||||
this.db?.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class DBUtil extends DBUtilBase {
|
|
||||||
private msgCache: Map<string | number, RawMessage> = new Map<string | number, RawMessage>();
|
|
||||||
private globalMsgShortId = -2147483640;
|
|
||||||
private groupIds: number[] = [];
|
|
||||||
private LURCache = new LRU<number>();
|
|
||||||
private LastSentCache = new (class {
|
|
||||||
private cache: { gid: number; uid: number }[] = [];
|
|
||||||
private maxSize: number;
|
|
||||||
|
|
||||||
constructor(maxSize: number = 5000) {
|
|
||||||
this.maxSize = maxSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
get(gid: number, uid: number): boolean {
|
|
||||||
const exists = this.cache.some(
|
|
||||||
(entry) => entry.gid === gid && entry.uid === uid
|
|
||||||
);
|
|
||||||
if (!exists) {
|
|
||||||
this.cache.push({ gid, uid });
|
|
||||||
if (this.cache.length > this.maxSize) {
|
|
||||||
this.cache.shift();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return exists;
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
super();
|
|
||||||
const interval = 1000 * 60 * 10; // 10分钟清理一次缓存
|
|
||||||
setInterval(() => {
|
|
||||||
logDebug('清理消息缓存');
|
|
||||||
this.msgCache.forEach((msg, key) => {
|
|
||||||
if ((Date.now() - parseInt(msg.msgTime) * 1000) > interval) {
|
|
||||||
this.msgCache.delete(key);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}, interval);
|
|
||||||
}
|
|
||||||
|
|
||||||
async init(dbPath: string) {
|
|
||||||
await super.init(dbPath);
|
|
||||||
this.globalMsgShortId = await this.getCurrentMaxShortId();
|
|
||||||
|
|
||||||
|
|
||||||
// 初始化群缓存列表
|
|
||||||
this.db!.serialize(() => {
|
|
||||||
const sql = 'SELECT * FROM sqlite_master WHERE type=\'table\'';
|
|
||||||
this.db!.all(sql, [], (err, rows: { name: string }[]) => {
|
|
||||||
if (err) return logError(err);
|
|
||||||
rows.forEach((row) => this.groupIds.push(parseInt(row.name)));
|
|
||||||
//logDebug(`已加载 ${groupIds.length} 个群`);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
this.LURCache.on(async (node) => {
|
|
||||||
const { value: time, groupId, userId } = node;
|
|
||||||
|
|
||||||
logDebug('插入发言时间', userId, groupId);
|
|
||||||
await this.createGroupInfoTimeTableIfNotExist(groupId);
|
|
||||||
|
|
||||||
const method = await this.getDataSetMethod(groupId, userId);
|
|
||||||
logDebug('插入发言时间方法判断', userId, groupId, method);
|
|
||||||
|
|
||||||
const sql =
|
|
||||||
method == 'update'
|
|
||||||
? `UPDATE "${groupId}" SET last_sent_time = ? WHERE user_id = ?`
|
|
||||||
: `INSERT INTO "${groupId}" (last_sent_time, user_id) VALUES (?, ?)`;
|
|
||||||
|
|
||||||
this.db!.all(sql, [time, userId], (err) => {
|
|
||||||
if (err) {
|
|
||||||
return logError('插入/更新发言时间失败', userId, groupId);
|
|
||||||
}
|
|
||||||
logDebug('插入/更新发言时间成功', userId, groupId);
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
}
|
|
||||||
async getDataSetMethod(groupId: number, userId: number) {
|
|
||||||
// 缓存记录
|
|
||||||
if (this.LastSentCache.get(groupId, userId)) {
|
|
||||||
logDebug('缓存命中', userId, groupId);
|
|
||||||
return 'update';
|
|
||||||
}
|
|
||||||
|
|
||||||
// 数据库判断
|
|
||||||
return new Promise<'insert' | 'update'>((resolve, reject) => {
|
|
||||||
this.db!.all(
|
|
||||||
`SELECT * FROM "${groupId}" WHERE user_id = ?`,
|
|
||||||
[userId],
|
|
||||||
(err, rows) => {
|
|
||||||
if (err) {
|
|
||||||
logError('查询发言时间存在失败', userId, groupId, err);
|
|
||||||
return logError('插入发言时间失败', userId, groupId, err);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rows.length === 0) {
|
|
||||||
logDebug('查询发言时间不存在', userId, groupId);
|
|
||||||
return resolve('insert');
|
|
||||||
}
|
|
||||||
|
|
||||||
logDebug('查询发言时间存在', userId, groupId);
|
|
||||||
resolve('update');
|
|
||||||
}
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
async createGroupInfoTimeTableIfNotExist(groupId: number) {
|
|
||||||
const createTableSQL = (groupId: number) =>
|
|
||||||
`CREATE TABLE IF NOT EXISTS "${groupId}" (
|
|
||||||
user_id INTEGER,
|
|
||||||
last_sent_time INTEGER,
|
|
||||||
join_time INTEGER,
|
|
||||||
PRIMARY KEY (user_id)
|
|
||||||
);`;
|
|
||||||
|
|
||||||
if (this.groupIds.includes(groupId)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
const sql = createTableSQL(groupId);
|
|
||||||
this.db!.all(sql, (err) => {
|
|
||||||
if (err) {
|
|
||||||
reject(err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.groupIds.push(groupId);
|
|
||||||
resolve(true);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
protected createTable() {
|
|
||||||
// 消息记录
|
|
||||||
const createTableSQL = `
|
|
||||||
CREATE TABLE IF NOT EXISTS msgs (
|
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
||||||
shortId INTEGER NOT NULL UNIQUE,
|
|
||||||
longId TEXT NOT NULL UNIQUE,
|
|
||||||
seq INTEGER NOT NULL,
|
|
||||||
peerUid TEXT NOT NULL,
|
|
||||||
chatType INTEGER NOT NULL
|
|
||||||
)`;
|
|
||||||
this.db!.run(createTableSQL, function (err) {
|
|
||||||
if (err) {
|
|
||||||
logError('Could not create table msgs', err.stack);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// 文件缓存
|
|
||||||
const createFileTableSQL = `
|
|
||||||
CREATE TABLE IF NOT EXISTS files (
|
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
||||||
name TEXT NOT NULL,
|
|
||||||
path TEXT NOT NULL,
|
|
||||||
url TEXT,
|
|
||||||
size INTEGER NOT NULL,
|
|
||||||
uuid TEXT,
|
|
||||||
elementType INTEGER,
|
|
||||||
element TEXT NOT NULL,
|
|
||||||
elementId TEXT NOT NULL,
|
|
||||||
msgId TEXT NOT NULL
|
|
||||||
)`;
|
|
||||||
this.db!.run(createFileTableSQL, function (err) {
|
|
||||||
if (err) {
|
|
||||||
logError('Could not create table files', err);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private async getCurrentMaxShortId() {
|
|
||||||
return new Promise<number>((resolve, reject) => {
|
|
||||||
this.db!.get('SELECT MAX(shortId) as maxId FROM msgs', (err, row: { maxId: number }) => {
|
|
||||||
if (err) {
|
|
||||||
logDebug('Could not get max short id, Use default -2147483640', err);
|
|
||||||
return resolve(-2147483640);
|
|
||||||
}
|
|
||||||
logDebug('数据库中消息最大短id', row?.maxId);
|
|
||||||
resolve(row?.maxId ?? -2147483640);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private async getMsg(query: string, params: any[]) {
|
|
||||||
const stmt = this.db!.prepare(query);
|
|
||||||
return new Promise<RawMessage | null>((resolve, reject) => {
|
|
||||||
stmt.get(...params, (err: any, row: DBMsg) => {
|
|
||||||
// log("getMsg", row, err);
|
|
||||||
if (err) {
|
|
||||||
logError('Could not get msg', err, query, params);
|
|
||||||
return resolve(null);
|
|
||||||
}
|
|
||||||
if (!row) {
|
|
||||||
// logDebug('不存在数据库中的消息,不进行处理', query, params);
|
|
||||||
resolve(null);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const msgId = row.longId;
|
|
||||||
NTQQMsgApi.getMsgsByMsgId({ peerUid: row.peerUid, chatType: row.chatType }, [msgId]).then(res => {
|
|
||||||
const msg = res.msgList[0];
|
|
||||||
if (!msg) {
|
|
||||||
resolve(null);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
msg.id = row.shortId;
|
|
||||||
resolve(msg);
|
|
||||||
}).catch(e => {
|
|
||||||
resolve(null);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async getMsgByShortId(shortId: number): Promise<RawMessage | null> {
|
|
||||||
if (this.msgCache.has(shortId)) {
|
|
||||||
return this.msgCache.get(shortId)!;
|
|
||||||
}
|
|
||||||
const getStmt = 'SELECT * FROM msgs WHERE shortId = ?';
|
|
||||||
return this.getMsg(getStmt, [shortId]);
|
|
||||||
}
|
|
||||||
|
|
||||||
async getMsgByLongId(longId: string): Promise<RawMessage | null> {
|
|
||||||
if (this.msgCache.has(longId)) {
|
|
||||||
return this.msgCache.get(longId)!;
|
|
||||||
}
|
|
||||||
return this.getMsg('SELECT * FROM msgs WHERE longId = ?', [longId]);
|
|
||||||
}
|
|
||||||
|
|
||||||
async getMsgBySeq(peerUid: string, seq: string): Promise<RawMessage | null> {
|
|
||||||
const stmt = 'SELECT * FROM msgs WHERE peerUid = ? AND seq = ?';
|
|
||||||
return this.getMsg(stmt, [peerUid, seq]);
|
|
||||||
}
|
|
||||||
|
|
||||||
async addMsg(msg: RawMessage, update = true): Promise<number> {
|
|
||||||
const existMsg = await this.getMsgByLongId(msg.msgId);
|
|
||||||
if (existMsg) {
|
|
||||||
// logDebug('消息已存在,更新数据库', msg.msgId);
|
|
||||||
if (update) this.updateMsg(msg).then();
|
|
||||||
return existMsg.id!;
|
|
||||||
}
|
|
||||||
const stmt = this.db!.prepare('INSERT INTO msgs (shortId, longId, seq, peerUid, chatType) VALUES (?, ?, ?, ?, ?)');
|
|
||||||
// const runAsync = promisify(stmt.run.bind(stmt));
|
|
||||||
const shortId = ++this.globalMsgShortId;
|
|
||||||
msg.id = shortId;
|
|
||||||
//logDebug(`记录消息到数据库, 消息长id: ${msg.msgId}, 短id: ${msg.id}`);
|
|
||||||
this.msgCache.set(shortId, msg);
|
|
||||||
this.msgCache.set(msg.msgId, msg);
|
|
||||||
stmt.run(this.globalMsgShortId, msg.msgId, msg.msgSeq.toString(), msg.peerUid, msg.chatType, (err: any) => {
|
|
||||||
if (err) {
|
|
||||||
if (err.errno === 19) {
|
|
||||||
this.getMsgByLongId(msg.msgId).then((msg: RawMessage | null) => {
|
|
||||||
if (msg) {
|
|
||||||
this.msgCache.set(shortId, msg);
|
|
||||||
this.msgCache.set(msg.msgId, msg);
|
|
||||||
// logDebug('获取消息短id成功', msg.id);
|
|
||||||
} else {
|
|
||||||
logError('db could not get msg by long id', err);
|
|
||||||
}
|
|
||||||
}).catch(e => logError('db getMsgByLongId error', e));
|
|
||||||
} else {
|
|
||||||
logError('db could not add msg', err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return shortId;
|
|
||||||
}
|
|
||||||
|
|
||||||
async updateMsg(msg: RawMessage) {
|
|
||||||
const existMsg = this.msgCache.get(msg.msgId);
|
|
||||||
if (existMsg) {
|
|
||||||
Object.assign(existMsg, msg);
|
|
||||||
}
|
|
||||||
//logDebug(`更新消息, shortId:${msg.id}, seq: ${msg.msgSeq}, msgId: ${msg.msgId}`);
|
|
||||||
const stmt = this.db!.prepare('UPDATE msgs SET seq=? WHERE longId=?');
|
|
||||||
stmt.run(msg.msgSeq, msg.msgId, (err: any) => {
|
|
||||||
if (err) {
|
|
||||||
logError('updateMsg db error', err);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
async addFileCache(file: DBFile) {
|
|
||||||
const stmt = this.db!.prepare('INSERT INTO files (name, path, url, size, uuid, elementType ,element, elementId, msgId) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)');
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
stmt.run(file.name, file.path, file.url, file.size, file.uuid,
|
|
||||||
file.elementType,
|
|
||||||
JSON.stringify(file.element),
|
|
||||||
file.elementId,
|
|
||||||
file.msgId,
|
|
||||||
function (err: any) {
|
|
||||||
if (err) {
|
|
||||||
logError('db could not add file', err);
|
|
||||||
reject(err);
|
|
||||||
}
|
|
||||||
resolve(null);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private async getFileCache(query: string, params: any[]) {
|
|
||||||
const stmt = this.db!.prepare(query);
|
|
||||||
return new Promise<DBFile | null>((resolve, reject) => {
|
|
||||||
stmt.get(...params, (err: any, row: DBFile & { element: string }) => {
|
|
||||||
if (err) {
|
|
||||||
logError('db could not get file cache', err);
|
|
||||||
reject(err);
|
|
||||||
}
|
|
||||||
if (row) {
|
|
||||||
row.element = JSON.parse(row.element);
|
|
||||||
}
|
|
||||||
resolve(row);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async getFileCacheByName(name: string): Promise<DBFile | null> {
|
|
||||||
return this.getFileCache('SELECT * FROM files WHERE name = ?', [name]);
|
|
||||||
}
|
|
||||||
|
|
||||||
async getFileCacheByUuid(uuid: string): Promise<DBFile | null> {
|
|
||||||
return this.getFileCache('SELECT * FROM files WHERE uuid = ?', [uuid]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// todo: 是否所有的文件都有uuid?语音消息有没有uuid?
|
|
||||||
async updateFileCache(file: DBFile) {
|
|
||||||
const stmt = this.db!.prepare('UPDATE files SET path = ?, url = ? WHERE uuid = ?');
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
stmt.run(file.path, file.url, file.uuid, function (err: any) {
|
|
||||||
if (err) {
|
|
||||||
logError('db could not update file cache', err);
|
|
||||||
reject(err);
|
|
||||||
}
|
|
||||||
resolve(null);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async getLastSentTimeAndJoinTime(
|
|
||||||
groupId: number
|
|
||||||
): Promise<IRember[]> {
|
|
||||||
logDebug('读取发言时间', groupId);
|
|
||||||
return new Promise<IRember[]>((resolve, reject) => {
|
|
||||||
this.db!.all(`SELECT * FROM "${groupId}" `, (err, rows: IRember[]) => {
|
|
||||||
const cache = this.LURCache.get(groupId).map(e=>({user_id:e.userId, last_sent_time:e.value}));
|
|
||||||
if (err) {
|
|
||||||
logError('查询发言时间失败', groupId);
|
|
||||||
return resolve(cache.map(e=>({...e, join_time:0})));
|
|
||||||
}
|
|
||||||
Object.assign(rows, cache)
|
|
||||||
logDebug('查询发言时间成功', groupId, rows);
|
|
||||||
resolve(rows);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
insertLastSentTime(
|
|
||||||
groupId: number,
|
|
||||||
userId: number,
|
|
||||||
time: number
|
|
||||||
) {
|
|
||||||
this.LURCache.set(groupId, userId, time);
|
|
||||||
}
|
|
||||||
async insertJoinTime(
|
|
||||||
groupId: number,
|
|
||||||
userId: number,
|
|
||||||
time: number
|
|
||||||
) {
|
|
||||||
await this.createGroupInfoTimeTableIfNotExist(groupId);
|
|
||||||
this.db!.all(
|
|
||||||
`INSERT OR REPLACE INTO "${groupId}" (user_id, last_sent_time, join_time) VALUES (?,?,?)`,
|
|
||||||
[userId, time, time],
|
|
||||||
(err) => {
|
|
||||||
if (err)
|
|
||||||
logError(err),
|
|
||||||
Promise.reject(),
|
|
||||||
logError('插入入群时间失败', userId, groupId);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
export const dbUtil = new DBUtil();
|
|
@@ -4,9 +4,8 @@ import crypto from 'crypto';
|
|||||||
import util from 'util';
|
import util from 'util';
|
||||||
import path from 'node:path';
|
import path from 'node:path';
|
||||||
import { log, logError } from './log';
|
import { log, logError } from './log';
|
||||||
import { dbUtil } from '@/common/utils/db';
|
|
||||||
import * as fileType from 'file-type';
|
import * as fileType from 'file-type';
|
||||||
import { v4 as uuidv4 } from 'uuid';
|
import { randomUUID } from 'crypto';
|
||||||
import { napCatCore } from '@/core';
|
import { napCatCore } from '@/core';
|
||||||
|
|
||||||
export const getNapCatDir = () => {
|
export const getNapCatDir = () => {
|
||||||
@@ -192,7 +191,7 @@ export async function uri2local(uri: string, fileName: string | null = null): Pr
|
|||||||
isLocal: false
|
isLocal: false
|
||||||
};
|
};
|
||||||
if (!fileName) {
|
if (!fileName) {
|
||||||
fileName = uuidv4();
|
fileName = randomUUID();
|
||||||
}
|
}
|
||||||
let filePath = path.join(getTempDir(), fileName);
|
let filePath = path.join(getTempDir(), fileName);
|
||||||
let url = null;
|
let url = null;
|
||||||
@@ -235,7 +234,7 @@ export async function uri2local(uri: string, fileName: string | null = null): Pr
|
|||||||
}
|
}
|
||||||
fileName = fileName.replace(/[/\\:*?"<>|]/g, '_');
|
fileName = fileName.replace(/[/\\:*?"<>|]/g, '_');
|
||||||
res.fileName = fileName;
|
res.fileName = fileName;
|
||||||
filePath = path.join(getTempDir(), uuidv4() + fileName);
|
filePath = path.join(getTempDir(), randomUUID() + fileName);
|
||||||
fs.writeFileSync(filePath, buffer);
|
fs.writeFileSync(filePath, buffer);
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
res.errMsg = `${url}下载失败,` + e.toString();
|
res.errMsg = `${url}下载失败,` + e.toString();
|
||||||
@@ -251,13 +250,14 @@ export async function uri2local(uri: string, fileName: string | null = null): Pr
|
|||||||
} else {
|
} else {
|
||||||
filePath = pathname;
|
filePath = pathname;
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
const cache = await dbUtil.getFileCacheByName(uri);
|
else {
|
||||||
if (cache) {
|
// const cache = await dbUtil.getFileCacheByName(uri);
|
||||||
filePath = cache.path;
|
// if (cache) {
|
||||||
} else {
|
// filePath = cache.path;
|
||||||
filePath = uri;
|
// } else {
|
||||||
}
|
// filePath = uri;
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
res.isLocal = true;
|
res.isLocal = true;
|
||||||
|
@@ -1,17 +1,58 @@
|
|||||||
import crypto from 'node:crypto';
|
import crypto from 'node:crypto';
|
||||||
import path from 'node:path';
|
import path from 'node:path';
|
||||||
import fs from 'fs/promises';
|
import fs from 'fs';
|
||||||
import { log, logDebug } from './log';
|
import { log, logDebug } from './log';
|
||||||
import { dirname } from 'node:path';
|
import { dirname } from 'node:path';
|
||||||
import { fileURLToPath } from 'node:url';
|
import { fileURLToPath } from 'node:url';
|
||||||
import * as fsPromise from 'node:fs/promises';
|
import * as fsPromise from 'node:fs/promises';
|
||||||
|
import os from 'node:os';
|
||||||
|
|
||||||
const __filename = fileURLToPath(import.meta.url);
|
const __filename = fileURLToPath(import.meta.url);
|
||||||
const __dirname = dirname(__filename);
|
const __dirname = dirname(__filename);
|
||||||
|
|
||||||
|
//下面这个类是用于将uid+msgid合并的类
|
||||||
|
export class UUIDConverter {
|
||||||
|
static encode(highStr: string, lowStr: string): string {
|
||||||
|
const high = BigInt(highStr);
|
||||||
|
const low = BigInt(lowStr);
|
||||||
|
const highHex = high.toString(16).padStart(16, '0');
|
||||||
|
const lowHex = low.toString(16).padStart(16, '0');
|
||||||
|
const combinedHex = highHex + lowHex;
|
||||||
|
const uuid = `${combinedHex.substring(0, 8)}-${combinedHex.substring(8, 12)}-${combinedHex.substring(12, 16)}-${combinedHex.substring(16, 20)}-${combinedHex.substring(20)}`;
|
||||||
|
return uuid;
|
||||||
|
}
|
||||||
|
static decode(uuid: string): { high: string, low: string } {
|
||||||
|
const hex = uuid.replace(/-/g, '');
|
||||||
|
const high = BigInt('0x' + hex.substring(0, 16));
|
||||||
|
const low = BigInt('0x' + hex.substring(16));
|
||||||
|
return { high: high.toString(), low: low.toString() };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
export function sleep(ms: number): Promise<void> {
|
export function sleep(ms: number): Promise<void> {
|
||||||
return new Promise(resolve => setTimeout(resolve, ms));
|
return new Promise(resolve => setTimeout(resolve, ms));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function PromiseTimer<T>(promise: Promise<T>, ms: number): Promise<T> {
|
||||||
|
const timeoutPromise = new Promise<T>((_, reject) =>
|
||||||
|
setTimeout(() => reject(new Error('PromiseTimer: Operation timed out')), ms)
|
||||||
|
);
|
||||||
|
return Promise.race([promise, timeoutPromise]);
|
||||||
|
}
|
||||||
|
export async function runAllWithTimeout<T>(tasks: Promise<T>[], timeout: number): Promise<T[]> {
|
||||||
|
const wrappedTasks = tasks.map(task =>
|
||||||
|
PromiseTimer(task, timeout).then(
|
||||||
|
result => ({ status: 'fulfilled', value: result }),
|
||||||
|
error => ({ status: 'rejected', reason: error })
|
||||||
|
)
|
||||||
|
);
|
||||||
|
const results = await Promise.all(wrappedTasks);
|
||||||
|
return results
|
||||||
|
.filter(result => result.status === 'fulfilled')
|
||||||
|
.map(result => (result as { status: 'fulfilled'; value: T }).value);
|
||||||
|
}
|
||||||
|
|
||||||
export function getMd5(s: string) {
|
export function getMd5(s: string) {
|
||||||
|
|
||||||
const h = crypto.createHash('md5');
|
const h = crypto.createHash('md5');
|
||||||
@@ -89,7 +130,36 @@ export function CacheClassFuncAsync(ttl: number = 3600 * 1000, customKey: string
|
|||||||
}
|
}
|
||||||
return logExecutionTime;
|
return logExecutionTime;
|
||||||
}
|
}
|
||||||
|
export function CacheClassFuncAsyncExtend(ttl: number = 3600 * 1000, customKey: string = '', checker: any = (...data: any[]) => { return true; }) {
|
||||||
|
//console.log('CacheClassFuncAsync', ttl, customKey);
|
||||||
|
function logExecutionTime(target: any, methodName: string, descriptor: PropertyDescriptor) {
|
||||||
|
//console.log('logExecutionTime', target, methodName, descriptor);
|
||||||
|
const cache = new Map<string, { expiry: number; value: any }>();
|
||||||
|
const originalMethod = descriptor.value;
|
||||||
|
descriptor.value = async function (...args: any[]) {
|
||||||
|
const key = `${customKey}${String(methodName)}.(${args.map(arg => JSON.stringify(arg)).join(', ')})`;
|
||||||
|
cache.forEach((value, key) => {
|
||||||
|
if (value.expiry < Date.now()) {
|
||||||
|
cache.delete(key);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const cachedValue = cache.get(key);
|
||||||
|
if (cachedValue && cachedValue.expiry > Date.now()) {
|
||||||
|
return cachedValue.value;
|
||||||
|
}
|
||||||
|
// const start = Date.now();
|
||||||
|
const result = await originalMethod.apply(this, args);
|
||||||
|
if (!checker(...args, result)) {
|
||||||
|
return result;//丢弃缓存
|
||||||
|
}
|
||||||
|
// const end = Date.now();
|
||||||
|
// console.log(`Method ${methodName} executed in ${end - start} ms.`);
|
||||||
|
cache.set(key, { expiry: Date.now() + ttl, value: result });
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return logExecutionTime;
|
||||||
|
}
|
||||||
// export function CacheClassFuncAsync(ttl: number = 3600 * 1000, customKey: string = ''): any {
|
// export function CacheClassFuncAsync(ttl: number = 3600 * 1000, customKey: string = ''): any {
|
||||||
// const cache = new Map<string, { expiry: number; value: any }>();
|
// const cache = new Map<string, { expiry: number; value: any }>();
|
||||||
|
|
||||||
@@ -235,14 +305,14 @@ export function migrateConfig(oldConfig: any) {
|
|||||||
}
|
}
|
||||||
// 升级旧的配置到新的
|
// 升级旧的配置到新的
|
||||||
export async function UpdateConfig() {
|
export async function UpdateConfig() {
|
||||||
const configFiles = await fs.readdir(path.join(__dirname, 'config'));
|
const configFiles = await fsPromise.readdir(path.join(__dirname, 'config'));
|
||||||
for (const file of configFiles) {
|
for (const file of configFiles) {
|
||||||
if (file.match(/^onebot11_\d+.json$/)) {
|
if (file.match(/^onebot11_\d+.json$/)) {
|
||||||
const CurrentConfig = JSON.parse(await fs.readFile(path.join(__dirname, 'config', file), 'utf8'));
|
const CurrentConfig = JSON.parse(await fsPromise.readFile(path.join(__dirname, 'config', file), 'utf8'));
|
||||||
if (isValidOldConfig(CurrentConfig)) {
|
if (isValidOldConfig(CurrentConfig)) {
|
||||||
log('正在迁移旧配置到新配置 File:', file);
|
log('正在迁移旧配置到新配置 File:', file);
|
||||||
const NewConfig = migrateConfig(CurrentConfig);
|
const NewConfig = migrateConfig(CurrentConfig);
|
||||||
await fs.writeFile(path.join(__dirname, 'config', file), JSON.stringify(NewConfig, null, 2));
|
await fsPromise.writeFile(path.join(__dirname, 'config', file), JSON.stringify(NewConfig, null, 2));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -262,7 +332,56 @@ export function isEqual(obj1: any, obj2: any) {
|
|||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
export function getDefaultQQVersionConfigInfo(): QQVersionConfigType {
|
||||||
|
if (os.platform() === 'linux') {
|
||||||
|
return {
|
||||||
|
baseVersion: '3.2.12-26702',
|
||||||
|
curVersion: '3.2.12-26702',
|
||||||
|
prevVersion: '',
|
||||||
|
onErrorVersions: [],
|
||||||
|
buildId: '26702'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
baseVersion: '9.9.15-26702',
|
||||||
|
curVersion: '9.9.15-26702',
|
||||||
|
prevVersion: '',
|
||||||
|
onErrorVersions: [],
|
||||||
|
buildId: '26702'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
export async function promisePipeline(promises: Promise<any>[], callback: (result: any) => boolean): Promise<void> {
|
||||||
|
let callbackCalled = false;
|
||||||
|
for (const promise of promises) {
|
||||||
|
if (callbackCalled) break;
|
||||||
|
try {
|
||||||
|
const result = await promise;
|
||||||
|
if (!callbackCalled) {
|
||||||
|
callbackCalled = callback(result);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error in promise pipeline:", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getQQVersionConfigPath(exePath: string = ""): string | undefined {
|
||||||
|
let configVersionInfoPath;
|
||||||
|
if (os.platform() !== 'linux') {
|
||||||
|
configVersionInfoPath = path.join(path.dirname(exePath), 'resources', 'app', 'versions', 'config.json');
|
||||||
|
} else {
|
||||||
|
const userPath = os.homedir();
|
||||||
|
const appDataPath = path.resolve(userPath, './.config/QQ');
|
||||||
|
configVersionInfoPath = path.resolve(appDataPath, './versions/config.json');
|
||||||
|
}
|
||||||
|
if (typeof configVersionInfoPath !== 'string') {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
if (!fs.existsSync(configVersionInfoPath)) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
return configVersionInfoPath;
|
||||||
|
}
|
||||||
export async function deleteOldFiles(directoryPath: string, daysThreshold: number) {
|
export async function deleteOldFiles(directoryPath: string, daysThreshold: number) {
|
||||||
try {
|
try {
|
||||||
const files = await fsPromise.readdir(directoryPath);
|
const files = await fsPromise.readdir(directoryPath);
|
||||||
|
@@ -91,8 +91,10 @@ export function enableConsoleLog(enable: boolean) {
|
|||||||
function formatMsg(msg: any[]) {
|
function formatMsg(msg: any[]) {
|
||||||
let logMsg = '';
|
let logMsg = '';
|
||||||
for (const msgItem of msg) {
|
for (const msgItem of msg) {
|
||||||
// 判断是否是对象
|
if (msgItem instanceof Error) { // 判断是否是错误
|
||||||
if (typeof msgItem === 'object') {
|
logMsg += msgItem.stack + ' ';
|
||||||
|
continue;
|
||||||
|
} else if (typeof msgItem === 'object') { // 判断是否是对象
|
||||||
const obj = JSON.parse(JSON.stringify(msgItem, null, 2));
|
const obj = JSON.parse(JSON.stringify(msgItem, null, 2));
|
||||||
logMsg += JSON.stringify(truncateString(obj)) + ' ';
|
logMsg += JSON.stringify(truncateString(obj)) + ' ';
|
||||||
continue;
|
continue;
|
||||||
|
@@ -7,7 +7,7 @@ export class RequestUtil {
|
|||||||
static async HttpsGetCookies(url: string): Promise<{ [key: string]: string }> {
|
static async HttpsGetCookies(url: string): Promise<{ [key: string]: string }> {
|
||||||
const client = url.startsWith('https') ? https : http;
|
const client = url.startsWith('https') ? https : http;
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
let req = client.get(url, (res) => {
|
const req = client.get(url, (res) => {
|
||||||
let cookies: { [key: string]: string } = {};
|
let cookies: { [key: string]: string } = {};
|
||||||
const handleRedirect = (res: http.IncomingMessage) => {
|
const handleRedirect = (res: http.IncomingMessage) => {
|
||||||
//console.log(res.headers.location);
|
//console.log(res.headers.location);
|
||||||
@@ -44,7 +44,7 @@ export class RequestUtil {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
req.on('error', (error: any) => {
|
req.on('error', (error: any) => {
|
||||||
reject(error);
|
reject(error);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
import os from 'node:os';
|
import os from 'node:os';
|
||||||
import path from 'node:path';
|
import path from 'node:path';
|
||||||
import { networkInterfaces } from 'os';
|
import { networkInterfaces } from 'os';
|
||||||
import { v4 as uuidv4 } from 'uuid';
|
import { randomUUID } from 'crypto';
|
||||||
|
|
||||||
// 缓解Win7设备兼容性问题
|
// 缓解Win7设备兼容性问题
|
||||||
let osName: string;
|
let osName: string;
|
||||||
@@ -30,7 +30,7 @@ export async function getMachineId(): Promise<string> {
|
|||||||
if (!machineId) {
|
if (!machineId) {
|
||||||
machineId = (async () => {
|
machineId = (async () => {
|
||||||
const id = await getMacMachineId();
|
const id = await getMacMachineId();
|
||||||
return id || uuidv4(); // fallback, generate a UUID
|
return id || randomUUID(); // fallback, generate a UUID
|
||||||
})();
|
})();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,31 +1,17 @@
|
|||||||
/**
|
//QQVersionType
|
||||||
* 运行时类型转换与检查类
|
type QQPackageInfoType = {
|
||||||
*/
|
version: string;
|
||||||
export class TypeCheck {
|
buildVersion: string;
|
||||||
static isEmpty(value: any): boolean {
|
platform: string;
|
||||||
return value === null || value === undefined || value === '' ||
|
eleArch: string;
|
||||||
(Array.isArray(value) && value.length === 0) || (typeof value === 'object' && Object.keys(value).length === 0);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
type QQVersionConfigType = {
|
||||||
export class TypeConvert {
|
baseVersion: string;
|
||||||
static toNumber(value: any): number {
|
curVersion: string;
|
||||||
const num = Number(value);
|
prevVersion: string;
|
||||||
if (isNaN(num)) {
|
onErrorVersions: Array<any>;
|
||||||
throw new Error(`无法将输入转换为数字: ${value}`);
|
buildId: string;
|
||||||
}
|
}
|
||||||
return num;
|
type QQAppidTableType = {
|
||||||
}
|
[key: string]: { appid: string, qua: string };
|
||||||
|
|
||||||
static toString(value: any): string {
|
|
||||||
return String(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
static toBoolean(value: any): boolean {
|
|
||||||
return Boolean(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
static toArray(value: any): any[] {
|
|
||||||
return Array.isArray(value) ? value : [value];
|
|
||||||
}
|
|
||||||
}
|
}
|
@@ -4,10 +4,10 @@ export async function checkVersion(): Promise<string> {
|
|||||||
return new Promise(async (resolve, reject) => {
|
return new Promise(async (resolve, reject) => {
|
||||||
const MirrorList =
|
const MirrorList =
|
||||||
[
|
[
|
||||||
|
'https://jsd.cdn.zzko.cn/gh/NapNeko/NapCatQQ@main/package.json',
|
||||||
'https://fastly.jsdelivr.net/gh/NapNeko/NapCatQQ@main/package.json',
|
'https://fastly.jsdelivr.net/gh/NapNeko/NapCatQQ@main/package.json',
|
||||||
'https://gcore.jsdelivr.net/gh/NapNeko/NapCatQQ@main/package.json',
|
'https://gcore.jsdelivr.net/gh/NapNeko/NapCatQQ@main/package.json',
|
||||||
'https://cdn.jsdelivr.us/gh/NapNeko/NapCatQQ@main/package.json',
|
'https://cdn.jsdelivr.net/gh/NapNeko/NapCatQQ@main/package.json'
|
||||||
'https://jsd.cdn.zzko.cn/gh/NapNeko/NapCatQQ@main/package.json'
|
|
||||||
];
|
];
|
||||||
let version = undefined;
|
let version = undefined;
|
||||||
for (const url of MirrorList) {
|
for (const url of MirrorList) {
|
||||||
|
File diff suppressed because one or more lines are too long
@@ -1,12 +1,9 @@
|
|||||||
import {
|
import {
|
||||||
CacheFileList,
|
|
||||||
CacheFileListItem,
|
CacheFileListItem,
|
||||||
CacheFileType,
|
CacheFileType,
|
||||||
CacheScanResult,
|
|
||||||
ChatCacheList,
|
|
||||||
ChatCacheListItemBasic,
|
ChatCacheListItemBasic,
|
||||||
ChatType,
|
ChatType,
|
||||||
ElementType, IMAGE_HTTP_HOST, IMAGE_HTTP_HOST_NT, RawMessage
|
ElementType, IMAGE_HTTP_HOST, IMAGE_HTTP_HOST_NT, Peer, PicElement, RawMessage
|
||||||
} from '@/core/entities';
|
} from '@/core/entities';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
@@ -15,31 +12,15 @@ import { log, logDebug, logError } from '@/common/utils/log';
|
|||||||
import { GeneralCallResult, napCatCore, OnRichMediaDownloadCompleteParams } from '@/core';
|
import { GeneralCallResult, napCatCore, OnRichMediaDownloadCompleteParams } from '@/core';
|
||||||
import { calculateFileMD5 } from '@/common/utils/file';
|
import { calculateFileMD5 } from '@/common/utils/file';
|
||||||
import * as fileType from 'file-type';
|
import * as fileType from 'file-type';
|
||||||
import { MsgListener } from '@/core/listeners';
|
|
||||||
import imageSize from 'image-size';
|
import imageSize from 'image-size';
|
||||||
import { ISizeCalculationResult } from 'image-size/dist/types/interface';
|
import { ISizeCalculationResult } from 'image-size/dist/types/interface';
|
||||||
import { sessionConfig } from '@/core/sessionConfig';
|
import { sessionConfig } from '@/core/sessionConfig';
|
||||||
import { randomUUID } from 'crypto';
|
|
||||||
import { rkeyManager } from '../utils/rkey';
|
import { rkeyManager } from '../utils/rkey';
|
||||||
import { AsyncQueue } from '@/common/utils/AsyncQueue';
|
import { NTEventDispatch } from '@/common/utils/EventTask';
|
||||||
// const rkeyExpireTime = 1000;
|
import { NodeIKernelSearchService } from '../services/NodeIKernelSearchService';
|
||||||
|
import { selfInfo } from '../data';
|
||||||
|
|
||||||
const getRKeyTaskQueue = new AsyncQueue();
|
|
||||||
|
|
||||||
const downloadMediaTasks: Map<string, (arg: OnRichMediaDownloadCompleteParams) => void> = new Map<string, (arg: OnRichMediaDownloadCompleteParams) => void>();
|
|
||||||
|
|
||||||
const downloadMediaListener = new MsgListener();
|
|
||||||
downloadMediaListener.onRichMediaDownloadComplete = arg => {
|
|
||||||
for (const [uuid, cb] of downloadMediaTasks) {
|
|
||||||
cb(arg);
|
|
||||||
downloadMediaTasks.delete(uuid);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
setTimeout(() => {
|
|
||||||
napCatCore.onLoginSuccess(() => {
|
|
||||||
napCatCore.addListener(downloadMediaListener);
|
|
||||||
});
|
|
||||||
}, 100);
|
|
||||||
export class NTQQFileApi {
|
export class NTQQFileApi {
|
||||||
static async getFileType(filePath: string) {
|
static async getFileType(filePath: string) {
|
||||||
return fileType.fileTypeFromFile(filePath);
|
return fileType.fileTypeFromFile(filePath);
|
||||||
@@ -52,12 +33,8 @@ export class NTQQFileApi {
|
|||||||
static async getFileSize(filePath: string): Promise<number> {
|
static async getFileSize(filePath: string): Promise<number> {
|
||||||
return await napCatCore.util.getFileSize(filePath);
|
return await napCatCore.util.getFileSize(filePath);
|
||||||
}
|
}
|
||||||
static async getVideoUrl(msg: RawMessage, element: any) {
|
static async getVideoUrl(peer: Peer, msgId: string, elementId: string) {
|
||||||
return (await napCatCore.session.getRichMediaService().getVideoPlayUrlV2({
|
return (await napCatCore.session.getRichMediaService().getVideoPlayUrlV2(peer, msgId, elementId, 0, { downSourceType: 1, triggerType: 1 })).urlResult.domainUrl;
|
||||||
chatType: msg.chatType,
|
|
||||||
peerUid: msg.peerUid,
|
|
||||||
guildId: '0'
|
|
||||||
}, msg.msgId, element.elementId, 0, { downSourceType: 1, triggerType: 1 })).urlResult.domainUrl[0].url;
|
|
||||||
}
|
}
|
||||||
// 上传文件到QQ的文件夹
|
// 上传文件到QQ的文件夹
|
||||||
static async uploadFile(filePath: string, elementType: ElementType = ElementType.PIC, elementSubType: number = 0) {
|
static async uploadFile(filePath: string, elementType: ElementType = ElementType.PIC, elementSubType: number = 0) {
|
||||||
@@ -91,7 +68,9 @@ export class NTQQFileApi {
|
|||||||
ext
|
ext
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
static async downloadMediaByUuid() {
|
||||||
|
//napCatCore.session.getRichMediaService().downloadFileForFileUuid();
|
||||||
|
}
|
||||||
static async downloadMedia(msgId: string, chatType: ChatType, peerUid: string, elementId: string, thumbPath: string, sourcePath: string, timeout = 1000 * 60 * 2, force: boolean = false) {
|
static async downloadMedia(msgId: string, chatType: ChatType, peerUid: string, elementId: string, thumbPath: string, sourcePath: string, timeout = 1000 * 60 * 2, force: boolean = false) {
|
||||||
//logDebug('receive downloadMedia task', msgId, chatType, peerUid, elementId, thumbPath, sourcePath, timeout, force);
|
//logDebug('receive downloadMedia task', msgId, chatType, peerUid, elementId, thumbPath, sourcePath, timeout, force);
|
||||||
// 用于下载收到的消息中的图片等
|
// 用于下载收到的消息中的图片等
|
||||||
@@ -106,31 +85,33 @@ export class NTQQFileApi {
|
|||||||
return sourcePath;
|
return sourcePath;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//logDebug('start downloadMedia', msgId, chatType, peerUid, elementId, thumbPath, sourcePath, timeout, force);
|
let data = await NTEventDispatch.CallNormalEvent<
|
||||||
return new Promise<string>((resolve, reject) => {
|
(
|
||||||
let completed = false;
|
params: {
|
||||||
const cb = (arg: OnRichMediaDownloadCompleteParams) => {
|
fileModelId: string,
|
||||||
//logDebug('downloadMedia complete', arg, msgId);
|
downloadSourceType: number,
|
||||||
|
triggerType: number,
|
||||||
|
msgId: string,
|
||||||
|
chatType: ChatType,
|
||||||
|
peerUid: string,
|
||||||
|
elementId: string,
|
||||||
|
thumbSize: number,
|
||||||
|
downloadType: number,
|
||||||
|
filePath: string
|
||||||
|
}) => Promise<unknown>,
|
||||||
|
(fileTransNotifyInfo: OnRichMediaDownloadCompleteParams) => void
|
||||||
|
>(
|
||||||
|
'NodeIKernelMsgService/downloadRichMedia',
|
||||||
|
'NodeIKernelMsgListener/onRichMediaDownloadComplete',
|
||||||
|
1,
|
||||||
|
timeout,
|
||||||
|
(arg: OnRichMediaDownloadCompleteParams) => {
|
||||||
if (arg.msgId === msgId) {
|
if (arg.msgId === msgId) {
|
||||||
completed = true;
|
return true;
|
||||||
let filePath = arg.filePath;
|
|
||||||
if (filePath.startsWith('\\')) {
|
|
||||||
// log('filePath start with \\');
|
|
||||||
const downloadPath = sessionConfig.defaultFileDownloadPath;
|
|
||||||
//logDebug('downloadPath', downloadPath);
|
|
||||||
filePath = path.join(downloadPath, filePath);
|
|
||||||
// 下载路径是下载文件夹的相对路径
|
|
||||||
}
|
|
||||||
resolve(filePath);
|
|
||||||
}
|
}
|
||||||
};
|
return false;
|
||||||
downloadMediaTasks.set(randomUUID(), cb);
|
},
|
||||||
setTimeout(() => {
|
{
|
||||||
if (!completed) {
|
|
||||||
reject('下载超时');
|
|
||||||
}
|
|
||||||
}, timeout);
|
|
||||||
napCatCore.session.getMsgService().downloadRichMedia({
|
|
||||||
fileModelId: '0',
|
fileModelId: '0',
|
||||||
downloadSourceType: 0,
|
downloadSourceType: 0,
|
||||||
triggerType: 1,
|
triggerType: 1,
|
||||||
@@ -140,9 +121,18 @@ export class NTQQFileApi {
|
|||||||
elementId: elementId,
|
elementId: elementId,
|
||||||
thumbSize: 0,
|
thumbSize: 0,
|
||||||
downloadType: 1,
|
downloadType: 1,
|
||||||
filePath: thumbPath,
|
filePath: thumbPath
|
||||||
});
|
}
|
||||||
});
|
);
|
||||||
|
let filePath = data[1].filePath;
|
||||||
|
if (filePath.startsWith('\\')) {
|
||||||
|
// log('filePath start with \\');
|
||||||
|
const downloadPath = sessionConfig.defaultFileDownloadPath;
|
||||||
|
//logDebug('downloadPath', downloadPath);
|
||||||
|
filePath = path.join(downloadPath, filePath);
|
||||||
|
// 下载路径是下载文件夹的相对路径
|
||||||
|
}
|
||||||
|
return filePath;
|
||||||
}
|
}
|
||||||
|
|
||||||
static async getImageSize(filePath: string): Promise<ISizeCalculationResult | undefined> {
|
static async getImageSize(filePath: string): Promise<ISizeCalculationResult | undefined> {
|
||||||
@@ -156,27 +146,137 @@ export class NTQQFileApi {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
static async addFileCache(peer: Peer, msgId: string, msgSeq: string, senderUid: string, elemId: string, elemType: string, fileSize: string, fileName: string) {
|
||||||
|
let GroupData;
|
||||||
|
let BuddyData;
|
||||||
|
if (peer.chatType === ChatType.group) {
|
||||||
|
GroupData =
|
||||||
|
[{
|
||||||
|
groupCode: peer.peerUid,
|
||||||
|
isConf: false,
|
||||||
|
hasModifyConfGroupFace: true,
|
||||||
|
hasModifyConfGroupName: true,
|
||||||
|
groupName: "NapCat.Cached",
|
||||||
|
remark: "NapCat.Cached"
|
||||||
|
}];
|
||||||
|
} else if (peer.chatType === ChatType.friend) {
|
||||||
|
BuddyData = [{
|
||||||
|
category_name: 'NapCat.Cached',
|
||||||
|
peerUid: peer.peerUid,
|
||||||
|
peerUin: peer.peerUid,
|
||||||
|
remark: 'NapCat.Cached'
|
||||||
|
}];
|
||||||
|
} else {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
static async getImageUrl(element: { originImageUrl: any; md5HexStr?: any; fileUuid: any; }, isPrivateImage: boolean) {
|
return napCatCore.session.getSearchService().addSearchHistory({
|
||||||
|
type: 4,
|
||||||
|
contactList: [],
|
||||||
|
id: -1,
|
||||||
|
groupInfos: [],
|
||||||
|
msgs: [],
|
||||||
|
fileInfos: [
|
||||||
|
{
|
||||||
|
chatType: peer.chatType,
|
||||||
|
buddyChatInfo: BuddyData || [],
|
||||||
|
discussChatInfo: [],
|
||||||
|
groupChatInfo: GroupData || [],
|
||||||
|
dataLineChatInfo: [],
|
||||||
|
tmpChatInfo: [],
|
||||||
|
msgId: msgId,
|
||||||
|
msgSeq: msgSeq,
|
||||||
|
msgTime: Math.floor(Date.now() / 1000).toString(),
|
||||||
|
senderUid: senderUid,
|
||||||
|
senderNick: 'NapCat.Cached',
|
||||||
|
senderRemark: 'NapCat.Cached',
|
||||||
|
senderCard: 'NapCat.Cached',
|
||||||
|
elemId: elemId,
|
||||||
|
elemType: elemType,
|
||||||
|
fileSize: fileSize,
|
||||||
|
filePath: '',
|
||||||
|
fileName: fileName,
|
||||||
|
hits: [{
|
||||||
|
start: 12,
|
||||||
|
end: 14
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
static async searchfile(keys: string[]) {
|
||||||
|
type EventType = NodeIKernelSearchService['searchFileWithKeywords'];
|
||||||
|
interface OnListener {
|
||||||
|
searchId: string,
|
||||||
|
hasMore: boolean,
|
||||||
|
resultItems: {
|
||||||
|
chatType: ChatType,
|
||||||
|
buddyChatInfo: any[],
|
||||||
|
discussChatInfo: any[],
|
||||||
|
groupChatInfo:
|
||||||
|
{
|
||||||
|
groupCode: string,
|
||||||
|
isConf: boolean,
|
||||||
|
hasModifyConfGroupFace: boolean,
|
||||||
|
hasModifyConfGroupName: boolean,
|
||||||
|
groupName: string,
|
||||||
|
remark: string
|
||||||
|
}[]
|
||||||
|
,
|
||||||
|
dataLineChatInfo: any[],
|
||||||
|
tmpChatInfo: any[],
|
||||||
|
msgId: string,
|
||||||
|
msgSeq: string,
|
||||||
|
msgTime: string,
|
||||||
|
senderUid: string,
|
||||||
|
senderNick: string,
|
||||||
|
senderRemark: string,
|
||||||
|
senderCard: string,
|
||||||
|
elemId: string,
|
||||||
|
elemType: number,
|
||||||
|
fileSize: string,
|
||||||
|
filePath: string,
|
||||||
|
fileName: string,
|
||||||
|
hits:
|
||||||
|
{
|
||||||
|
start: number,
|
||||||
|
end: number
|
||||||
|
}[]
|
||||||
|
}[]
|
||||||
|
};
|
||||||
|
const Event = await NTEventDispatch.CreatEventFunction<EventType>('NodeIKernelSearchService/searchFileWithKeywords');
|
||||||
|
let id = '';
|
||||||
|
const Listener = NTEventDispatch.RegisterListen<(params: OnListener) => void>('NodeIKernelSearchListener/onSearchFileKeywordsResult', 1, 20000, (params) => {
|
||||||
|
if (id !== '' && params.searchId == id) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
id = await Event!(keys, 12);
|
||||||
|
let [ret] = (await Listener);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
static async getImageUrl(element: PicElement) {
|
||||||
if (!element) {
|
if (!element) {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
const url = element.originImageUrl; // 没有域名
|
const url: string = element.originImageUrl!; // 没有域名
|
||||||
const md5HexStr = element.md5HexStr;
|
const md5HexStr = element.md5HexStr;
|
||||||
const fileMd5 = element.md5HexStr;
|
const fileMd5 = element.md5HexStr;
|
||||||
const fileUuid = element.fileUuid;
|
const fileUuid = element.fileUuid;
|
||||||
|
|
||||||
if (url) {
|
if (url) {
|
||||||
if (url.startsWith('/download')) {
|
let UrlParse = new URL(IMAGE_HTTP_HOST + url);//临时解析拼接
|
||||||
if (url.includes('&rkey=')) {
|
let imageAppid = UrlParse.searchParams.get('appid');
|
||||||
|
let isNewPic = imageAppid && ['1406', '1407'].includes(imageAppid);
|
||||||
|
if (isNewPic) {
|
||||||
|
let UrlRkey = UrlParse.searchParams.get('rkey');
|
||||||
|
if (UrlRkey) {
|
||||||
return IMAGE_HTTP_HOST_NT + url;
|
return IMAGE_HTTP_HOST_NT + url;
|
||||||
}
|
}
|
||||||
|
|
||||||
const rkeyData = await rkeyManager.getRkey();
|
const rkeyData = await rkeyManager.getRkey();
|
||||||
|
UrlRkey = imageAppid === '1406' ? rkeyData.private_rkey : rkeyData.group_rkey;
|
||||||
const existsRKey = isPrivateImage ? rkeyData.private_rkey : rkeyData.group_rkey;
|
return IMAGE_HTTP_HOST_NT + url + `${UrlRkey}`;
|
||||||
return IMAGE_HTTP_HOST_NT + url + `${existsRKey}`;
|
|
||||||
} else {
|
} else {
|
||||||
// 老的图片url,不需要rkey
|
// 老的图片url,不需要rkey
|
||||||
return IMAGE_HTTP_HOST + url;
|
return IMAGE_HTTP_HOST + url;
|
||||||
|
@@ -1,11 +1,66 @@
|
|||||||
import { FriendRequest, User } from '@/core/entities';
|
import { FriendRequest, FriendV2, SimpleInfo, User } from '@/core/entities';
|
||||||
import { napCatCore, OnBuddyChangeParams } from '@/core';
|
import { BuddyListReqType, napCatCore, NodeIKernelBuddyListener, NodeIKernelProfileService, OnBuddyChangeParams } from '@/core';
|
||||||
import { NTEventDispatch } from '@/common/utils/EventTask';
|
import { NTEventDispatch } from '@/common/utils/EventTask';
|
||||||
|
import { LimitedHashTable } from '@/common/utils/MessageUnique';
|
||||||
|
import { CacheClassFuncAsyncExtend } from '@/common/utils/helper';
|
||||||
export class NTQQFriendApi {
|
export class NTQQFriendApi {
|
||||||
|
static async getBuddyV2(refresh = false): Promise<FriendV2[]> {
|
||||||
|
let uids: string[] = [];
|
||||||
|
const buddyService = napCatCore.session.getBuddyService();
|
||||||
|
const buddyListV2 = refresh ? await buddyService.getBuddyListV2('0', BuddyListReqType.KNOMAL) : await buddyService.getBuddyListV2('0', BuddyListReqType.KNOMAL);
|
||||||
|
uids.push(...buddyListV2.data.flatMap(item => item.buddyUids));
|
||||||
|
const data = await NTEventDispatch.CallNoListenerEvent<NodeIKernelProfileService['getCoreAndBaseInfo']>(
|
||||||
|
'NodeIKernelProfileService/getCoreAndBaseInfo', 5000, 'nodeStore', uids
|
||||||
|
);
|
||||||
|
return Array.from(data.values());
|
||||||
|
}
|
||||||
|
@CacheClassFuncAsyncExtend(3600 * 1000, 'getBuddyIdMap', () => true)
|
||||||
|
static async getBuddyIdMapCache(refresh = false): Promise<LimitedHashTable<string, string>> {
|
||||||
|
return await NTQQFriendApi.getBuddyIdMap(refresh);
|
||||||
|
}
|
||||||
|
static async getBuddyIdMap(refresh = false): Promise<LimitedHashTable<string, string>> {
|
||||||
|
let uids: string[] = [];
|
||||||
|
let retMap: LimitedHashTable<string, string> = new LimitedHashTable<string, string>(5000);
|
||||||
|
const buddyService = napCatCore.session.getBuddyService();
|
||||||
|
const buddyListV2 = refresh ? await buddyService.getBuddyListV2('0', BuddyListReqType.KNOMAL) : await buddyService.getBuddyListV2('0', BuddyListReqType.KNOMAL);
|
||||||
|
uids.push(...buddyListV2.data.flatMap(item => item.buddyUids));
|
||||||
|
const data = await NTEventDispatch.CallNoListenerEvent<NodeIKernelProfileService['getCoreAndBaseInfo']>(
|
||||||
|
'NodeIKernelProfileService/getCoreAndBaseInfo', 5000, 'nodeStore', uids
|
||||||
|
);
|
||||||
|
data.forEach((value, key) => {
|
||||||
|
retMap.set(value.uin!, value.uid!);
|
||||||
|
});
|
||||||
|
//console.log('getBuddyIdMap', retMap.getValue);
|
||||||
|
return retMap;
|
||||||
|
}
|
||||||
|
static async getBuddyV2ExWithCate(refresh = false) {
|
||||||
|
let uids: string[] = [];
|
||||||
|
let categoryMap: Map<string, any> = new Map();
|
||||||
|
const buddyService = napCatCore.session.getBuddyService();
|
||||||
|
const buddyListV2 = refresh ? (await buddyService.getBuddyListV2('0', BuddyListReqType.KNOMAL)).data : (await buddyService.getBuddyListV2('0', BuddyListReqType.KNOMAL)).data;
|
||||||
|
uids.push(
|
||||||
|
...buddyListV2.flatMap(item => {
|
||||||
|
item.buddyUids.forEach(uid => {
|
||||||
|
categoryMap.set(uid, { categoryId: item.categoryId, categroyName: item.categroyName });
|
||||||
|
});
|
||||||
|
return item.buddyUids
|
||||||
|
}));
|
||||||
|
const data = await NTEventDispatch.CallNoListenerEvent<NodeIKernelProfileService['getCoreAndBaseInfo']>(
|
||||||
|
'NodeIKernelProfileService/getCoreAndBaseInfo', 5000, 'nodeStore', uids
|
||||||
|
);
|
||||||
|
return Array.from(data).map(([key, value]) => {
|
||||||
|
const category = categoryMap.get(key);
|
||||||
|
return category ? { ...value, categoryId: category.categoryId, categroyName: category.categroyName } : value;
|
||||||
|
});
|
||||||
|
}
|
||||||
static async isBuddy(uid: string) {
|
static async isBuddy(uid: string) {
|
||||||
return napCatCore.session.getBuddyService().isBuddy(uid);
|
return napCatCore.session.getBuddyService().isBuddy(uid);
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* @deprecated
|
||||||
|
* @param forced
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
static async getFriends(forced = false): Promise<User[]> {
|
static async getFriends(forced = false): Promise<User[]> {
|
||||||
let [_retData, _BuddyArg] = await NTEventDispatch.CallNormalEvent
|
let [_retData, _BuddyArg] = await NTEventDispatch.CallNormalEvent
|
||||||
<(force: boolean) => Promise<any>, (arg: OnBuddyChangeParams) => void>
|
<(force: boolean) => Promise<any>, (arg: OnBuddyChangeParams) => void>
|
||||||
@@ -14,6 +69,7 @@ export class NTQQFriendApi {
|
|||||||
'NodeIKernelBuddyListener/onBuddyListChange',
|
'NodeIKernelBuddyListener/onBuddyListChange',
|
||||||
1,
|
1,
|
||||||
5000,
|
5000,
|
||||||
|
() => true,
|
||||||
forced
|
forced
|
||||||
);
|
);
|
||||||
const friends: User[] = [];
|
const friends: User[] = [];
|
||||||
|
@@ -1,24 +1,112 @@
|
|||||||
import { GroupMember, GroupRequestOperateTypes, GroupMemberRole, GroupNotify, Group, MemberExtSourceType } from '../entities';
|
import { GroupMember, GroupRequestOperateTypes, GroupMemberRole, GroupNotify, Group, MemberExtSourceType, GroupNotifyTypes, ChatType, Peer, GroupListUpdateType } from '../entities';
|
||||||
import { GeneralCallResult, NTQQUserApi, napCatCore } from '@/core';
|
import { GeneralCallResult, NTQQUserApi, NodeIKernelGroupListener, NodeIKernelGroupService, napCatCore } from '@/core';
|
||||||
import { NTEventDispatch } from '@/common/utils/EventTask';
|
import { NTEventDispatch } from '@/common/utils/EventTask';
|
||||||
import { logDebug } from '@/common/utils/log';
|
import { log } from '@/common/utils/log';
|
||||||
// console.log(process.pid);
|
import { groupMembers } from '../data';
|
||||||
// setTimeout(async () => {
|
import { CacheClassFuncAsyncExtend, runAllWithTimeout } from '@/common/utils/helper';
|
||||||
// console.log(JSON.stringify(await NTQQGroupApi.getMemberExtInfo(), null, 2));
|
|
||||||
// }, 20000);
|
|
||||||
export class NTQQGroupApi {
|
export class NTQQGroupApi {
|
||||||
|
static async setGroupAvatar(gc: string, filePath: string) {
|
||||||
|
return napCatCore.session.getGroupService().setHeader(gc, filePath);
|
||||||
|
}
|
||||||
static async getGroups(forced = false) {
|
static async getGroups(forced = false) {
|
||||||
|
type ListenerType = NodeIKernelGroupListener['onGroupListUpdate'];
|
||||||
let [_retData, _updateType, groupList] = await NTEventDispatch.CallNormalEvent
|
let [_retData, _updateType, groupList] = await NTEventDispatch.CallNormalEvent
|
||||||
<(force: boolean) => Promise<any>, (updateType: number, groupList: Group[]) => void>
|
<(force: boolean) => Promise<any>, ListenerType>
|
||||||
(
|
(
|
||||||
'NodeIKernelGroupService/getGroupList',
|
'NodeIKernelGroupService/getGroupList',
|
||||||
'NodeIKernelGroupListener/onGroupListUpdate',
|
'NodeIKernelGroupListener/onGroupListUpdate',
|
||||||
1,
|
1,
|
||||||
5000,
|
5000,
|
||||||
|
(updateType) => true,
|
||||||
forced
|
forced
|
||||||
);
|
);
|
||||||
return groupList;
|
return groupList;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@CacheClassFuncAsyncExtend(3600 * 1000, "LastestSendTime", () => true)
|
||||||
|
static async getGroupMemberLastestSendTimeCache(GroupCode: string) {
|
||||||
|
return NTQQGroupApi.getGroupMemberLastestSendTime(GroupCode);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 通过QQ自带数据库获取群成员最后发言时间(仅返回有效数据 且消耗延迟大 需要进行缓存)
|
||||||
|
* @param GroupCode 群号
|
||||||
|
* @returns Map<string, string> key: uin value: sendTime
|
||||||
|
* @example
|
||||||
|
* let ret = await NTQQGroupApi.getGroupMemberLastestSendTime('123456');
|
||||||
|
* for (let [uin, sendTime] of ret) {
|
||||||
|
* console.log(uin, sendTime);
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
static async getGroupMemberLastestSendTime(GroupCode: string) {
|
||||||
|
async function getdata(uid: string) {
|
||||||
|
let NTRet = await NTQQGroupApi.getLastestMsgByUids(GroupCode, [uid]);
|
||||||
|
if (NTRet.result != 0 && NTRet.msgList.length < 1) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
return { sendUin: NTRet.msgList[0].senderUin, sendTime: NTRet.msgList[0].msgTime }
|
||||||
|
}
|
||||||
|
let currentGroupMembers = groupMembers.get(GroupCode);
|
||||||
|
let PromiseData: Promise<({
|
||||||
|
sendUin: string;
|
||||||
|
sendTime: string;
|
||||||
|
} | undefined)>[] = [];
|
||||||
|
let ret: Map<string, string> = new Map();
|
||||||
|
if (!currentGroupMembers) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
for (let member of currentGroupMembers.values()) {
|
||||||
|
PromiseData.push(getdata(member.uid).catch(() => undefined));
|
||||||
|
}
|
||||||
|
let allRet = await runAllWithTimeout(PromiseData, 2500);
|
||||||
|
for (let PromiseDo of allRet) {
|
||||||
|
if (PromiseDo) {
|
||||||
|
ret.set(PromiseDo.sendUin, PromiseDo.sendTime);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
static async getLastestMsgByUids(GroupCode: string, uids: string[]) {
|
||||||
|
let ret = await napCatCore.session.getMsgService().queryMsgsWithFilterEx('0', '0', '0', {
|
||||||
|
chatInfo: {
|
||||||
|
peerUid: GroupCode,
|
||||||
|
chatType: ChatType.group,
|
||||||
|
},
|
||||||
|
filterMsgType: [],
|
||||||
|
filterSendersUid: uids,
|
||||||
|
filterMsgToTime: '0',
|
||||||
|
filterMsgFromTime: '0',
|
||||||
|
isReverseOrder: false,
|
||||||
|
isIncludeCurrent: true,
|
||||||
|
pageLimit: 1,
|
||||||
|
});
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
static async getGroupMemberAll(GroupCode: string, forced = false) {
|
||||||
|
return napCatCore.session.getGroupService().getAllMemberList(GroupCode, forced);
|
||||||
|
}
|
||||||
|
static async getLastestMsg(GroupCode: string, uins: string[]) {
|
||||||
|
let uids: Array<string> = [];
|
||||||
|
for (let uin of uins) {
|
||||||
|
let uid = await NTQQUserApi.getUidByUin(uin)
|
||||||
|
if (uid) {
|
||||||
|
uids.push(uid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let ret = await napCatCore.session.getMsgService().queryMsgsWithFilterEx('0', '0', '0', {
|
||||||
|
chatInfo: {
|
||||||
|
peerUid: GroupCode,
|
||||||
|
chatType: ChatType.group,
|
||||||
|
},
|
||||||
|
filterMsgType: [],
|
||||||
|
filterSendersUid: uids,
|
||||||
|
filterMsgToTime: '0',
|
||||||
|
filterMsgFromTime: '0',
|
||||||
|
isReverseOrder: false,
|
||||||
|
isIncludeCurrent: true,
|
||||||
|
pageLimit: 1,
|
||||||
|
});
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
static async getGroupRecommendContactArkJson(GroupCode: string) {
|
static async getGroupRecommendContactArkJson(GroupCode: string) {
|
||||||
return napCatCore.session.getGroupService().getGroupRecommendContactArkJson(GroupCode);
|
return napCatCore.session.getGroupService().getGroupRecommendContactArkJson(GroupCode);
|
||||||
}
|
}
|
||||||
@@ -31,6 +119,30 @@ export class NTQQGroupApi {
|
|||||||
static async DelGroupFileFolder(groupCode: string, folderId: string) {
|
static async DelGroupFileFolder(groupCode: string, folderId: string) {
|
||||||
return napCatCore.session.getRichMediaService().deleteGroupFolder(groupCode, folderId);
|
return napCatCore.session.getRichMediaService().deleteGroupFolder(groupCode, folderId);
|
||||||
}
|
}
|
||||||
|
static async addGroupEssence(GroupCode: string, msgId: string) {
|
||||||
|
// 代码没测过
|
||||||
|
// 需要 ob11msgid->msgId + (peer) -> msgSeq + msgRandom
|
||||||
|
let MsgData = await napCatCore.session.getMsgService().getMsgsIncludeSelf({ chatType: 2, guildId: '', peerUid: GroupCode }, msgId, 1, false);
|
||||||
|
let param = {
|
||||||
|
groupCode: GroupCode,
|
||||||
|
msgRandom: parseInt(MsgData.msgList[0].msgRandom),
|
||||||
|
msgSeq: parseInt(MsgData.msgList[0].msgSeq)
|
||||||
|
};
|
||||||
|
// GetMsgByShoretID(ShoretID); -> MsgService.getMsgs(Peer,MsgId,1,false); -> 组出参数
|
||||||
|
return napCatCore.session.getGroupService().addGroupEssence(param);
|
||||||
|
}
|
||||||
|
static async removeGroupEssence(GroupCode: string, msgId: string) {
|
||||||
|
// 代码没测过
|
||||||
|
// 需要 ob11msgid->msgId + (peer) -> msgSeq + msgRandom
|
||||||
|
let MsgData = await napCatCore.session.getMsgService().getMsgsIncludeSelf({ chatType: 2, guildId: '', peerUid: GroupCode }, msgId, 1, false);
|
||||||
|
let param = {
|
||||||
|
groupCode: GroupCode,
|
||||||
|
msgRandom: parseInt(MsgData.msgList[0].msgRandom),
|
||||||
|
msgSeq: parseInt(MsgData.msgList[0].msgSeq)
|
||||||
|
};
|
||||||
|
// GetMsgByShoretID(ShoretID); -> MsgService.getMsgs(Peer,MsgId,1,false); -> 组出参数
|
||||||
|
return napCatCore.session.getGroupService().removeGroupEssence(param);
|
||||||
|
}
|
||||||
static async getSingleScreenNotifies(num: number) {
|
static async getSingleScreenNotifies(num: number) {
|
||||||
let [_retData, _doubt, _seq, notifies] = await NTEventDispatch.CallNormalEvent
|
let [_retData, _doubt, _seq, notifies] = await NTEventDispatch.CallNormalEvent
|
||||||
<(arg1: boolean, arg2: string, arg3: number) => Promise<any>, (doubt: boolean, seq: string, notifies: GroupNotify[]) => void>
|
<(arg1: boolean, arg2: string, arg3: number) => Promise<any>, (doubt: boolean, seq: string, notifies: GroupNotify[]) => void>
|
||||||
@@ -39,12 +151,32 @@ export class NTQQGroupApi {
|
|||||||
'NodeIKernelGroupListener/onGroupSingleScreenNotifies',
|
'NodeIKernelGroupListener/onGroupSingleScreenNotifies',
|
||||||
1,
|
1,
|
||||||
5000,
|
5000,
|
||||||
|
() => true,
|
||||||
false,
|
false,
|
||||||
'',
|
'',
|
||||||
num
|
num
|
||||||
);
|
);
|
||||||
return notifies;
|
return notifies;
|
||||||
}
|
}
|
||||||
|
static async getGroupMemberV2(GroupCode: string, uid: string, forced = false) {
|
||||||
|
type ListenerType = NodeIKernelGroupListener['onMemberInfoChange'];
|
||||||
|
type EventType = NodeIKernelGroupService['getMemberInfo'];
|
||||||
|
// NTEventDispatch.CreatListenerFunction('NodeIKernelGroupListener/onGroupMemberInfoUpdate',
|
||||||
|
//return napCatCore.session.getGroupService().getMemberInfo(GroupCode, [uid], forced);
|
||||||
|
const [ret, _groupCode, _changeType, _members] = await NTEventDispatch.CallNormalEvent
|
||||||
|
<EventType, ListenerType>
|
||||||
|
(
|
||||||
|
'NodeIKernelGroupService/getMemberInfo',
|
||||||
|
'NodeIKernelGroupListener/onMemberInfoChange',
|
||||||
|
1,
|
||||||
|
5000,
|
||||||
|
(groupCode: string, changeType: number, members: Map<string, GroupMember>) => {
|
||||||
|
return groupCode == GroupCode && members.has(uid);
|
||||||
|
},
|
||||||
|
GroupCode, [uid], forced
|
||||||
|
);
|
||||||
|
return _members.get(uid);
|
||||||
|
}
|
||||||
static async getGroupMembers(groupQQ: string, num = 3000): Promise<Map<string, GroupMember>> {
|
static async getGroupMembers(groupQQ: string, num = 3000): Promise<Map<string, GroupMember>> {
|
||||||
const groupService = napCatCore.session.getGroupService();
|
const groupService = napCatCore.session.getGroupService();
|
||||||
const sceneId = groupService.createMemberListScene(groupQQ, 'groupMemberList_MainWindow');
|
const sceneId = groupService.createMemberListScene(groupQQ, 'groupMemberList_MainWindow');
|
||||||
@@ -52,6 +184,7 @@ export class NTQQGroupApi {
|
|||||||
if (result.errCode !== 0) {
|
if (result.errCode !== 0) {
|
||||||
throw ('获取群成员列表出错,' + result.errMsg);
|
throw ('获取群成员列表出错,' + result.errMsg);
|
||||||
}
|
}
|
||||||
|
|
||||||
//logDebug(`获取群(${groupQQ})成员列表结果:`, `finish: ${result.result.finish}`); //, Array.from(result.result.infos.values()));
|
//logDebug(`获取群(${groupQQ})成员列表结果:`, `finish: ${result.result.finish}`); //, Array.from(result.result.infos.values()));
|
||||||
return result.result.infos;
|
return result.result.infos;
|
||||||
/*
|
/*
|
||||||
@@ -87,16 +220,21 @@ export class NTQQGroupApi {
|
|||||||
const _Pskey = (await NTQQUserApi.getPSkey(['qun.qq.com'])).domainPskeyMap.get('qun.qq.com')!;
|
const _Pskey = (await NTQQUserApi.getPSkey(['qun.qq.com'])).domainPskeyMap.get('qun.qq.com')!;
|
||||||
return napCatCore.session.getGroupService().uploadGroupBulletinPic(GroupCode, _Pskey, imageurl);
|
return napCatCore.session.getGroupService().uploadGroupBulletinPic(GroupCode, _Pskey, imageurl);
|
||||||
}
|
}
|
||||||
static async handleGroupRequest(notify: GroupNotify, operateType: GroupRequestOperateTypes, reason?: string) {
|
static async handleGroupRequest(flag: string, operateType: GroupRequestOperateTypes, reason?: string) {
|
||||||
|
let flagitem = flag.split('|');
|
||||||
|
let groupCode = flagitem[0];
|
||||||
|
let seq = flagitem[1];
|
||||||
|
let type = parseInt(flagitem[2]);
|
||||||
|
|
||||||
return napCatCore.session.getGroupService().operateSysNotify(
|
return napCatCore.session.getGroupService().operateSysNotify(
|
||||||
false,
|
false,
|
||||||
{
|
{
|
||||||
'operateType': operateType, // 2 拒绝
|
'operateType': operateType, // 2 拒绝
|
||||||
'targetMsg': {
|
'targetMsg': {
|
||||||
'seq': notify.seq, // 通知序列号
|
'seq': seq, // 通知序列号
|
||||||
'type': notify.type,
|
'type': type,
|
||||||
'groupCode': notify.group.groupCode,
|
'groupCode': groupCode,
|
||||||
'postscript': reason || ''
|
'postscript': reason || ' ' // 仅传空值可能导致处理失败,故默认给个空格
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@@ -1,83 +1,84 @@
|
|||||||
import { GetFileListParam, Peer, RawMessage, SendMessageElement } from '@/core/entities';
|
import { GetFileListParam, Peer, RawMessage, SendMessageElement, SendMsgElementConstructor } from '@/core/entities';
|
||||||
import { selfInfo } from '@/core/data';
|
import { friends, groups, selfInfo } from '@/core/data';
|
||||||
import { log, logError } from '@/common/utils/log';
|
import { log, logWarn } from '@/common/utils/log';
|
||||||
import { sleep } from '@/common/utils/helper';
|
import { sleep } from '@/common/utils/helper';
|
||||||
import { napCatCore } from '@/core';
|
import { napCatCore, NTQQUserApi } from '@/core';
|
||||||
import { MsgListener, onGroupFileInfoUpdateParamType } from '@/core/listeners';
|
import { onGroupFileInfoUpdateParamType } from '@/core/listeners';
|
||||||
import { GeneralCallResult } from '@/core/services/common';
|
import { GeneralCallResult } from '@/core/services/common';
|
||||||
import { randomUUID } from 'crypto';
|
import { MessageUnique } from '../../../common/utils/MessageUnique';
|
||||||
|
import { NTEventDispatch } from '@/common/utils/EventTask';
|
||||||
|
import { requireMinNTQQBuild } from '@/common/utils/QQBasicInfo';
|
||||||
|
|
||||||
|
async function LoadMessageIdList(Peer: Peer, msgId: string) {
|
||||||
const sendMessagePool: Record<string, ((sendSuccessMsg: RawMessage) => void | Promise<void>) | null> = {};// peerUid: callbackFunc
|
let msgList = await NTQQMsgApi.getMsgHistory(Peer, msgId, 50);
|
||||||
|
for (let j = 0; j < msgList.msgList.length; j++) {
|
||||||
const sendSuccessCBMap: Record<string, ((sendSuccessMsg: RawMessage) => boolean | Promise<boolean>) | null> = {};// uuid: callbackFunc
|
let shortId = MessageUnique.createMsg(Peer, msgList.msgList[j].msgId);
|
||||||
|
|
||||||
const GroupFileInfoUpdateTasks: Map<string, ((groupFileListResult: onGroupFileInfoUpdateParamType) => void)> = new Map();
|
|
||||||
|
|
||||||
const sentMsgTasks: Map<string, (msg: RawMessage) => void> = new Map();
|
|
||||||
|
|
||||||
const msgListener = new MsgListener();
|
|
||||||
|
|
||||||
msgListener.onGroupFileInfoUpdate = (groupFileListResult: onGroupFileInfoUpdateParamType) => {
|
|
||||||
for (const [uuid, cb] of GroupFileInfoUpdateTasks) {
|
|
||||||
cb(groupFileListResult);
|
|
||||||
GroupFileInfoUpdateTasks.delete(uuid);
|
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
async function loadMessageUnique() {
|
||||||
msgListener.onAddSendMsg = (msgRecord: RawMessage) => {
|
if (groups.size > 100) {
|
||||||
// console.log("sent msg", msgRecord, sendMessagePool);
|
logWarn('[性能检测] 群数量大于100,可能会导致性能问题');
|
||||||
for (const [uuid, cb] of sentMsgTasks) {
|
|
||||||
cb(msgRecord);
|
|
||||||
sentMsgTasks.delete(uuid);
|
|
||||||
}
|
}
|
||||||
if (sendMessagePool[msgRecord.peerUid]) {
|
let predict = (groups.size + friends.size / 2) / 5;
|
||||||
const r = sendMessagePool[msgRecord.peerUid]?.(msgRecord);
|
predict = predict < 20 ? 20 : predict;
|
||||||
if (r instanceof Promise) {
|
predict = predict > 50 ? 50 : predict;
|
||||||
r.then().catch(logError);
|
//let waitpromise: Array<Promise<{ msgList: RawMessage[]; }>> = [];
|
||||||
|
predict = Math.floor(predict * 50);
|
||||||
|
MessageUnique.resize(predict);
|
||||||
|
let RecentContact = await NTQQUserApi.getRecentContactListSnapShot(predict);
|
||||||
|
let LoadMessageIdDo: Array<Promise<void>> = new Array<Promise<void>>();
|
||||||
|
if (RecentContact?.info?.changedList && RecentContact?.info?.changedList?.length > 0) {
|
||||||
|
for (let i = 0; i < RecentContact.info.changedList.length; i++) {
|
||||||
|
let Peer: Peer = { chatType: RecentContact.info.changedList[i].chatType, peerUid: RecentContact.info.changedList[i].peerUid, guildId: '' };
|
||||||
|
LoadMessageIdDo.push(LoadMessageIdList(Peer, RecentContact.info.changedList[i].msgId));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
await Promise.all(LoadMessageIdDo).then(() => {
|
||||||
|
log(`[消息序列] 加载 ${predict} 条历史消息记录完成`);
|
||||||
msgListener.onMsgInfoListUpdate = (msgInfoList: RawMessage[]) => {
|
|
||||||
msgInfoList.forEach(msg => {
|
|
||||||
new Promise((resolve, reject) => {
|
|
||||||
for (const cbId in sendSuccessCBMap) {
|
|
||||||
const cb = sendSuccessCBMap[cbId]!;
|
|
||||||
const cbResult = cb(msg);
|
|
||||||
const checkResult = (result: boolean) => {
|
|
||||||
if (result) {
|
|
||||||
delete sendSuccessCBMap[cbId];
|
|
||||||
}
|
|
||||||
};
|
|
||||||
if (cbResult instanceof Promise) {
|
|
||||||
cbResult.then(checkResult);
|
|
||||||
} else {
|
|
||||||
checkResult(cbResult);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}).then().catch(log);
|
|
||||||
});
|
});
|
||||||
};
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
napCatCore.onLoginSuccess(() => {
|
napCatCore.onLoginSuccess(async () => {
|
||||||
napCatCore.addListener(msgListener);
|
await sleep(100);
|
||||||
|
// NTQQMsgApi.CheckSendMode().then().catch();
|
||||||
|
loadMessageUnique().then().catch();
|
||||||
|
//let data = await napCatCore.session.getMsgService().sendSsoCmdReqByContend("LightAppSvc.mini_app_growguard.ReportExecute","1124343");
|
||||||
|
//console.log(data);
|
||||||
});
|
});
|
||||||
}, 100);
|
}, 100);
|
||||||
|
//歇菜LocalMsg压根不写Db
|
||||||
|
// setTimeout(async () => {
|
||||||
|
// let ele: MessageElement = { extBufForUI: '0x', ...SendMsgElementConstructor.text('测试消息') };
|
||||||
|
// let MsgId = await NTQQMsgApi.getMsgUniqueEx();
|
||||||
|
// let peer = { chatType: 2, peerUid: '', guildId: '' };
|
||||||
|
// console.log(await napCatCore.session.getTestPerformanceService().insertMsg(
|
||||||
|
// {
|
||||||
|
// peer: peer,
|
||||||
|
// msgTime: Math.floor(Date.now() / 1000).toString(),
|
||||||
|
// msgId: MsgId,
|
||||||
|
// msgSeq: '56564',
|
||||||
|
// batchNums: 1,
|
||||||
|
// timesPerBatch: 1,
|
||||||
|
// numPerTime: 1
|
||||||
|
// }, [ele]
|
||||||
|
// ));
|
||||||
|
// console.log(await NTQQMsgApi.multiForwardMsg(peer, peer, [MsgId]));
|
||||||
|
// }, 25000)
|
||||||
|
|
||||||
export class NTQQMsgApi {
|
export class NTQQMsgApi {
|
||||||
|
static async getMsgEmojiLikesList(peer: Peer, msgSeq: string, emojiId: string, emojiType: string, count: number = 20) {
|
||||||
|
//console.log(peer, msgSeq, emojiId, emojiType, count);
|
||||||
|
return napCatCore.session.getMsgService().getMsgEmojiLikesList(peer, msgSeq, emojiId, emojiType, "", false, 20)
|
||||||
|
}
|
||||||
// static napCatCore: NapCatCore | null = null;
|
// static napCatCore: NapCatCore | null = null;
|
||||||
// enum BaseEmojiType {
|
// enum BaseEmojiType {
|
||||||
// NORMAL_EMOJI,
|
// NORMAL_EMOJI,
|
||||||
// SUPER_EMOJI,
|
// SUPER_EMOJI,
|
||||||
// RANDOM_SUPER_EMOJI,
|
// RANDOM_SUPER_EMOJI,
|
||||||
// CHAIN_SUPER_EMOJI,
|
// CHAIN_SUPER_EMOJI,
|
||||||
// EMOJI_EMOJI
|
// EMOJI_EMOJI
|
||||||
// }
|
// }
|
||||||
static async setEmojiLike(peer: Peer, msgSeq: string, emojiId: string, set: boolean = true) {
|
static async setEmojiLike(peer: Peer, msgSeq: string, emojiId: string, set: boolean = true) {
|
||||||
// nt_qq//global//nt_data//Emoji//emoji-resource//sysface_res/apng/ 下可以看到所有QQ表情预览
|
// nt_qq//global//nt_data//Emoji//emoji-resource//sysface_res/apng/ 下可以看到所有QQ表情预览
|
||||||
// nt_qq\global\nt_data\Emoji\emoji-resource\face_config.json 里面有所有表情的id, 自带表情id是QSid, 标准emoji表情id是QCid
|
// nt_qq\global\nt_data\Emoji\emoji-resource\face_config.json 里面有所有表情的id, 自带表情id是QSid, 标准emoji表情id是QCid
|
||||||
@@ -90,142 +91,208 @@ export class NTQQMsgApi {
|
|||||||
} | undefined> {
|
} | undefined> {
|
||||||
return napCatCore.session.getMsgService().getMultiMsg(peer, rootMsgId, parentMsgId);
|
return napCatCore.session.getMsgService().getMultiMsg(peer, rootMsgId, parentMsgId);
|
||||||
}
|
}
|
||||||
|
static async getLastestMsgByUids(peer: Peer, count: number = 20) {
|
||||||
|
let ret = await napCatCore.session.getMsgService().queryMsgsWithFilterEx('0', '0', '0', {
|
||||||
|
chatInfo: peer,
|
||||||
|
filterMsgType: [],
|
||||||
|
filterSendersUid: [],
|
||||||
|
filterMsgToTime: '0',
|
||||||
|
filterMsgFromTime: '0',
|
||||||
|
isReverseOrder: false,
|
||||||
|
isIncludeCurrent: true,
|
||||||
|
pageLimit: 1,
|
||||||
|
});
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
static async getMsgsByMsgId(peer: Peer, msgIds: string[]) {
|
static async getMsgsByMsgId(peer: Peer, msgIds: string[]) {
|
||||||
return await napCatCore.session.getMsgService().getMsgsByMsgId(peer, msgIds);
|
return await napCatCore.session.getMsgService().getMsgsByMsgId(peer, msgIds);
|
||||||
}
|
}
|
||||||
|
static async getSingleMsg(peer: Peer, seq: string) {
|
||||||
|
return await napCatCore.session.getMsgService().getSingleMsg(peer, seq);
|
||||||
|
}
|
||||||
|
static async fetchFavEmojiList(num: number) {
|
||||||
|
return napCatCore.session.getMsgService().fetchFavEmojiList("", num, true, true)
|
||||||
|
}
|
||||||
|
static async queryMsgsWithFilterExWithSeq(peer: Peer, msgSeq: string) {
|
||||||
|
let ret = await napCatCore.session.getMsgService().queryMsgsWithFilterEx('0', '0', msgSeq, {
|
||||||
|
chatInfo: peer,
|
||||||
|
filterMsgType: [],
|
||||||
|
filterSendersUid: [],
|
||||||
|
filterMsgToTime: '0',
|
||||||
|
filterMsgFromTime: '0',
|
||||||
|
isReverseOrder: false,
|
||||||
|
isIncludeCurrent: true,
|
||||||
|
pageLimit: 1,
|
||||||
|
});
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
static async getMsgsBySeqAndCount(peer: Peer, seq: string, count: number, desc: boolean, z: boolean) {
|
static async getMsgsBySeqAndCount(peer: Peer, seq: string, count: number, desc: boolean, z: boolean) {
|
||||||
return await napCatCore.session.getMsgService().getMsgsBySeqAndCount(peer, seq, count, desc, z);
|
return await napCatCore.session.getMsgService().getMsgsBySeqAndCount(peer, seq, count, desc, z);
|
||||||
}
|
}
|
||||||
|
|
||||||
static async activateChat(peer: Peer) {
|
|
||||||
// await this.fetchRecentContact();
|
|
||||||
// await sleep(500);
|
|
||||||
}
|
|
||||||
|
|
||||||
static async activateChatAndGetHistory(peer: Peer) {
|
|
||||||
|
|
||||||
}
|
|
||||||
static async setMsgRead(peer: Peer) {
|
static async setMsgRead(peer: Peer) {
|
||||||
return napCatCore.session.getMsgService().setMsgRead(peer);
|
return napCatCore.session.getMsgService().setMsgRead(peer);
|
||||||
}
|
}
|
||||||
static async getGroupFileList(GroupCode: string, params: GetFileListParam) {
|
static async getGroupFileList(GroupCode: string, params: GetFileListParam) {
|
||||||
return new Promise<Array<any>>(async (resolve, reject) => {
|
let data = await NTEventDispatch.CallNormalEvent<
|
||||||
let complete = false;
|
(GroupCode: string, params: GetFileListParam) => Promise<unknown>,
|
||||||
setTimeout(() => {
|
(groupFileListResult: onGroupFileInfoUpdateParamType) => void
|
||||||
if (!complete) {
|
>(
|
||||||
reject('获取群文件列表超时');
|
'NodeIKernelRichMediaService/getGroupFileList',
|
||||||
}
|
'NodeIKernelMsgListener/onGroupFileInfoUpdate',
|
||||||
}, 5000);
|
1,
|
||||||
const GroupFileInfoUpdateCB = (groupFileListResult: onGroupFileInfoUpdateParamType) => {
|
5000,
|
||||||
complete = true;
|
(groupFileListResult: onGroupFileInfoUpdateParamType) => {
|
||||||
resolve(groupFileListResult.item);
|
return true;
|
||||||
};
|
},
|
||||||
GroupFileInfoUpdateTasks.set(randomUUID(), GroupFileInfoUpdateCB);
|
GroupCode,
|
||||||
await napCatCore.session.getRichMediaService().getGroupFileList(GroupCode, params);
|
params
|
||||||
});
|
);
|
||||||
|
return data[1].item;
|
||||||
}
|
}
|
||||||
static async getMsgHistory(peer: Peer, msgId: string, count: number) {
|
static async getMsgHistory(peer: Peer, msgId: string, count: number) {
|
||||||
// 消息时间从旧到新
|
// 消息时间从旧到新
|
||||||
return napCatCore.session.getMsgService().getMsgsIncludeSelf(peer, msgId, count, true);
|
return napCatCore.session.getMsgService().getMsgsIncludeSelf(peer, msgId, count, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
static async fetchRecentContact() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
static async recallMsg(peer: Peer, msgIds: string[]) {
|
static async recallMsg(peer: Peer, msgIds: string[]) {
|
||||||
await napCatCore.session.getMsgService().recallMsg({
|
await napCatCore.session.getMsgService().recallMsg({
|
||||||
chatType: peer.chatType,
|
chatType: peer.chatType,
|
||||||
peerUid: peer.peerUid
|
peerUid: peer.peerUid
|
||||||
}, msgIds);
|
}, msgIds);
|
||||||
}
|
}
|
||||||
|
static async sendMsgV2(peer: Peer, msgElements: SendMessageElement[], waitComplete = true, timeout = 10000) {
|
||||||
static async sendMsg(peer: Peer, msgElements: SendMessageElement[], waitComplete = true, timeout = 10000): Promise<RawMessage> {
|
// function generateMsgId() {
|
||||||
const peerUid = peer.peerUid;
|
// const timestamp = Math.floor(Date.now() / 1000);
|
||||||
// 等待上一个相同的peer发送完
|
// const random = Math.floor(Math.random() * Math.pow(2, 32));
|
||||||
let checkLastSendUsingTime = 0;
|
// const buffer = Buffer.alloc(8);
|
||||||
const waitLastSend: () => Promise<void> = async () => {
|
// buffer.writeUInt32BE(timestamp, 0);
|
||||||
if (checkLastSendUsingTime > timeout) {
|
// buffer.writeUInt32BE(random, 4);
|
||||||
throw ('发送超时');
|
// const msgId = BigInt("0x" + buffer.toString('hex')).toString();
|
||||||
}
|
// return msgId;
|
||||||
const lastSending = sendMessagePool[peer.peerUid];
|
// }
|
||||||
if (lastSending) {
|
// 此处有采用Hack方法 利用数据返回正确得到对应消息
|
||||||
// log("有正在发送的消息,等待中...")
|
// 与之前 Peer队列 MsgSeq队列 真正的MsgId并发不同
|
||||||
await sleep(500);
|
// 谨慎采用 目前测试暂无问题 Developer.Mlikiowa
|
||||||
checkLastSendUsingTime += 500;
|
let msgId = await NTQQMsgApi.getMsgUnique(peer.chatType, await NTQQMsgApi.getServerTime());
|
||||||
return await waitLastSend();
|
let data = await NTEventDispatch.CallNormalEvent<
|
||||||
} else {
|
(msgId: string, peer: Peer, msgElements: SendMessageElement[], map: Map<any, any>) => Promise<unknown>,
|
||||||
return;
|
(msgList: RawMessage[]) => void
|
||||||
}
|
>(
|
||||||
};
|
'NodeIKernelMsgService/sendMsg',
|
||||||
await waitLastSend();
|
'NodeIKernelMsgListener/onMsgInfoListUpdate',
|
||||||
|
1,
|
||||||
return new Promise((resolve, reject) => {
|
timeout,
|
||||||
let completed = false;
|
(msgRecords: RawMessage[]) => {
|
||||||
let sentMessage: RawMessage | null = null;
|
for (let msgRecord of msgRecords) {
|
||||||
const sendSuccessCBId = randomUUID() as string;
|
if (msgRecord.msgId === msgId && msgRecord.sendStatus === 2) {
|
||||||
sendSuccessCBMap[sendSuccessCBId] = (msgRecord: RawMessage) => {
|
|
||||||
if (msgRecord.msgId === sentMessage?.msgId) {
|
|
||||||
if (msgRecord.sendStatus === 2) {
|
|
||||||
delete sendSuccessCBMap[sendSuccessCBId];
|
|
||||||
completed = true;
|
|
||||||
resolve(msgRecord);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
};
|
},
|
||||||
sendMessagePool[peerUid] = async (rawMessage: RawMessage) => {
|
msgId,
|
||||||
// console.log('收到sent 消息', rawMessage.msgId);
|
peer,
|
||||||
delete sendMessagePool[peerUid];
|
msgElements,
|
||||||
sentMessage = rawMessage;
|
new Map()
|
||||||
};
|
);
|
||||||
setTimeout(() => {
|
let retMsg = data[1].find(msgRecord => {
|
||||||
if (completed) return;
|
if (msgRecord.msgId === msgId) {
|
||||||
delete sendMessagePool[peerUid];
|
return true;
|
||||||
delete sendSuccessCBMap[sendSuccessCBId];
|
}
|
||||||
reject('发送超时');
|
|
||||||
}, timeout);
|
|
||||||
const result = napCatCore.session.getMsgService().sendMsg('0', peer, msgElements, new Map());
|
|
||||||
});
|
});
|
||||||
|
return retMsg;
|
||||||
|
}
|
||||||
|
static sendMsgEx(peer: Peer, msgElements: SendMessageElement[], waitComplete = true, timeout = 10000) {
|
||||||
|
//return NTQQMsgApi.sendMsgV1(peer, msgElements, waitComplete, timeout);
|
||||||
|
}
|
||||||
|
static async sendMsg(peer: Peer, msgElements: SendMessageElement[], waitComplete = true, timeout = 10000) {
|
||||||
|
//唉? !我有个想法
|
||||||
|
let msgId = await NTQQMsgApi.getMsgUnique(peer.chatType, await NTQQMsgApi.getServerTime());
|
||||||
|
peer.guildId = msgId;
|
||||||
|
let data = await NTEventDispatch.CallNormalEvent<
|
||||||
|
(msgId: string, peer: Peer, msgElements: SendMessageElement[], map: Map<any, any>) => Promise<unknown>,
|
||||||
|
(msgList: RawMessage[]) => void
|
||||||
|
>(
|
||||||
|
'NodeIKernelMsgService/sendMsg',
|
||||||
|
'NodeIKernelMsgListener/onMsgInfoListUpdate',
|
||||||
|
1,
|
||||||
|
timeout,
|
||||||
|
(msgRecords: RawMessage[]) => {
|
||||||
|
for (let msgRecord of msgRecords) {
|
||||||
|
if (msgRecord.guildId === msgId && msgRecord.sendStatus === 2) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
"0",
|
||||||
|
peer,
|
||||||
|
msgElements,
|
||||||
|
new Map()
|
||||||
|
);
|
||||||
|
let retMsg = data[1].find(msgRecord => {
|
||||||
|
if (msgRecord.guildId === msgId) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return retMsg;
|
||||||
|
}
|
||||||
|
static async getMsgUnique(chatType: number, time: string) {
|
||||||
|
if (requireMinNTQQBuild('26702')) {
|
||||||
|
return napCatCore.session.getMsgService().generateMsgUniqueId(chatType, time);
|
||||||
|
}
|
||||||
|
return napCatCore.session.getMsgService().getMsgUniqueId(time);
|
||||||
|
}
|
||||||
|
static async getServerTime() {
|
||||||
|
return napCatCore.session.getMSFService().getServerTime();
|
||||||
|
}
|
||||||
|
static async getServerTimeV2() {
|
||||||
|
return NTEventDispatch.CallNoListenerEvent<() => string>('NodeIKernelMsgService/getServerTime', 5000);
|
||||||
}
|
}
|
||||||
|
|
||||||
static async forwardMsg(srcPeer: Peer, destPeer: Peer, msgIds: string[]) {
|
static async forwardMsg(srcPeer: Peer, destPeer: Peer, msgIds: string[]) {
|
||||||
return napCatCore.session.getMsgService().forwardMsg(msgIds, srcPeer, [destPeer], new Map());
|
return napCatCore.session.getMsgService().forwardMsg(msgIds, srcPeer, [destPeer], new Map());
|
||||||
}
|
}
|
||||||
|
|
||||||
static async multiForwardMsg(srcPeer: Peer, destPeer: Peer, msgIds: string[]): Promise<RawMessage> {
|
static async multiForwardMsg(srcPeer: Peer, destPeer: Peer, msgIds: string[]): Promise<RawMessage> {
|
||||||
const msgInfos = msgIds.map(id => {
|
const msgInfos = msgIds.map(id => {
|
||||||
return { msgId: id, senderShowName: selfInfo.nick };
|
return { msgId: id, senderShowName: selfInfo.nick };
|
||||||
});
|
});
|
||||||
|
let data = await NTEventDispatch.CallNormalEvent<
|
||||||
return new Promise((resolve, reject) => {
|
(msgInfo: typeof msgInfos, srcPeer: Peer, destPeer: Peer, comment: Array<any>, attr: Map<any, any>,) => Promise<unknown>,
|
||||||
let complete = false;
|
(msgList: RawMessage[]) => void
|
||||||
const onSentCB = (msg: RawMessage) => {
|
>(
|
||||||
const arkElement = msg.elements.find(ele => ele.arkElement);
|
'NodeIKernelMsgService/multiForwardMsgWithComment',
|
||||||
if (!arkElement) {
|
'NodeIKernelMsgListener/onMsgInfoListUpdate',
|
||||||
// log("收到的不是转发消息")
|
1,
|
||||||
return;
|
5000,
|
||||||
|
(msgRecords: RawMessage[]) => {
|
||||||
|
for (let msgRecord of msgRecords) {
|
||||||
|
if (msgRecord.peerUid == destPeer.peerUid && msgRecord.senderUid == selfInfo.uid) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
const forwardData: any = JSON.parse(arkElement.arkElement.bytesData);
|
return false;
|
||||||
if (forwardData.app != 'com.tencent.multimsg') {
|
},
|
||||||
return;
|
msgInfos,
|
||||||
}
|
srcPeer,
|
||||||
if (msg.peerUid == destPeer.peerUid && msg.senderUid == selfInfo.uid) {
|
destPeer,
|
||||||
complete = true;
|
[],
|
||||||
resolve(msg);
|
new Map()
|
||||||
}
|
|
||||||
};
|
|
||||||
sentMsgTasks.set(randomUUID(), onSentCB);
|
|
||||||
setTimeout(() => {
|
|
||||||
if (!complete) {
|
|
||||||
reject('转发消息超时');
|
|
||||||
}
|
|
||||||
}, 5000);
|
|
||||||
napCatCore.session.getMsgService().multiForwardMsgWithComment(msgInfos, srcPeer, destPeer, [], new Map());
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
|
for (let msg of data[1]) {
|
||||||
|
const arkElement = msg.elements.find(ele => ele.arkElement);
|
||||||
|
if (!arkElement) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const forwardData: any = JSON.parse(arkElement.arkElement.bytesData);
|
||||||
|
if (forwardData.app != 'com.tencent.multimsg') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (msg.peerUid == destPeer.peerUid && msg.senderUid == selfInfo.uid) {
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new Error('转发消息超时');
|
||||||
|
}
|
||||||
|
static async markallMsgAsRead() {
|
||||||
|
return napCatCore.session.getMsgService().setAllC2CAndGroupMsgRead();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -19,6 +19,43 @@ export interface CustomMusicSignPostData {
|
|||||||
image?: string,
|
image?: string,
|
||||||
singer?: string
|
singer?: string
|
||||||
}
|
}
|
||||||
|
// let t = await napCatCore.session.getGroupService().shareDigest({
|
||||||
|
// appId: "100497308",
|
||||||
|
// appType: 1,
|
||||||
|
// msgStyle: 0,
|
||||||
|
// recvUin: "726067488",
|
||||||
|
// sendType: 1,
|
||||||
|
// clientInfo: {
|
||||||
|
// platform: 1
|
||||||
|
// },
|
||||||
|
// richMsg: {
|
||||||
|
// usingArk: true,
|
||||||
|
// title: "Bot测试title",
|
||||||
|
// summary: "Bot测试summary",
|
||||||
|
// url: "https://www.bilibili.com",
|
||||||
|
// pictureUrl: "https://y.qq.com/music/photo_new/T002R300x300M0000035DC6W4ZpSqf_1.jpg?max_age=2592000",
|
||||||
|
// brief: "Bot测试brief",
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
// {
|
||||||
|
// errCode: 0,
|
||||||
|
// errMsg: '',
|
||||||
|
// rsp: {
|
||||||
|
// sendType: 1,
|
||||||
|
// recvUin: '726067488',
|
||||||
|
// recvOpenId: '',
|
||||||
|
// errCode: 901501,
|
||||||
|
// errMsg: 'imagent service_error:150_OIDB_NO_PRIV',
|
||||||
|
// extInfo: {
|
||||||
|
// wording: '消息下发失败(错误码:901501)',
|
||||||
|
// jumpResult: 0,
|
||||||
|
// jumpUrl: '',
|
||||||
|
// level: 0,
|
||||||
|
// subLevel: 0,
|
||||||
|
// developMsg: 'imagent error'
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
// export class MusicSign {
|
// export class MusicSign {
|
||||||
// private readonly url: string;
|
// private readonly url: string;
|
||||||
|
@@ -1,11 +1,7 @@
|
|||||||
|
|
||||||
|
|
||||||
import { NTEventDispatch } from '@/common/utils/EventTask';
|
import { NTEventDispatch } from '@/common/utils/EventTask';
|
||||||
import { GeneralCallResult, NTQQUserApi, napCatCore } from '@/core';
|
import { GeneralCallResult, NTQQFileApi, NTQQUserApi, napCatCore } from '@/core';
|
||||||
// setTimeout(async () => {
|
|
||||||
// let ret = await NTQQSystemApi.getArkJsonCollection('1-2-162b9b42-65b9-4405-a8ed-2e256ec8aa50');
|
|
||||||
// console.log(ret);
|
|
||||||
// }, 20000)
|
|
||||||
export class NTQQSystemApi {
|
export class NTQQSystemApi {
|
||||||
static async hasOtherRunningQQProcess() {
|
static async hasOtherRunningQQProcess() {
|
||||||
return napCatCore.util.hasOtherRunningQQProcess();
|
return napCatCore.util.hasOtherRunningQQProcess();
|
||||||
@@ -16,7 +12,7 @@ export class NTQQSystemApi {
|
|||||||
static async translateEnWordToZn(words: string[]) {
|
static async translateEnWordToZn(words: string[]) {
|
||||||
return napCatCore.session.getRichMediaService().translateEnWordToZn(words);
|
return napCatCore.session.getRichMediaService().translateEnWordToZn(words);
|
||||||
}
|
}
|
||||||
//调用会超时 没灯用
|
//调用会超时 没灯用 (好像是通知listener的) onLineDev
|
||||||
static async getOnlineDev() {
|
static async getOnlineDev() {
|
||||||
return napCatCore.session.getMsgService().getOnLineDev();
|
return napCatCore.session.getMsgService().getOnLineDev();
|
||||||
}
|
}
|
||||||
@@ -33,7 +29,7 @@ export class NTQQSystemApi {
|
|||||||
static async BootMiniApp(appfile: string, params: string) {
|
static async BootMiniApp(appfile: string, params: string) {
|
||||||
await napCatCore.session.getNodeMiscService().setMiniAppVersion('2.16.4');
|
await napCatCore.session.getNodeMiscService().setMiniAppVersion('2.16.4');
|
||||||
let c = await napCatCore.session.getNodeMiscService().getMiniAppPath();
|
let c = await napCatCore.session.getNodeMiscService().getMiniAppPath();
|
||||||
console.log(c);
|
|
||||||
return napCatCore.session.getNodeMiscService().startNewMiniApp(appfile, params);
|
return napCatCore.session.getNodeMiscService().startNewMiniApp(appfile, params);
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -1,40 +1,29 @@
|
|||||||
import { ModifyProfileParams, SelfInfo, User, UserDetailInfoByUin } from '@/core/entities';
|
import { ModifyProfileParams, SelfInfo, User, UserDetailInfoByUin, UserDetailInfoByUinV2 } from '@/core/entities';
|
||||||
import { friends, selfInfo } from '@/core/data';
|
import { friends, groupMembers, selfInfo } from '@/core/data';
|
||||||
import { CacheClassFuncAsync } from '@/common/utils/helper';
|
import { CacheClassFuncAsync, CacheClassFuncAsyncExtend } from '@/common/utils/helper';
|
||||||
import { GeneralCallResult, napCatCore, NTQQFriendApi } from '@/core';
|
import { napCatCore, NTQQFriendApi } from '@/core';
|
||||||
import { ProfileListener } from '@/core/listeners';
|
import { NodeIKernelProfileListener, ProfileListener } from '@/core/listeners';
|
||||||
import { rejects } from 'assert';
|
|
||||||
import { randomUUID } from 'crypto';
|
|
||||||
import { RequestUtil } from '@/common/utils/request';
|
import { RequestUtil } from '@/common/utils/request';
|
||||||
import { logDebug, logError } from '@/common/utils/log';
|
import { logWarn } from '@/common/utils/log';
|
||||||
import { NTEventDispatch } from '@/common/utils/EventTask';
|
import { NTEventDispatch } from '@/common/utils/EventTask';
|
||||||
const userInfoCache: Record<string, User> = {}; // uid: User
|
import { NodeIKernelProfileService, ProfileBizType, UserDetailSource } from '@/core/services';
|
||||||
|
import { requireMinNTQQBuild } from '@/common/utils/QQBasicInfo';
|
||||||
|
|
||||||
const profileListener = new ProfileListener();
|
|
||||||
|
|
||||||
const userDetailHandlers: Map<string, ((profile: User) => void)> = new Map();
|
|
||||||
profileListener.onProfileDetailInfoChanged = (profile) => {
|
|
||||||
userInfoCache[profile.uid] = profile;
|
|
||||||
userDetailHandlers.forEach(handler => handler(profile));
|
|
||||||
};
|
|
||||||
setTimeout(() => {
|
|
||||||
napCatCore.onLoginSuccess(() => {
|
|
||||||
napCatCore.addListener(profileListener);
|
|
||||||
});
|
|
||||||
}, 100);
|
|
||||||
// 老版本逻辑现已移除
|
|
||||||
// console.log('onProfileDetailInfoChanged', profile);
|
|
||||||
// recevCount++;
|
|
||||||
// firstProfile = profile;
|
|
||||||
// if (recevCount === 2) {
|
|
||||||
// profileService.removeKernelProfileListener(listenerId);
|
|
||||||
// // if (!completed) {
|
|
||||||
// completed = true;
|
|
||||||
// resolve(profile);
|
|
||||||
// // }
|
|
||||||
// }
|
|
||||||
// };
|
|
||||||
export class NTQQUserApi {
|
export class NTQQUserApi {
|
||||||
|
static async getProfileLike(uid: string) {
|
||||||
|
return napCatCore.session.getProfileLikeService().getBuddyProfileLike({
|
||||||
|
friendUids: [
|
||||||
|
uid
|
||||||
|
],
|
||||||
|
basic: 1,
|
||||||
|
vote: 1,
|
||||||
|
favorite: 0,
|
||||||
|
userProfile: 1,
|
||||||
|
type: 2,
|
||||||
|
start: 0,
|
||||||
|
limit: 20
|
||||||
|
});
|
||||||
|
}
|
||||||
static async setLongNick(longNick: string) {
|
static async setLongNick(longNick: string) {
|
||||||
return napCatCore.session.getProfileService().setLongNick(longNick);
|
return napCatCore.session.getProfileService().setLongNick(longNick);
|
||||||
}
|
}
|
||||||
@@ -58,60 +47,110 @@ export class NTQQUserApi {
|
|||||||
const ret = await napCatCore.session.getProfileService().setHeader(filePath) as setQQAvatarRet;
|
const ret = await napCatCore.session.getProfileService().setHeader(filePath) as setQQAvatarRet;
|
||||||
return { result: ret?.result, errMsg: ret?.errMsg };
|
return { result: ret?.result, errMsg: ret?.errMsg };
|
||||||
}
|
}
|
||||||
|
static async setGroupAvatar(gc: string, filePath: string) {
|
||||||
static async getSelfInfo() {
|
return napCatCore.session.getGroupService().setHeader(gc, filePath);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static async getUserInfo(uid: string) {
|
static async fetchUserDetailInfos(uids: string[]) {
|
||||||
|
//26702 以上使用新接口 .Dev Mlikiowa
|
||||||
|
type EventService = NodeIKernelProfileService['fetchUserDetailInfo'];
|
||||||
|
type EventListener = NodeIKernelProfileListener['onUserDetailInfoChanged'];
|
||||||
|
let retData: User[] = [];
|
||||||
|
let [_retData, _retListener] = await NTEventDispatch.CallNormalEvent
|
||||||
|
<EventService, EventListener>
|
||||||
|
(
|
||||||
|
'NodeIKernelProfileService/fetchUserDetailInfo',
|
||||||
|
'NodeIKernelProfileListener/onUserDetailInfoChanged',
|
||||||
|
uids.length,
|
||||||
|
5000,
|
||||||
|
(profile) => {
|
||||||
|
if (uids.includes(profile.uid)) {
|
||||||
|
let RetUser: User = {
|
||||||
|
...profile.simpleInfo.coreInfo,
|
||||||
|
...profile.simpleInfo.status,
|
||||||
|
...profile.simpleInfo.vasInfo,
|
||||||
|
...profile.commonExt,
|
||||||
|
...profile.simpleInfo.baseInfo,
|
||||||
|
qqLevel: profile.commonExt.qqLevel,
|
||||||
|
pendantId: ""
|
||||||
|
};
|
||||||
|
retData.push(RetUser);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
"BuddyProfileStore",
|
||||||
|
uids,
|
||||||
|
UserDetailSource.KSERVER,
|
||||||
|
[
|
||||||
|
ProfileBizType.KALL
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
return retData;
|
||||||
}
|
}
|
||||||
// enum ProfileBizType {
|
static async fetchUserDetailInfo(uid: string) {
|
||||||
// KALL,
|
type EventService = NodeIKernelProfileService['fetchUserDetailInfo'];
|
||||||
// KBASEEXTEND,
|
type EventListener = NodeIKernelProfileListener['onUserDetailInfoChanged'];
|
||||||
// KVAS,
|
let [_retData, profile] = await NTEventDispatch.CallNormalEvent
|
||||||
// KQZONE,
|
<EventService, EventListener>
|
||||||
// KOTHER
|
(
|
||||||
// }
|
'NodeIKernelProfileService/fetchUserDetailInfo',
|
||||||
static async getUserDetailInfo(uid: string): Promise<User> {
|
'NodeIKernelProfileListener/onUserDetailInfoChanged',
|
||||||
// const existUser = userInfoCache[uid];
|
1,
|
||||||
// if (existUser) {
|
5000,
|
||||||
// return existUser;
|
(profile) => {
|
||||||
// }
|
if (profile.uid === uid) {
|
||||||
const profileService = napCatCore.session.getProfileService();
|
return true;
|
||||||
// console.log('getUserDetailInfo', result);
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
const uuid = randomUUID();
|
|
||||||
let completed = false;
|
|
||||||
let retData: User | undefined = undefined;
|
|
||||||
let isFirst = true;
|
|
||||||
// 不管返回几次 超时有数据就该返回 兼容就好了
|
|
||||||
setTimeout(() => {
|
|
||||||
if (!completed) {
|
|
||||||
if (retData) {
|
|
||||||
resolve(retData);
|
|
||||||
} else {
|
|
||||||
reject('getUserDetailInfo timeout');
|
|
||||||
}
|
}
|
||||||
}
|
return false;
|
||||||
userDetailHandlers.delete(uuid);
|
},
|
||||||
}, 5000);
|
"BuddyProfileStore",
|
||||||
userDetailHandlers.set(uuid, (profile) => {
|
[
|
||||||
if (profile.uid === uid) {
|
uid
|
||||||
if (isFirst) {
|
],
|
||||||
retData = profile;
|
UserDetailSource.KSERVER,
|
||||||
isFirst = false;
|
[
|
||||||
// console.log('getUserDetailInfo', profile);
|
ProfileBizType.KALL
|
||||||
} else {
|
]
|
||||||
completed = true;
|
);
|
||||||
resolve(profile);
|
let RetUser: User = {
|
||||||
|
...profile.simpleInfo.coreInfo,
|
||||||
|
...profile.simpleInfo.status,
|
||||||
|
...profile.simpleInfo.vasInfo,
|
||||||
|
...profile.commonExt,
|
||||||
|
...profile.simpleInfo.baseInfo,
|
||||||
|
qqLevel: profile.commonExt.qqLevel,
|
||||||
|
pendantId: ""
|
||||||
|
};
|
||||||
|
return RetUser;
|
||||||
|
}
|
||||||
|
static async getUserDetailInfo(uid: string) {
|
||||||
|
if (requireMinNTQQBuild('26702')) {
|
||||||
|
return this.fetchUserDetailInfo(uid);
|
||||||
|
}
|
||||||
|
return this.getUserDetailInfoOld(uid);
|
||||||
|
}
|
||||||
|
static async getUserDetailInfoOld(uid: string) {
|
||||||
|
type EventService = NodeIKernelProfileService['getUserDetailInfoWithBizInfo'];
|
||||||
|
type EventListener = NodeIKernelProfileListener['onProfileDetailInfoChanged'];
|
||||||
|
let [_retData, profile] = await NTEventDispatch.CallNormalEvent
|
||||||
|
<EventService, EventListener>
|
||||||
|
(
|
||||||
|
'NodeIKernelProfileService/getUserDetailInfoWithBizInfo',
|
||||||
|
'NodeIKernelProfileListener/onProfileDetailInfoChanged',
|
||||||
|
2,
|
||||||
|
5000,
|
||||||
|
(profile: User) => {
|
||||||
|
if (profile.uid === uid) {
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
return false;
|
||||||
});
|
},
|
||||||
profileService.getUserDetailInfoWithBizInfo(uid, [0]).then(result => {
|
uid,
|
||||||
// console.log('getUserDetailInfo', result);
|
[0]
|
||||||
});
|
);
|
||||||
});
|
return profile;
|
||||||
}
|
}
|
||||||
static async modifySelfProfile(param: ModifyProfileParams) {
|
static async modifySelfProfile(param: ModifyProfileParams) {
|
||||||
return napCatCore.session.getProfileService().modifyDesktopMiniProfile(param);
|
return napCatCore.session.getProfileService().modifyDesktopMiniProfile(param);
|
||||||
@@ -163,29 +202,89 @@ export class NTQQUserApi {
|
|||||||
}
|
}
|
||||||
return skey;
|
return skey;
|
||||||
}
|
}
|
||||||
|
@CacheClassFuncAsyncExtend(3600 * 1000, 'Uin2Uid', (Uin: string, Uid: string | undefined) => {
|
||||||
|
if (Uid && Uid.indexOf('u_') != -1) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
logWarn("uin转换到uid时异常", Uin, Uid);
|
||||||
|
return false;
|
||||||
|
})
|
||||||
static async getUidByUin(Uin: string) {
|
static async getUidByUin(Uin: string) {
|
||||||
let ret = await NTEventDispatch.CallNoListenerEvent
|
//此代码仅临时使用,后期会被废弃
|
||||||
<(Uin: string[]) => Promise<{ uidInfo: Map<string, string> }>>(
|
if (requireMinNTQQBuild('26702')) {
|
||||||
'NodeIKernelUixConvertService/getUid',
|
return await NTQQUserApi.getUidByUinV2(Uin);
|
||||||
5000,
|
}
|
||||||
[Uin]
|
return await NTQQUserApi.getUidByUinV1(Uin);
|
||||||
);
|
}
|
||||||
let uid = ret.uidInfo.get(Uin); //通过QQ默认方式转换
|
@CacheClassFuncAsyncExtend(3600 * 1000, 'Uid2Uin', (Uid: string | undefined, Uin: number | undefined) => {
|
||||||
|
if (Uin && Uin != 0 && !isNaN(Uin)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
logWarn("uid转换到uin时异常", Uid, Uin);
|
||||||
|
return false;
|
||||||
|
})
|
||||||
|
static async getUinByUid(Uid: string) {
|
||||||
|
//此代码仅临时使用,后期会被废弃
|
||||||
|
if (requireMinNTQQBuild('26702')) {
|
||||||
|
return await NTQQUserApi.getUinByUidV2(Uid);
|
||||||
|
}
|
||||||
|
return await NTQQUserApi.getUinByUidV1(Uid);
|
||||||
|
}
|
||||||
|
|
||||||
|
//后期改成流水线处理
|
||||||
|
static async getUidByUinV2(Uin: string) {
|
||||||
|
let uid = (await napCatCore.session.getProfileService().getUidByUin('FriendsServiceImpl', [Uin])).get(Uin);
|
||||||
|
if (uid) return uid;
|
||||||
|
uid = (await napCatCore.session.getGroupService().getUidByUins([Uin])).uids.get(Uin);
|
||||||
|
if (uid) return uid;
|
||||||
|
uid = (await napCatCore.session.getUixConvertService().getUid([Uin])).uidInfo.get(Uin);
|
||||||
|
if (uid) return uid;
|
||||||
|
console.log((await NTQQFriendApi.getBuddyIdMapCache(true)));
|
||||||
|
uid = (await NTQQFriendApi.getBuddyIdMapCache(true)).getValue(Uin);//从Buddy缓存获取Uid
|
||||||
|
if (uid) return uid;
|
||||||
|
uid = (await NTQQFriendApi.getBuddyIdMap(true)).getValue(Uin);
|
||||||
|
if (uid) return uid;
|
||||||
|
let unveifyUid = (await NTQQUserApi.getUserDetailInfoByUinV2(Uin)).detail.uid;//从QQ Native 特殊转换
|
||||||
|
if (unveifyUid.indexOf("*") == -1) uid = unveifyUid;
|
||||||
|
//if (uid) return uid;
|
||||||
|
return uid;
|
||||||
|
}
|
||||||
|
//后期改成流水线处理
|
||||||
|
static async getUinByUidV2(Uid: string) {
|
||||||
|
let uin = (await napCatCore.session.getProfileService().getUinByUid('FriendsServiceImpl', [Uid])).get(Uid);
|
||||||
|
if (uin) return uin;
|
||||||
|
uin = (await napCatCore.session.getGroupService().getUinByUids([Uid])).uins.get(Uid);
|
||||||
|
if (uin) return uin;
|
||||||
|
uin = (await napCatCore.session.getUixConvertService().getUin([Uid])).uinInfo.get(Uid);
|
||||||
|
if (uin) return uin;
|
||||||
|
uin = (await NTQQFriendApi.getBuddyIdMapCache(true)).getKey(Uid);//从Buddy缓存获取Uin
|
||||||
|
if (uin) return uin;
|
||||||
|
uin = (await NTQQFriendApi.getBuddyIdMap(true)).getKey(Uid);
|
||||||
|
if (uin) return uin;
|
||||||
|
uin = (await NTQQUserApi.getUserDetailInfo(Uid)).uin; //从QQ Native 转换
|
||||||
|
return uin;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async getUidByUinV1(Uin: string) {
|
||||||
|
// 通用转换开始尝试
|
||||||
|
let uid = (await napCatCore.session.getUixConvertService().getUid([Uin])).uidInfo.get(Uin);
|
||||||
|
// Uid 好友转
|
||||||
if (!uid) {
|
if (!uid) {
|
||||||
Array.from(friends.values()).forEach((t) => {
|
Array.from(friends.values()).forEach((t) => {
|
||||||
if (t.uin == Uin) {
|
if (t.uin == Uin) {
|
||||||
//logDebug('getUidByUin', t.uid, t.uin, Uin);
|
|
||||||
uid = t.uid;
|
uid = t.uid;
|
||||||
}
|
}
|
||||||
//console.log(t.uid, t.uin, Uin);
|
|
||||||
});
|
});
|
||||||
//uid = Array.from(friends.values()).find((t) => { t.uin == Uin })?.uid; // 从NC维护的QQ Buddy缓存 转换
|
|
||||||
}
|
}
|
||||||
|
//Uid 群友列表转
|
||||||
if (!uid) {
|
if (!uid) {
|
||||||
uid = (await NTQQFriendApi.getFriends(false)).find((t) => { t.uin == Uin })?.uid; //从QQ Native 缓存转换 方法一
|
for (let groupMembersList of groupMembers.values()) {
|
||||||
}
|
for (let GroupMember of groupMembersList.values()) {
|
||||||
if (!uid) {
|
if (GroupMember.uin == Uin) {
|
||||||
uid = (await NTQQFriendApi.getFriends(true)).find((t) => { t.uin == Uin })?.uid; //从QQ Native 非缓存转换 方法二
|
uid = GroupMember.uid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (!uid) {
|
if (!uid) {
|
||||||
let unveifyUid = (await NTQQUserApi.getUserDetailInfoByUin(Uin)).info.uid;//从QQ Native 特殊转换 方法三
|
let unveifyUid = (await NTQQUserApi.getUserDetailInfoByUin(Uin)).info.uid;//从QQ Native 特殊转换 方法三
|
||||||
@@ -195,17 +294,53 @@ export class NTQQUserApi {
|
|||||||
}
|
}
|
||||||
return uid;
|
return uid;
|
||||||
}
|
}
|
||||||
static async getUinByUid(Uid: string | undefined) {
|
static async getUinByUidV1(Uid: string) {
|
||||||
if (!Uid) {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
let ret = await NTEventDispatch.CallNoListenerEvent
|
let ret = await NTEventDispatch.CallNoListenerEvent
|
||||||
<(Uin: string[]) => Promise<{ uinInfo: Map<string, string> }>>(
|
<(Uin: string[]) => Promise<{ uinInfo: Map<string, string> }>>(
|
||||||
'NodeIKernelUixConvertService/getUin',
|
'NodeIKernelUixConvertService/getUin',
|
||||||
5000,
|
5000,
|
||||||
[Uid]
|
[Uid]
|
||||||
);
|
);
|
||||||
return ret.uinInfo.get(Uid);
|
let uin = ret.uinInfo.get(Uid);
|
||||||
|
if (!uin) {
|
||||||
|
//从Buddy缓存获取Uin
|
||||||
|
Array.from(friends.values()).forEach((t) => {
|
||||||
|
if (t.uid == Uid) {
|
||||||
|
uin = t.uin;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if (!uin) {
|
||||||
|
uin = (await NTQQUserApi.getUserDetailInfo(Uid)).uin; //从QQ Native 转换
|
||||||
|
}
|
||||||
|
|
||||||
|
// if (!uin) {
|
||||||
|
// uin = (await NTQQFriendApi.getFriends(false)).find((t) => { t.uid == Uid })?.uin; //从QQ Native 缓存转换
|
||||||
|
// }
|
||||||
|
// if (!uin) {
|
||||||
|
// uin = (await NTQQFriendApi.getFriends(true)).find((t) => { t.uid == Uid })?.uin; //从QQ Native 非缓存转换
|
||||||
|
// }
|
||||||
|
return uin;
|
||||||
|
}
|
||||||
|
static async getRecentContactListSnapShot(count: number) {
|
||||||
|
return await napCatCore.session.getRecentContactService().getRecentContactListSnapShot(count);
|
||||||
|
}
|
||||||
|
static async getRecentContactListSyncLimit(count: number) {
|
||||||
|
return await napCatCore.session.getRecentContactService().getRecentContactListSyncLimit(count);
|
||||||
|
}
|
||||||
|
static async getRecentContactListSync() {
|
||||||
|
return await napCatCore.session.getRecentContactService().getRecentContactListSync();
|
||||||
|
}
|
||||||
|
static async getRecentContactList() {
|
||||||
|
return await napCatCore.session.getRecentContactService().getRecentContactList();
|
||||||
|
}
|
||||||
|
static async getUserDetailInfoByUinV2(Uin: string) {
|
||||||
|
return await NTEventDispatch.CallNoListenerEvent
|
||||||
|
<(Uin: string) => Promise<UserDetailInfoByUinV2>>(
|
||||||
|
'NodeIKernelProfileService/getUserDetailInfoByUin',
|
||||||
|
5000,
|
||||||
|
Uin
|
||||||
|
);
|
||||||
}
|
}
|
||||||
static async getUserDetailInfoByUin(Uin: string) {
|
static async getUserDetailInfoByUin(Uin: string) {
|
||||||
return NTEventDispatch.CallNoListenerEvent
|
return NTEventDispatch.CallNoListenerEvent
|
||||||
|
@@ -114,6 +114,22 @@ export interface GroupEssenceMsgRet {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
export class WebApi {
|
export class WebApi {
|
||||||
|
static async shareDigest(groupCode: string, msgSeq: string, msgRandom: string, targetGroupCode: string) {
|
||||||
|
const CookiesObject = await NTQQUserApi.getCookies('qun.qq.com');
|
||||||
|
const CookieValue = Object.entries(CookiesObject).map(([key, value]) => `${key}=${value}`).join('; ');
|
||||||
|
const Bkn = WebApi.genBkn(CookiesObject.skey);
|
||||||
|
let ret: any = undefined;
|
||||||
|
const data = 'group_code=' + groupCode + '&msg_seq=' + msgSeq + '&msg_random=' + msgRandom + '&target_group_code=' + targetGroupCode;
|
||||||
|
const url = 'https://qun.qq.com/cgi-bin/group_digest/share_digest?bkn=' + Bkn + "&" + data;
|
||||||
|
//console.log(url);
|
||||||
|
try {
|
||||||
|
ret = await RequestUtil.HttpGetText(url, 'GET', '', { 'Cookie': CookieValue });
|
||||||
|
return ret;
|
||||||
|
} catch (e) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
@CacheClassFuncAsync(3600 * 1000, 'webapi_get_group_members')
|
@CacheClassFuncAsync(3600 * 1000, 'webapi_get_group_members')
|
||||||
static async getGroupEssenceMsg(GroupCode: string, page_start: string) {
|
static async getGroupEssenceMsg(GroupCode: string, page_start: string) {
|
||||||
const CookiesObject = await NTQQUserApi.getCookies('qun.qq.com');
|
const CookiesObject = await NTQQUserApi.getCookies('qun.qq.com');
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
import QQWrapper, { NodeIQQNTWrapperEngine, NodeIQQNTWrapperSession, NodeQQNTWrapperUtil } from '@/core/wrapper';
|
import QQWrapper, { NodeIQQNTWrapperEngine, NodeIQQNTWrapperSession, NodeQQNTWrapperUtil } from '@/core/wrapper';
|
||||||
|
import { DeviceList } from '@/onebot11/main';
|
||||||
import {
|
import {
|
||||||
NodeIKernelLoginService,
|
NodeIKernelLoginService,
|
||||||
NodeIKernelBuddyService,
|
NodeIKernelBuddyService,
|
||||||
@@ -14,14 +15,13 @@ import { DependsAdapter, DispatcherAdapter, GlobalAdapter, NodeIGlobalAdapter }
|
|||||||
import path from 'node:path';
|
import path from 'node:path';
|
||||||
import os from 'node:os';
|
import os from 'node:os';
|
||||||
import fs from 'node:fs';
|
import fs from 'node:fs';
|
||||||
import { appid, qqVersionConfigInfo } from '@/common/utils/QQBasicInfo';
|
import { getFullQQVesion, QQVersionAppid, QQVersionQua, requireMinNTQQBuild } from '@/common/utils/QQBasicInfo';
|
||||||
import { hostname, systemVersion } from '@/common/utils/system';
|
import { hostname, systemVersion } from '@/common/utils/system';
|
||||||
import { genSessionConfig } from '@/core/sessionConfig';
|
import { genSessionConfig } from '@/core/sessionConfig';
|
||||||
import { dbUtil } from '@/common/utils/db';
|
|
||||||
import { sleep } from '@/common/utils/helper';
|
import { sleep } from '@/common/utils/helper';
|
||||||
import crypto from 'node:crypto';
|
import crypto from 'node:crypto';
|
||||||
import { rawFriends, friends, groupMembers, groups, selfInfo, stat } from '@/core/data';
|
import { groupMembers, groups, selfInfo, stat } from '@/core/data';
|
||||||
import { RawMessage } from '@/core/entities';
|
import { GroupMember, RawMessage } from '@/core/entities';
|
||||||
import { NTEventDispatch } from '@/common/utils/EventTask';
|
import { NTEventDispatch } from '@/common/utils/EventTask';
|
||||||
import {
|
import {
|
||||||
enableConsoleLog,
|
enableConsoleLog,
|
||||||
@@ -33,6 +33,7 @@ import {
|
|||||||
setLogSelfInfo
|
setLogSelfInfo
|
||||||
} from '@/common/utils/log';
|
} from '@/common/utils/log';
|
||||||
import { napCatConfig } from '@/core/utils/config';
|
import { napCatConfig } from '@/core/utils/config';
|
||||||
|
import { NTQQFriendApi } from './apis';
|
||||||
|
|
||||||
export interface OnLoginSuccess {
|
export interface OnLoginSuccess {
|
||||||
(uin: string, uid: string): void | Promise<void>;
|
(uin: string, uid: string): void | Promise<void>;
|
||||||
@@ -84,20 +85,15 @@ export class NapCatCore {
|
|||||||
const dataPath = path.resolve(this.dataPath, './NapCat/data');
|
const dataPath = path.resolve(this.dataPath, './NapCat/data');
|
||||||
fs.mkdirSync(dataPath, { recursive: true });
|
fs.mkdirSync(dataPath, { recursive: true });
|
||||||
logDebug('本账号数据/缓存目录:', dataPath);
|
logDebug('本账号数据/缓存目录:', dataPath);
|
||||||
dbUtil.init(path.resolve(dataPath, `./${arg.uin}-v2.db`)).then(() => {
|
this.initDataListener();
|
||||||
this.initDataListener();
|
this.onLoginSuccessFuncList.map(cb => {
|
||||||
this.onLoginSuccessFuncList.map(cb => {
|
new Promise((resolve, reject) => {
|
||||||
new Promise((resolve, reject) => {
|
const result = cb(arg.uin, arg.uid);
|
||||||
const result = cb(arg.uin, arg.uid);
|
if (result instanceof Promise) {
|
||||||
if (result instanceof Promise) {
|
result.then(resolve).catch(reject);
|
||||||
result.then(resolve).catch(reject);
|
}
|
||||||
}
|
}).then();
|
||||||
}).then();
|
|
||||||
});
|
|
||||||
}).catch((e) => {
|
|
||||||
logError('数据库初始化失败', e);
|
|
||||||
});
|
});
|
||||||
// this.initDataListener();
|
|
||||||
}).catch((e) => {
|
}).catch((e) => {
|
||||||
logError('initSession failed', e);
|
logError('initSession failed', e);
|
||||||
throw new Error(`启动失败: ${JSON.stringify(e)}`);
|
throw new Error(`启动失败: ${JSON.stringify(e)}`);
|
||||||
@@ -108,8 +104,8 @@ export class NapCatCore {
|
|||||||
logError('登录失败(onQRCodeSessionFailed)', errMsg);
|
logError('登录失败(onQRCodeSessionFailed)', errMsg);
|
||||||
if (errType == 1 && errCode == 3) {
|
if (errType == 1 && errCode == 3) {
|
||||||
// 二维码过期刷新
|
// 二维码过期刷新
|
||||||
this.loginService.getQRCodePicture();
|
|
||||||
}
|
}
|
||||||
|
this.loginService.getQRCodePicture();
|
||||||
};
|
};
|
||||||
this.loginListener.onLoginFailed = (args) => {
|
this.loginListener.onLoginFailed = (args) => {
|
||||||
logError('登录失败(onLoginFailed)', args);
|
logError('登录失败(onLoginFailed)', args);
|
||||||
@@ -138,10 +134,10 @@ export class NapCatCore {
|
|||||||
base_path_prefix: '',
|
base_path_prefix: '',
|
||||||
platform_type: 3,
|
platform_type: 3,
|
||||||
app_type: 4,
|
app_type: 4,
|
||||||
app_version: qqVersionConfigInfo.curVersion,
|
app_version: getFullQQVesion(),
|
||||||
os_version: 'Windows 10 Pro',
|
os_version: 'Windows 10 Pro',
|
||||||
use_xlog: true,
|
use_xlog: true,
|
||||||
qua: `V1_WIN_NQ_${qqVersionConfigInfo.curVersion.replace('-', '_')}_GW_B`,
|
qua: QQVersionQua,
|
||||||
global_path_config: {
|
global_path_config: {
|
||||||
desktopGlobalPath: this.dataPathGlobal,
|
desktopGlobalPath: this.dataPathGlobal,
|
||||||
},
|
},
|
||||||
@@ -149,10 +145,10 @@ export class NapCatCore {
|
|||||||
}, new QQWrapper.NodeIGlobalAdapter(new GlobalAdapter()));
|
}, new QQWrapper.NodeIGlobalAdapter(new GlobalAdapter()));
|
||||||
this.loginService.initConfig({
|
this.loginService.initConfig({
|
||||||
machineId: '',
|
machineId: '',
|
||||||
appid,
|
appid: QQVersionAppid,
|
||||||
platVer: systemVersion,
|
platVer: systemVersion,
|
||||||
commonPath: this.dataPathGlobal,
|
commonPath: this.dataPathGlobal,
|
||||||
clientVer: qqVersionConfigInfo.curVersion,
|
clientVer: getFullQQVesion(),
|
||||||
hostName: hostname
|
hostName: hostname
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -189,7 +185,6 @@ export class NapCatCore {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private initDataListener() {
|
private initDataListener() {
|
||||||
// 消息相关
|
// 消息相关
|
||||||
interface LineDevice {
|
interface LineDevice {
|
||||||
@@ -208,10 +203,17 @@ export class NapCatCore {
|
|||||||
}
|
}
|
||||||
const msgListener = new MsgListener();
|
const msgListener = new MsgListener();
|
||||||
msgListener.onLineDev = (Devices: LineDevice[]) => {
|
msgListener.onLineDev = (Devices: LineDevice[]) => {
|
||||||
|
DeviceList.splice(0, DeviceList.length);
|
||||||
Devices.map((Device: LineDevice) => {
|
Devices.map((Device: LineDevice) => {
|
||||||
if (Device.clientType === 2) {
|
let DeviceData = {
|
||||||
log('账号设备(' + Device.devUid + ') 在线状态变更');
|
app_id: Device.devUid,
|
||||||
}
|
device_name: Device.clientType.toString(),
|
||||||
|
device_kind: Device.clientType.toString(),
|
||||||
|
};
|
||||||
|
DeviceList.push(DeviceData);
|
||||||
|
// if (Device.clientType === 2) {
|
||||||
|
// log('账号设备(' + Device.devUid + ') 在线状态变更');
|
||||||
|
// }
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
msgListener.onKickedOffLine = (Info: KickedOffLineInfo) => {
|
msgListener.onKickedOffLine = (Info: KickedOffLineInfo) => {
|
||||||
@@ -242,6 +244,7 @@ export class NapCatCore {
|
|||||||
stat.last_message_time = Math.floor(Date.now() / 1000);
|
stat.last_message_time = Math.floor(Date.now() / 1000);
|
||||||
};
|
};
|
||||||
msgListener.onRecvMsg = (msgList: RawMessage[]) => {
|
msgListener.onRecvMsg = (msgList: RawMessage[]) => {
|
||||||
|
// console.log(JSON.stringify(msgList[0],null,2));
|
||||||
stat.packet_received += 1;
|
stat.packet_received += 1;
|
||||||
stat.message_received += msgList.length;
|
stat.message_received += msgList.length;
|
||||||
stat.last_message_time = Math.floor(Date.now() / 1000);
|
stat.last_message_time = Math.floor(Date.now() / 1000);
|
||||||
@@ -250,31 +253,23 @@ export class NapCatCore {
|
|||||||
stat.packet_received += 1;
|
stat.packet_received += 1;
|
||||||
};
|
};
|
||||||
this.addListener(msgListener);
|
this.addListener(msgListener);
|
||||||
// 好友相关
|
// 好友相关
|
||||||
const buddyListener = new BuddyListener();
|
const buddyListener = new BuddyListener();
|
||||||
buddyListener.onBuddyListChange = arg => {
|
|
||||||
rawFriends.length = 0;
|
|
||||||
rawFriends.push(...arg);
|
|
||||||
// console.log('onBuddyListChange', arg);
|
|
||||||
for (const categoryItem of arg) {
|
|
||||||
for (const friend of categoryItem.buddyList) {
|
|
||||||
// console.log("onBuddyListChange", friend)
|
|
||||||
const existFriend = friends.get(friend.uid);
|
|
||||||
if (existFriend) {
|
|
||||||
Object.assign(existFriend, friend);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
friends.set(friend.uid, friend);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// console.log("onBuddyListChange", friend)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
this.addListener(buddyListener);
|
this.addListener(buddyListener);
|
||||||
// 刷新一次好友列表
|
// 刷新一次好友列表 26702版本以下需要手动刷新一次获取 高版本NTQQ自带缓存
|
||||||
this.session.getBuddyService().getBuddyList(true).then(arg => {
|
if (!requireMinNTQQBuild('26702')) {
|
||||||
// console.log('getBuddyList', arg);
|
this.session.getBuddyService().getBuddyList(true).then(arg => {
|
||||||
});
|
// console.log('getBuddyList', arg);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// NTQQFriendApi.getBuddyV2(true).then((res) => {
|
||||||
|
// res.forEach((item) => {
|
||||||
|
// CachedIdMap.set(item.uid!, item.uin!);
|
||||||
|
// });
|
||||||
|
// }).catch();
|
||||||
|
}
|
||||||
interface SelfStatusInfo {
|
interface SelfStatusInfo {
|
||||||
uid: string
|
uid: string
|
||||||
status: number
|
status: number
|
||||||
@@ -327,6 +322,7 @@ export class NapCatCore {
|
|||||||
if (groupMembers.has(groupCode)) {
|
if (groupMembers.has(groupCode)) {
|
||||||
const existMembers = groupMembers.get(groupCode)!;
|
const existMembers = groupMembers.get(groupCode)!;
|
||||||
arg.infos.forEach((member, uid) => {
|
arg.infos.forEach((member, uid) => {
|
||||||
|
//console.log('onMemberListChange', member);
|
||||||
const existMember = existMembers.get(uid);
|
const existMember = existMembers.get(uid);
|
||||||
if (existMember) {
|
if (existMember) {
|
||||||
Object.assign(existMember, member);
|
Object.assign(existMember, member);
|
||||||
@@ -346,7 +342,7 @@ export class NapCatCore {
|
|||||||
// console.log('onMemberListChange', groupCode, arg);
|
// console.log('onMemberListChange', groupCode, arg);
|
||||||
};
|
};
|
||||||
groupListener.onMemberInfoChange = (groupCode, changeType, members) => {
|
groupListener.onMemberInfoChange = (groupCode, changeType, members) => {
|
||||||
// console.log('onMemberInfoChange', arg);
|
//console.log('onMemberInfoChange', groupCode, changeType, members);
|
||||||
if (changeType === 0 && members.get(selfInfo.uid)?.isDelete) {
|
if (changeType === 0 && members.get(selfInfo.uid)?.isDelete) {
|
||||||
// 自身退群或者被踢退群 5s用于Api操作 之后不再出现
|
// 自身退群或者被踢退群 5s用于Api操作 之后不再出现
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
@@ -359,6 +355,9 @@ export class NapCatCore {
|
|||||||
members.forEach((member, uid) => {
|
members.forEach((member, uid) => {
|
||||||
const existMember = existMembers.get(uid);
|
const existMember = existMembers.get(uid);
|
||||||
if (existMember) {
|
if (existMember) {
|
||||||
|
// 检查管理变动
|
||||||
|
member.isChangeRole = this.checkAdminEvent(groupCode, member, existMember);
|
||||||
|
// 更新成员信息
|
||||||
Object.assign(existMember, member);
|
Object.assign(existMember, member);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -471,6 +470,13 @@ export class NapCatCore {
|
|||||||
const loginList = await this.loginService.getLoginList();
|
const loginList = await this.loginService.getLoginList();
|
||||||
return loginList;
|
return loginList;
|
||||||
}
|
}
|
||||||
|
checkAdminEvent(groupCode: string, memberNew: GroupMember, memberOld: GroupMember | undefined): boolean {
|
||||||
|
if (memberNew.role !== memberOld?.role) {
|
||||||
|
log(`群 ${groupCode} ${memberNew.nick} 角色变更为 ${memberNew.role === 3 ? '管理员' : '群员'}`);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const napCatCore = new NapCatCore();
|
export const napCatCore = new NapCatCore();
|
||||||
|
@@ -1,11 +1,13 @@
|
|||||||
import {
|
import {
|
||||||
type Friend,
|
type Friend,
|
||||||
type Group,
|
type Group,
|
||||||
type GroupMember, GroupNotify,
|
type GroupMember,
|
||||||
type SelfInfo,
|
type SelfInfo,
|
||||||
BuddyCategoryType
|
type BuddyCategoryType,
|
||||||
|
FriendV2
|
||||||
} from './entities';
|
} from './entities';
|
||||||
import { isNumeric } from '@/common/utils/helper';
|
import { isNumeric } from '@/common/utils/helper';
|
||||||
|
import { LimitedHashTable } from '@/common/utils/MessageUnique';
|
||||||
import { NTQQGroupApi } from '@/core/apis';
|
import { NTQQGroupApi } from '@/core/apis';
|
||||||
|
|
||||||
export const selfInfo: SelfInfo = {
|
export const selfInfo: SelfInfo = {
|
||||||
@@ -14,8 +16,6 @@ export const selfInfo: SelfInfo = {
|
|||||||
nick: '',
|
nick: '',
|
||||||
online: true
|
online: true
|
||||||
};
|
};
|
||||||
// 未来只在此处保留 selfInfo stat
|
|
||||||
// groupCode -> Group
|
|
||||||
export const groups: Map<string, Group> = new Map<string, Group>();
|
export const groups: Map<string, Group> = new Map<string, Group>();
|
||||||
|
|
||||||
export function deleteGroup(groupQQ: string) {
|
export function deleteGroup(groupQQ: string) {
|
||||||
@@ -26,11 +26,11 @@ export function deleteGroup(groupQQ: string) {
|
|||||||
// 群号 -> 群成员map(uid=>GroupMember)
|
// 群号 -> 群成员map(uid=>GroupMember)
|
||||||
export const groupMembers: Map<string, Map<string, GroupMember>> = new Map<string, Map<string, GroupMember>>();
|
export const groupMembers: Map<string, Map<string, GroupMember>> = new Map<string, Map<string, GroupMember>>();
|
||||||
|
|
||||||
// uid -> Friend 下面这俩个准备移除 QQ里面自带缓存
|
|
||||||
export const friends: Map<string, Friend> = new Map<string, Friend>();
|
export const friends: Map<string, Friend> = new Map<string, Friend>();
|
||||||
export const rawFriends: Array<BuddyCategoryType> = []; // 带分组的好友列表
|
|
||||||
|
|
||||||
export const groupNotifies: Record<string, GroupNotify> = {}; // flag->GroupNotify
|
//转换列表
|
||||||
|
//export const CachedIdMap = new LimitedHashTable<string, string>(1000);
|
||||||
|
|
||||||
export async function getGroup(qq: string | number): Promise<Group | undefined> {
|
export async function getGroup(qq: string | number): Promise<Group | undefined> {
|
||||||
let group = groups.get(qq.toString());
|
let group = groups.get(qq.toString());
|
||||||
if (!group) {
|
if (!group) {
|
||||||
|
@@ -8,8 +8,10 @@ import {
|
|||||||
SendPicElement,
|
SendPicElement,
|
||||||
SendPttElement,
|
SendPttElement,
|
||||||
SendReplyElement,
|
SendReplyElement,
|
||||||
|
sendShareLocationElement,
|
||||||
SendTextElement,
|
SendTextElement,
|
||||||
SendVideoElement
|
SendVideoElement,
|
||||||
|
viedo_type
|
||||||
} from './index';
|
} from './index';
|
||||||
import { promises as fs } from 'node:fs';
|
import { promises as fs } from 'node:fs';
|
||||||
import ffmpeg from 'fluent-ffmpeg';
|
import ffmpeg from 'fluent-ffmpeg';
|
||||||
@@ -27,6 +29,16 @@ export const mFaceCache = new Map<string, string>(); // emojiId -> faceName
|
|||||||
|
|
||||||
|
|
||||||
export class SendMsgElementConstructor {
|
export class SendMsgElementConstructor {
|
||||||
|
static location(): sendShareLocationElement {
|
||||||
|
return {
|
||||||
|
elementType: ElementType.SHARELOCATION,
|
||||||
|
elementId: '',
|
||||||
|
shareLocationElement: {
|
||||||
|
text: "测试",
|
||||||
|
ext: ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
static text(content: string): SendTextElement {
|
static text(content: string): SendTextElement {
|
||||||
return {
|
return {
|
||||||
elementType: ElementType.TEXT,
|
elementType: ElementType.TEXT,
|
||||||
@@ -116,7 +128,7 @@ export class SendMsgElementConstructor {
|
|||||||
return element;
|
return element;
|
||||||
}
|
}
|
||||||
|
|
||||||
static async video(filePath: string, fileName: string = '', diyThumbPath: string = ''): Promise<SendVideoElement> {
|
static async video(filePath: string, fileName: string = '', diyThumbPath: string = '', videotype: viedo_type = viedo_type.VIDEO_FORMAT_MP4): Promise<SendVideoElement> {
|
||||||
const { fileName: _fileName, path, fileSize, md5 } = await NTQQFileApi.uploadFile(filePath, ElementType.VIDEO);
|
const { fileName: _fileName, path, fileSize, md5 } = await NTQQFileApi.uploadFile(filePath, ElementType.VIDEO);
|
||||||
if (fileSize === 0) {
|
if (fileSize === 0) {
|
||||||
throw '文件异常,大小为0';
|
throw '文件异常,大小为0';
|
||||||
@@ -184,6 +196,7 @@ export class SendMsgElementConstructor {
|
|||||||
thumbWidth: videoInfo.width,
|
thumbWidth: videoInfo.width,
|
||||||
thumbHeight: videoInfo.height,
|
thumbHeight: videoInfo.height,
|
||||||
fileSize: '' + fileSize,
|
fileSize: '' + fileSize,
|
||||||
|
//fileFormat: videotype
|
||||||
// fileUuid: "",
|
// fileUuid: "",
|
||||||
// transferStatus: 0,
|
// transferStatus: 0,
|
||||||
// progress: 0,
|
// progress: 0,
|
||||||
@@ -235,7 +248,28 @@ export class SendMsgElementConstructor {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
// NodeIQQNTWrapperSession sendMsg [
|
||||||
|
// "0",
|
||||||
|
// {
|
||||||
|
// "peerUid": "u_e_RIxgTs2NaJ68h0PwOPSg",
|
||||||
|
// "chatType": 1,
|
||||||
|
// "guildId": ""
|
||||||
|
// },
|
||||||
|
// [
|
||||||
|
// {
|
||||||
|
// "elementId": "0",
|
||||||
|
// "elementType": 6,
|
||||||
|
// "faceElement": {
|
||||||
|
// "faceIndex": 0,
|
||||||
|
// "faceType": 5,
|
||||||
|
// "msgType": 0,
|
||||||
|
// "pokeType": 1,
|
||||||
|
// "pokeStrength": 0
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// ],
|
||||||
|
// {}
|
||||||
|
// ]
|
||||||
static face(faceId: number): SendFaceElement {
|
static face(faceId: number): SendFaceElement {
|
||||||
// 从face_config.json中获取表情名称
|
// 从face_config.json中获取表情名称
|
||||||
const sysFaces = faceConfig.sysface;
|
const sysFaces = faceConfig.sysface;
|
||||||
|
@@ -1,12 +1,18 @@
|
|||||||
import { QQLevel, Sex } from './user';
|
import { QQLevel, Sex } from './user';
|
||||||
|
export enum GroupListUpdateType {
|
||||||
|
REFRESHALL,
|
||||||
|
GETALL,
|
||||||
|
MODIFIED,
|
||||||
|
REMOVE
|
||||||
|
}
|
||||||
export interface Group {
|
export interface Group {
|
||||||
groupCode: string,
|
groupCode: string,
|
||||||
|
createTime?: string,//高版本才有
|
||||||
maxMember: number,
|
maxMember: number,
|
||||||
memberCount: number,
|
memberCount: number,
|
||||||
groupName: string,
|
groupName: string,
|
||||||
groupStatus: 0,
|
groupStatus: number,
|
||||||
memberRole: 2,
|
memberRole: number,
|
||||||
isTop: boolean,
|
isTop: boolean,
|
||||||
toppedTimestamp: string,
|
toppedTimestamp: string,
|
||||||
privilegeFlag: number, //65760
|
privilegeFlag: number, //65760
|
||||||
@@ -52,4 +58,7 @@ export interface GroupMember {
|
|||||||
isRobot: boolean;
|
isRobot: boolean;
|
||||||
sex?: Sex
|
sex?: Sex
|
||||||
qqLevel?: QQLevel
|
qqLevel?: QQLevel
|
||||||
|
isChangeRole: boolean;
|
||||||
|
joinTime: string;
|
||||||
|
lastSpeakTime: string;
|
||||||
}
|
}
|
@@ -23,6 +23,7 @@ export interface GetFileListParam {
|
|||||||
showOnlinedocFolder: number
|
showOnlinedocFolder: number
|
||||||
}
|
}
|
||||||
export enum ElementType {
|
export enum ElementType {
|
||||||
|
UNKNOWN = 0,
|
||||||
TEXT = 1,
|
TEXT = 1,
|
||||||
PIC = 2,
|
PIC = 2,
|
||||||
FILE = 3,
|
FILE = 3,
|
||||||
@@ -30,11 +31,192 @@ export enum ElementType {
|
|||||||
VIDEO = 5,
|
VIDEO = 5,
|
||||||
FACE = 6,
|
FACE = 6,
|
||||||
REPLY = 7,
|
REPLY = 7,
|
||||||
|
WALLET = 9,
|
||||||
|
GreyTip = 8,//Poke别叫戳一搓了 官方名字拍一拍 戳一戳是另一个名字
|
||||||
ARK = 10,
|
ARK = 10,
|
||||||
MFACE = 11,
|
MFACE = 11,
|
||||||
MARKDOWN = 14
|
LIVEGIFT = 12,
|
||||||
|
STRUCTLONGMSG = 13,
|
||||||
|
MARKDOWN = 14,
|
||||||
|
GIPHY = 15,
|
||||||
|
MULTIFORWARD = 16,
|
||||||
|
INLINEKEYBOARD = 17,
|
||||||
|
INTEXTGIFT = 18,
|
||||||
|
CALENDAR = 19,
|
||||||
|
YOLOGAMERESULT = 20,
|
||||||
|
AVRECORD = 21,
|
||||||
|
FEED = 22,
|
||||||
|
TOFURECORD = 23,
|
||||||
|
ACEBUBBLE = 24,
|
||||||
|
ACTIVITY = 25,
|
||||||
|
TOFU = 26,
|
||||||
|
FACEBUBBLE = 27,
|
||||||
|
SHARELOCATION = 28,
|
||||||
|
TASKTOPMSG = 29,
|
||||||
|
RECOMMENDEDMSG = 43,
|
||||||
|
ACTIONBAR = 44
|
||||||
|
}
|
||||||
|
export interface ActionBarElement {
|
||||||
|
rows: InlineKeyboardRow[];
|
||||||
|
botAppid: string;
|
||||||
|
}
|
||||||
|
export interface SendActionBarElement {
|
||||||
|
elementType: ElementType.ACTIONBAR;
|
||||||
|
elementId: string;
|
||||||
|
actionBarElement: ActionBarElement;
|
||||||
|
}
|
||||||
|
export interface RecommendedMsgElement {
|
||||||
|
rows: InlineKeyboardRow[];
|
||||||
|
botAppid: string;
|
||||||
|
}
|
||||||
|
export interface SendRecommendedMsgElement {
|
||||||
|
elementType: ElementType.RECOMMENDEDMSG;
|
||||||
|
elementId: string;
|
||||||
|
recommendedMsgElement: RecommendedMsgElement;
|
||||||
|
}
|
||||||
|
export interface InlineKeyboardButton {
|
||||||
|
id: string;
|
||||||
|
label: string;
|
||||||
|
visitedLabel: string;
|
||||||
|
unsupportTips: string;
|
||||||
|
data: string;
|
||||||
|
specifyRoleIds: string[];
|
||||||
|
specifyTinyids: string[];
|
||||||
|
style: number;
|
||||||
|
type: number;
|
||||||
|
clickLimit: number;
|
||||||
|
atBotShowChannelList: boolean;
|
||||||
|
permissionType: number;
|
||||||
|
}
|
||||||
|
export interface InlineKeyboardRow {
|
||||||
|
buttons: InlineKeyboardButton[];
|
||||||
|
}
|
||||||
|
export interface TofuElementContent {
|
||||||
|
color: string;
|
||||||
|
tittle: string;
|
||||||
|
}
|
||||||
|
export interface TaskTopMsgElement {
|
||||||
|
msgTitle: string;
|
||||||
|
msgSummary: string;
|
||||||
|
iconUrl: string;
|
||||||
|
topMsgType: number;
|
||||||
|
}
|
||||||
|
export interface SendTaskTopMsgElement {
|
||||||
|
elementType: ElementType.TASKTOPMSG;
|
||||||
|
elementId: string;
|
||||||
|
taskTopMsgElement: TaskTopMsgElement;
|
||||||
|
}
|
||||||
|
export interface TofuRecordElement {
|
||||||
|
type: number;
|
||||||
|
busiid: string;
|
||||||
|
busiuuid: string;
|
||||||
|
descriptionContent: string;
|
||||||
|
contentlist: TofuElementContent[],
|
||||||
|
background: string;
|
||||||
|
icon: string;
|
||||||
|
uinlist: string[],
|
||||||
|
uidlist: string[],
|
||||||
|
busiExtra: string;
|
||||||
|
updateTime: string;
|
||||||
|
dependedmsgid: string;
|
||||||
|
msgtime: string;
|
||||||
|
onscreennotify: boolean;
|
||||||
|
}
|
||||||
|
export interface SendTofuRecordElement {
|
||||||
|
elementType: ElementType.TOFURECORD;
|
||||||
|
elementId: string;
|
||||||
|
tofuRecordElement: TofuRecordElement;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface FaceBubbleElement {
|
||||||
|
faceCount: number;
|
||||||
|
faceSummary: string;
|
||||||
|
faceFlag: number;
|
||||||
|
content: string;
|
||||||
|
oldVersionStr: string;
|
||||||
|
faceType: number;
|
||||||
|
others: string;
|
||||||
|
yellowFaceInfo: {
|
||||||
|
index: number;
|
||||||
|
buf: string;
|
||||||
|
compatibleText: string;
|
||||||
|
text: string;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export interface SendFaceBubbleElement {
|
||||||
|
elementType: ElementType.FACEBUBBLE;
|
||||||
|
elementId: string;
|
||||||
|
faceBubbleElement: FaceBubbleElement;
|
||||||
|
|
||||||
|
}
|
||||||
|
export interface AvRecordElement {
|
||||||
|
type: number;
|
||||||
|
time: string;
|
||||||
|
text: string;
|
||||||
|
mainType: number;
|
||||||
|
hasRead: boolean;
|
||||||
|
extraType: number;
|
||||||
|
}
|
||||||
|
export interface SendavRecordElement {
|
||||||
|
elementType: ElementType.AVRECORD;
|
||||||
|
elementId: string;
|
||||||
|
avRecordElement: AvRecordElement;
|
||||||
|
}
|
||||||
|
export interface YoloUserInfo {
|
||||||
|
uid: string;
|
||||||
|
result: number;
|
||||||
|
rank: number;
|
||||||
|
bizId: string
|
||||||
|
}
|
||||||
|
export interface SendInlineKeyboardElement {
|
||||||
|
elementType: ElementType.INLINEKEYBOARD;
|
||||||
|
elementId: string;
|
||||||
|
inlineKeyboardElement: {
|
||||||
|
rows: number;
|
||||||
|
botAppid: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
export interface YoloGameResultElement {
|
||||||
|
UserInfo: YoloUserInfo[];
|
||||||
|
}
|
||||||
|
export interface SendYoloGameResultElement {
|
||||||
|
elementType: ElementType.YOLOGAMERESULT;
|
||||||
|
yoloGameResultElement: YoloGameResultElement
|
||||||
|
}
|
||||||
|
export interface GiphyElement {
|
||||||
|
id: string;
|
||||||
|
isClip: boolean;
|
||||||
|
width: number;
|
||||||
|
height: number;
|
||||||
|
}
|
||||||
|
export interface SendGiphyElement {
|
||||||
|
elementType: ElementType.GIPHY;
|
||||||
|
elementId: string;
|
||||||
|
giphyElement: GiphyElement;
|
||||||
|
}
|
||||||
|
export interface SendWalletElement {
|
||||||
|
elementType: ElementType.UNKNOWN;//不做 设置位置
|
||||||
|
elementId: string;
|
||||||
|
walletElement: {}
|
||||||
|
}
|
||||||
|
export interface CalendarElement {
|
||||||
|
summary: string;
|
||||||
|
msg: string;
|
||||||
|
expireTimeMs: string;
|
||||||
|
schemaType: number;
|
||||||
|
schema: string
|
||||||
|
}
|
||||||
|
export interface SendCalendarElement {
|
||||||
|
elementType: ElementType.CALENDAR;
|
||||||
|
elementId: string;
|
||||||
|
calendarElement: CalendarElement;
|
||||||
|
}
|
||||||
|
export interface SendliveGiftElement {
|
||||||
|
elementType: ElementType.LIVEGIFT;
|
||||||
|
elementId: string;
|
||||||
|
liveGiftElement: {}
|
||||||
|
}
|
||||||
export interface SendTextElement {
|
export interface SendTextElement {
|
||||||
elementType: ElementType.TEXT;
|
elementType: ElementType.TEXT;
|
||||||
elementId: string;
|
elementId: string;
|
||||||
@@ -80,32 +262,18 @@ export enum PicSubType {
|
|||||||
export interface SendPicElement {
|
export interface SendPicElement {
|
||||||
elementType: ElementType.PIC;
|
elementType: ElementType.PIC;
|
||||||
elementId: string;
|
elementId: string;
|
||||||
picElement: {
|
picElement:PicElement
|
||||||
md5HexStr: string;
|
}
|
||||||
fileSize: number | string;
|
export interface ReplyElement {
|
||||||
picWidth: number;
|
replayMsgSeq: string;
|
||||||
picHeight: number;
|
replayMsgId: string;
|
||||||
fileName: string;
|
senderUin: string;
|
||||||
sourcePath: string;
|
senderUinStr: string;
|
||||||
original: boolean;
|
|
||||||
picType: PicType;
|
|
||||||
picSubType: PicSubType;
|
|
||||||
fileUuid: string;
|
|
||||||
fileSubId: string;
|
|
||||||
thumbFileSize: number;
|
|
||||||
summary: string;
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SendReplyElement {
|
export interface SendReplyElement {
|
||||||
elementType: ElementType.REPLY;
|
elementType: ElementType.REPLY;
|
||||||
elementId: string;
|
elementId: string;
|
||||||
replyElement: {
|
replyElement: ReplyElement
|
||||||
replayMsgSeq: string;
|
|
||||||
replayMsgId: string;
|
|
||||||
senderUin: string;
|
|
||||||
senderUinStr: string;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SendFaceElement {
|
export interface SendFaceElement {
|
||||||
@@ -118,6 +286,32 @@ export interface SendMarketFaceElement {
|
|||||||
elementType: ElementType.MFACE;
|
elementType: ElementType.MFACE;
|
||||||
marketFaceElement: MarketFaceElement;
|
marketFaceElement: MarketFaceElement;
|
||||||
}
|
}
|
||||||
|
export interface SendstructLongMsgElement {
|
||||||
|
elementType: ElementType.STRUCTLONGMSG;
|
||||||
|
elementId: string;
|
||||||
|
structLongMsgElement: StructLongMsgElement;
|
||||||
|
}
|
||||||
|
export interface StructLongMsgElement {
|
||||||
|
xmlContent: string;
|
||||||
|
resId: string;
|
||||||
|
}
|
||||||
|
export interface SendactionBarElement {
|
||||||
|
elementType: ElementType.ACTIONBAR;
|
||||||
|
elementId: string;
|
||||||
|
actionBarElement: {
|
||||||
|
rows: number;
|
||||||
|
botAppid: string;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export interface ShareLocationElement {
|
||||||
|
text: string;
|
||||||
|
ext: string;
|
||||||
|
}
|
||||||
|
export interface sendShareLocationElement {
|
||||||
|
elementType: ElementType.SHARELOCATION;
|
||||||
|
elementId: string;
|
||||||
|
shareLocationElement?: ShareLocationElement;
|
||||||
|
}
|
||||||
|
|
||||||
export interface FileElement {
|
export interface FileElement {
|
||||||
fileMd5?: string;
|
fileMd5?: string;
|
||||||
@@ -160,10 +354,51 @@ export interface SendMarkdownElement {
|
|||||||
elementId: string;
|
elementId: string;
|
||||||
markdownElement: MarkdownElement;
|
markdownElement: MarkdownElement;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type SendMessageElement = SendTextElement | SendPttElement |
|
export type SendMessageElement = SendTextElement | SendPttElement |
|
||||||
SendPicElement | SendReplyElement | SendFaceElement | SendMarketFaceElement | SendFileElement | SendVideoElement | SendArkElement | SendMarkdownElement
|
SendPicElement | SendReplyElement | SendFaceElement | SendMarketFaceElement | SendFileElement |
|
||||||
|
SendVideoElement | SendArkElement | SendMarkdownElement | sendShareLocationElement;
|
||||||
|
|
||||||
|
export interface TextElement {
|
||||||
|
content: string;
|
||||||
|
atType: number;
|
||||||
|
atUid: string;
|
||||||
|
atTinyId: string;
|
||||||
|
atNtUid: string;
|
||||||
|
}
|
||||||
|
export interface MessageElement {
|
||||||
|
elementType: ElementType,
|
||||||
|
elementId: string,
|
||||||
|
extBufForUI: string,//"0x",
|
||||||
|
textElement?: TextElement;
|
||||||
|
faceElement?: FaceElement,
|
||||||
|
marketFaceElement?: MarkdownElement,
|
||||||
|
replyElement?: ReplyElement,
|
||||||
|
picElement?: PicElement,
|
||||||
|
pttElement?: PttElement,
|
||||||
|
videoElement?: VideoElement,
|
||||||
|
grayTipElement?: GrayTipElement,
|
||||||
|
arkElement?: ArkElement,
|
||||||
|
fileElement?: FileElement,
|
||||||
|
liveGiftElement?: null,
|
||||||
|
markdownElement?: MarkdownElement,
|
||||||
|
structLongMsgElement?: StructLongMsgElement,
|
||||||
|
multiForwardMsgElement?: MultiForwardMsgElement,
|
||||||
|
giphyElement?: GiphyElement,
|
||||||
|
walletElement?: null,
|
||||||
|
inlineKeyboardElement?: InlineKeyboardElement,
|
||||||
|
textGiftElement?: null,//????
|
||||||
|
calendarElement?: CalendarElement,
|
||||||
|
yoloGameResultElement?: YoloGameResultElement,
|
||||||
|
avRecordElement?: AvRecordElement,
|
||||||
|
structMsgElement?: null,
|
||||||
|
faceBubbleElement?: FaceBubbleElement,
|
||||||
|
shareLocationElement?: ShareLocationElement,
|
||||||
|
tofuRecordElement?: TofuRecordElement,
|
||||||
|
taskTopMsgElement?: TaskTopMsgElement,
|
||||||
|
recommendedMsgElement?: RecommendedMsgElement,
|
||||||
|
actionBarElement?:ActionBarElement
|
||||||
|
|
||||||
|
}
|
||||||
export enum AtType {
|
export enum AtType {
|
||||||
notAt = 0,
|
notAt = 0,
|
||||||
atAll = 1,
|
atAll = 1,
|
||||||
@@ -254,18 +489,23 @@ export const IMAGE_HTTP_HOST = 'https://gchat.qpic.cn';
|
|||||||
export const IMAGE_HTTP_HOST_NT = 'https://multimedia.nt.qq.com.cn';
|
export const IMAGE_HTTP_HOST_NT = 'https://multimedia.nt.qq.com.cn';
|
||||||
|
|
||||||
export interface PicElement {
|
export interface PicElement {
|
||||||
picSubType?: number;
|
md5HexStr?: string;
|
||||||
originImageUrl: string; // http url, 没有host,host是https://gchat.qpic.cn/, 带download参数的是https://multimedia.nt.qq.com.cn
|
fileSize: number | string;//number
|
||||||
originImageMd5?: string;
|
|
||||||
sourcePath: string; // 图片本地路径
|
|
||||||
thumbPath: Map<number, string>;
|
|
||||||
picWidth: number;
|
picWidth: number;
|
||||||
picHeight: number;
|
picHeight: number;
|
||||||
fileSize: number;
|
|
||||||
fileName: string;
|
fileName: string;
|
||||||
|
sourcePath: string;
|
||||||
|
original: boolean;
|
||||||
|
picType: PicType;
|
||||||
|
picSubType?: PicSubType;
|
||||||
fileUuid: string;
|
fileUuid: string;
|
||||||
md5HexStr?: string;
|
fileSubId: string;
|
||||||
}
|
thumbFileSize: number;
|
||||||
|
summary: string;
|
||||||
|
thumbPath: Map<number, string>;
|
||||||
|
originImageMd5?: string;
|
||||||
|
originImageUrl?: string; // http url, 没有host,host是https://gchat.qpic.cn/, 带download参数的是https://multimedia.nt.qq.com.cn
|
||||||
|
};
|
||||||
|
|
||||||
export enum GrayTipElementSubType {
|
export enum GrayTipElementSubType {
|
||||||
INVITE_NEW_MEMBER = 12,
|
INVITE_NEW_MEMBER = 12,
|
||||||
@@ -289,6 +529,7 @@ export interface GrayTipElement {
|
|||||||
templId: string;
|
templId: string;
|
||||||
};
|
};
|
||||||
jsonGrayTipElement: {
|
jsonGrayTipElement: {
|
||||||
|
busiId?: number;
|
||||||
jsonStr: string;
|
jsonStr: string;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -329,7 +570,7 @@ export interface VideoElement {
|
|||||||
thumbMd5?: string
|
thumbMd5?: string
|
||||||
fileTime?: number; // second
|
fileTime?: number; // second
|
||||||
thumbSize?: number; // byte
|
thumbSize?: number; // byte
|
||||||
fileFormat?: number; // 2表示mp4 参考下面条目
|
fileFormat?: viedo_type; // 2表示mp4 参考下面条目
|
||||||
fileSize?: string; // byte
|
fileSize?: string; // byte
|
||||||
thumbWidth?: number;
|
thumbWidth?: number;
|
||||||
thumbHeight?: number;
|
thumbHeight?: number;
|
||||||
@@ -490,6 +731,8 @@ export interface MultiForwardMsgElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface RawMessage {
|
export interface RawMessage {
|
||||||
|
guildId: string;
|
||||||
|
msgRandom: string;
|
||||||
// int32, 自己维护的消息id
|
// int32, 自己维护的消息id
|
||||||
id?: number;
|
id?: number;
|
||||||
|
|
||||||
@@ -510,10 +753,12 @@ export interface RawMessage {
|
|||||||
chatType: ChatType;
|
chatType: ChatType;
|
||||||
sendStatus?: number; // 消息状态,别人发的2是已撤回,自己发的2是已发送
|
sendStatus?: number; // 消息状态,别人发的2是已撤回,自己发的2是已发送
|
||||||
recallTime: string; // 撤回时间, "0"是没有撤回
|
recallTime: string; // 撤回时间, "0"是没有撤回
|
||||||
|
records: RawMessage[];
|
||||||
elements: {
|
elements: {
|
||||||
elementId: string;
|
elementId: string;
|
||||||
elementType: ElementType;
|
elementType: ElementType;
|
||||||
replyElement: {
|
replyElement: {
|
||||||
|
sourceMsgIdInRecords: string;
|
||||||
senderUid: string; // 原消息发送者QQ号
|
senderUid: string; // 原消息发送者QQ号
|
||||||
sourceMsgIsIncPic: boolean; // 原消息是否有图片
|
sourceMsgIsIncPic: boolean; // 原消息是否有图片
|
||||||
sourceMsgText: string;
|
sourceMsgText: string;
|
||||||
|
@@ -62,6 +62,7 @@ export enum BuddyReqType {
|
|||||||
KMEINITIATORWAITPEERCONFIRM
|
KMEINITIATORWAITPEERCONFIRM
|
||||||
}
|
}
|
||||||
export interface FriendRequest {
|
export interface FriendRequest {
|
||||||
|
isInitiator?: boolean;
|
||||||
isDecide: boolean;
|
isDecide: boolean;
|
||||||
friendUid: string;
|
friendUid: string;
|
||||||
reqType: BuddyReqType,
|
reqType: BuddyReqType,
|
||||||
|
@@ -9,7 +9,181 @@ export interface BuddyCategoryType {
|
|||||||
categroyMbCount: number;
|
categroyMbCount: number;
|
||||||
buddyList: User[];
|
buddyList: User[];
|
||||||
}
|
}
|
||||||
|
export interface CoreInfo {
|
||||||
|
uid: string;
|
||||||
|
uin: string;
|
||||||
|
nick: string;
|
||||||
|
remark: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface BaseInfo {
|
||||||
|
qid: string;
|
||||||
|
longNick: string;
|
||||||
|
birthday_year: number;
|
||||||
|
birthday_month: number;
|
||||||
|
birthday_day: number;
|
||||||
|
age: number;
|
||||||
|
sex: number;
|
||||||
|
eMail: string;
|
||||||
|
phoneNum: string;
|
||||||
|
categoryId: number;
|
||||||
|
richTime: number;
|
||||||
|
richBuffer: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface MusicInfo {
|
||||||
|
buf: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface VideoBizInfo {
|
||||||
|
cid: string;
|
||||||
|
tvUrl: string;
|
||||||
|
synchType: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface VideoInfo {
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ExtOnlineBusinessInfo {
|
||||||
|
buf: string;
|
||||||
|
customStatus: any;
|
||||||
|
videoBizInfo: VideoBizInfo;
|
||||||
|
videoInfo: VideoInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ExtBuffer {
|
||||||
|
buf: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface UserStatus {
|
||||||
|
uid: string;
|
||||||
|
uin: string;
|
||||||
|
status: number;
|
||||||
|
extStatus: number;
|
||||||
|
batteryStatus: number;
|
||||||
|
termType: number;
|
||||||
|
netType: number;
|
||||||
|
iconType: number;
|
||||||
|
customStatus: any;
|
||||||
|
setTime: string;
|
||||||
|
specialFlag: number;
|
||||||
|
abiFlag: number;
|
||||||
|
eNetworkType: number;
|
||||||
|
showName: string;
|
||||||
|
termDesc: string;
|
||||||
|
musicInfo: MusicInfo;
|
||||||
|
extOnlineBusinessInfo: ExtOnlineBusinessInfo;
|
||||||
|
extBuffer: ExtBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface PrivilegeIcon {
|
||||||
|
jumpUrl: string;
|
||||||
|
openIconList: any[];
|
||||||
|
closeIconList: any[];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface VasInfo {
|
||||||
|
vipFlag: boolean;
|
||||||
|
yearVipFlag: boolean;
|
||||||
|
svipFlag: boolean;
|
||||||
|
vipLevel: number;
|
||||||
|
bigClub: boolean;
|
||||||
|
bigClubLevel: number;
|
||||||
|
nameplateVipType: number;
|
||||||
|
grayNameplateFlag: number;
|
||||||
|
superVipTemplateId: number;
|
||||||
|
diyFontId: number;
|
||||||
|
pendantId: number;
|
||||||
|
pendantDiyId: number;
|
||||||
|
faceId: number;
|
||||||
|
vipFont: number;
|
||||||
|
vipFontType: number;
|
||||||
|
magicFont: number;
|
||||||
|
fontEffect: number;
|
||||||
|
newLoverDiamondFlag: number;
|
||||||
|
extendNameplateId: number;
|
||||||
|
diyNameplateIDs: any[];
|
||||||
|
vipStartFlag: number;
|
||||||
|
vipDataFlag: number;
|
||||||
|
gameNameplateId: string;
|
||||||
|
gameLastLoginTime: string;
|
||||||
|
gameRank: number;
|
||||||
|
gameIconShowFlag: boolean;
|
||||||
|
gameCardId: string;
|
||||||
|
vipNameColorId: string;
|
||||||
|
privilegeIcon: PrivilegeIcon;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface RelationFlags {
|
||||||
|
topTime: string;
|
||||||
|
isBlock: boolean;
|
||||||
|
isMsgDisturb: boolean;
|
||||||
|
isSpecialCareOpen: boolean;
|
||||||
|
isSpecialCareZone: boolean;
|
||||||
|
ringId: string;
|
||||||
|
isBlocked: boolean;
|
||||||
|
recommendImgFlag: number;
|
||||||
|
disableEmojiShortCuts: number;
|
||||||
|
qidianMasterFlag: number;
|
||||||
|
qidianCrewFlag: number;
|
||||||
|
qidianCrewFlag2: number;
|
||||||
|
isHideQQLevel: number;
|
||||||
|
isHidePrivilegeIcon: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
interface CommonExt {
|
||||||
|
constellation: number;
|
||||||
|
shengXiao: number;
|
||||||
|
kBloodType: number;
|
||||||
|
homeTown: string;
|
||||||
|
makeFriendCareer: number;
|
||||||
|
pos: string;
|
||||||
|
college: string;
|
||||||
|
country: string;
|
||||||
|
province: string;
|
||||||
|
city: string;
|
||||||
|
postCode: string;
|
||||||
|
address: string;
|
||||||
|
regTime: number;
|
||||||
|
interest: string;
|
||||||
|
labels: any[];
|
||||||
|
qqLevel: QQLevel;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Pic {
|
||||||
|
picId: string;
|
||||||
|
picTime: number;
|
||||||
|
picUrlMap: Record<string, string>;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface PhotoWall {
|
||||||
|
picList: Pic[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SimpleInfo {
|
||||||
|
uid?: string;
|
||||||
|
uin?: string;
|
||||||
|
coreInfo: CoreInfo;
|
||||||
|
baseInfo: BaseInfo;
|
||||||
|
status: UserStatus | null;
|
||||||
|
vasInfo: VasInfo | null;
|
||||||
|
relationFlags: RelationFlags | null;
|
||||||
|
otherFlags: any | null;
|
||||||
|
intimate: any | null;
|
||||||
|
}
|
||||||
|
export interface FriendV2 extends SimpleInfo {
|
||||||
|
categoryId?: number;
|
||||||
|
categroyName?: string;
|
||||||
|
}
|
||||||
|
export interface UserDetailInfoListenerArg {
|
||||||
|
uid: string;
|
||||||
|
uin: string;
|
||||||
|
simpleInfo: SimpleInfo;
|
||||||
|
commonExt: CommonExt;
|
||||||
|
photoWall: PhotoWall;
|
||||||
|
}
|
||||||
export interface ModifyProfileParams {
|
export interface ModifyProfileParams {
|
||||||
nick: string,
|
nick: string,
|
||||||
longNick: string,
|
longNick: string,
|
||||||
@@ -101,7 +275,18 @@ export interface Friend extends User { }
|
|||||||
export enum BizKey {
|
export enum BizKey {
|
||||||
KPRIVILEGEICON,
|
KPRIVILEGEICON,
|
||||||
KPHOTOWALL
|
KPHOTOWALL
|
||||||
}
|
}
|
||||||
|
export interface UserDetailInfoByUinV2 {
|
||||||
|
result: number,
|
||||||
|
errMsg: string,
|
||||||
|
detail: {
|
||||||
|
uid: string,
|
||||||
|
uin: string,
|
||||||
|
simpleInfo: SimpleInfo,
|
||||||
|
commonExt: CommonExt,
|
||||||
|
photoWall: null
|
||||||
|
}
|
||||||
|
}
|
||||||
export interface UserDetailInfoByUin {
|
export interface UserDetailInfoByUin {
|
||||||
result: number,
|
result: number,
|
||||||
errMsg: string,
|
errMsg: string,
|
||||||
|
42
src/core/src/external/appid.json
vendored
Normal file
42
src/core/src/external/appid.json
vendored
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
{
|
||||||
|
"3.1.2-13107": {
|
||||||
|
"appid": 537146866,
|
||||||
|
"qua": "V1_LNX_NQ_3.1.2-13107_RDM_B"
|
||||||
|
},
|
||||||
|
"3.2.10-25765": {
|
||||||
|
"appid": 537234773,
|
||||||
|
"qua": "V1_LNX_NQ_3.2.10_25765_GW_B"
|
||||||
|
},
|
||||||
|
"3.2.12-26702": {
|
||||||
|
"appid": 537237950,
|
||||||
|
"qua": "V1_LNX_NQ_3.2.12_26702_GW_B"
|
||||||
|
},
|
||||||
|
"9.9.11-24815": {
|
||||||
|
"appid": 537226656,
|
||||||
|
"qua": "V1_WIN_NQ_9.9.11_24815_GW_B"
|
||||||
|
},
|
||||||
|
"9.9.12-25493": {
|
||||||
|
"appid": 537231759,
|
||||||
|
"qua": "V1_WIN_NQ_9.9.12_25493_GW_B"
|
||||||
|
},
|
||||||
|
"9.9.12-25765": {
|
||||||
|
"appid": 537234702,
|
||||||
|
"qua": "V1_WIN_NQ_9.9.12_25765_GW_B"
|
||||||
|
},
|
||||||
|
"9.9.12-26299": {
|
||||||
|
"appid": 537234826,
|
||||||
|
"qua": "V1_WIN_NQ_9.9.12_26299_GW_B"
|
||||||
|
},
|
||||||
|
"9.9.12-26339": {
|
||||||
|
"appid": 537234826,
|
||||||
|
"qua": "V1_WIN_NQ_9.9.12_26339_GW_B"
|
||||||
|
},
|
||||||
|
"9.9.12-26466": {
|
||||||
|
"appid": 537234826,
|
||||||
|
"qua": "V1_WIN_NQ_9.9.12_26466_GW_B"
|
||||||
|
},
|
||||||
|
"9.9.15-26702": {
|
||||||
|
"appid": 537237765,
|
||||||
|
"qua": "V1_WIN_NQ_9.9.15_26702_GW_B"
|
||||||
|
}
|
||||||
|
}
|
@@ -3,6 +3,8 @@ import { BuddyCategoryType, FriendRequestNotify } from '@/core/entities';
|
|||||||
export type OnBuddyChangeParams = BuddyCategoryType[]
|
export type OnBuddyChangeParams = BuddyCategoryType[]
|
||||||
|
|
||||||
interface IBuddyListener {
|
interface IBuddyListener {
|
||||||
|
onBuddyListChangedV2(arg: unknown): void,//V2版本 还没兼容
|
||||||
|
|
||||||
onBuddyListChange(arg: OnBuddyChangeParams): void,
|
onBuddyListChange(arg: OnBuddyChangeParams): void,
|
||||||
|
|
||||||
onBuddyInfoChange(arg: unknown): void,
|
onBuddyInfoChange(arg: unknown): void,
|
||||||
@@ -44,6 +46,9 @@ export interface NodeIKernelBuddyListener extends IBuddyListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class BuddyListener implements IBuddyListener {
|
export class BuddyListener implements IBuddyListener {
|
||||||
|
onBuddyListChangedV2(arg: unknown): void {
|
||||||
|
//throw new Error('Method not implemented.');
|
||||||
|
}
|
||||||
onAddBuddyNeedVerify(arg: unknown) {
|
onAddBuddyNeedVerify(arg: unknown) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
import { Group, GroupMember, GroupNotify } from '@/core/entities';
|
import { Group, GroupListUpdateType, GroupMember, GroupNotify } from '@/core/entities';
|
||||||
|
|
||||||
interface IGroupListener {
|
interface IGroupListener {
|
||||||
onGroupListUpdate(updateType: number, groupList: Group[]): void;
|
onGroupListUpdate(updateType: GroupListUpdateType, groupList: Group[]): void;
|
||||||
|
|
||||||
onGroupExtListUpdate(...args: unknown[]): void;
|
onGroupExtListUpdate(...args: unknown[]): void;
|
||||||
|
|
||||||
@@ -96,7 +96,7 @@ export class GroupListener implements IGroupListener {
|
|||||||
onGroupFirstBulletinNotify(...args: unknown[]) {
|
onGroupFirstBulletinNotify(...args: unknown[]) {
|
||||||
}
|
}
|
||||||
|
|
||||||
onGroupListUpdate(updateType: number, groupList: Group[]) {
|
onGroupListUpdate(updateType: GroupListUpdateType, groupList: Group[]) {
|
||||||
}
|
}
|
||||||
|
|
||||||
onGroupNotifiesUpdated(dboubt: boolean, notifies: GroupNotify[]) {
|
onGroupNotifiesUpdated(dboubt: boolean, notifies: GroupNotify[]) {
|
||||||
@@ -202,7 +202,7 @@ export class DebugGroupListener implements IGroupListener {
|
|||||||
console.log('onGroupNotifiesUnreadCountUpdated:', ...args);
|
console.log('onGroupNotifiesUnreadCountUpdated:', ...args);
|
||||||
}
|
}
|
||||||
|
|
||||||
onGroupSingleScreenNotifies(doubt: boolean, seq: string, notifies: GroupNotify[]){
|
onGroupSingleScreenNotifies(doubt: boolean, seq: string, notifies: GroupNotify[]) {
|
||||||
console.log('onGroupSingleScreenNotifies:');
|
console.log('onGroupSingleScreenNotifies:');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -40,14 +40,14 @@ export interface onGroupFileInfoUpdateParamType {
|
|||||||
// fromNick: '拾xxxx,
|
// fromNick: '拾xxxx,
|
||||||
// sig: '0x'
|
// sig: '0x'
|
||||||
// }
|
// }
|
||||||
export interface TempOnRecvParams{
|
export interface TempOnRecvParams {
|
||||||
sessionType: number,//1
|
sessionType: number,//1
|
||||||
chatType: ChatType,//100
|
chatType: ChatType,//100
|
||||||
peerUid: string,//uid
|
peerUid: string,//uid
|
||||||
groupCode: string,//gc
|
groupCode: string,//gc
|
||||||
fromNick: string,//gc name
|
fromNick: string,//gc name
|
||||||
sig: string,
|
sig: string,
|
||||||
|
|
||||||
}
|
}
|
||||||
export interface IKernelMsgListener {
|
export interface IKernelMsgListener {
|
||||||
onAddSendMsg(msgRecord: RawMessage): void;
|
onAddSendMsg(msgRecord: RawMessage): void;
|
||||||
@@ -158,7 +158,41 @@ export interface IKernelMsgListener {
|
|||||||
|
|
||||||
onRichMediaUploadComplete(fileTransNotifyInfo: unknown): void;
|
onRichMediaUploadComplete(fileTransNotifyInfo: unknown): void;
|
||||||
|
|
||||||
onSearchGroupFileInfoUpdate(searchGroupFileResult: unknown): void;
|
onSearchGroupFileInfoUpdate(searchGroupFileResult:
|
||||||
|
{
|
||||||
|
result: {
|
||||||
|
retCode: number,
|
||||||
|
retMsg: string,
|
||||||
|
clientWording: string
|
||||||
|
},
|
||||||
|
syncCookie: string,
|
||||||
|
totalMatchCount: number,
|
||||||
|
ownerMatchCount: number,
|
||||||
|
isEnd: boolean,
|
||||||
|
reqId: number,
|
||||||
|
item: Array<{
|
||||||
|
groupCode: string,
|
||||||
|
groupName: string,
|
||||||
|
uploaderUin: string,
|
||||||
|
uploaderName: string,
|
||||||
|
matchUin: string,
|
||||||
|
matchWords: Array<unknown>,
|
||||||
|
fileNameHits: Array<{
|
||||||
|
start: number,
|
||||||
|
end: number
|
||||||
|
}>,
|
||||||
|
fileModelId: string,
|
||||||
|
fileId: string,
|
||||||
|
fileName: string,
|
||||||
|
fileSize: string,
|
||||||
|
busId: number,
|
||||||
|
uploadTime: number,
|
||||||
|
modifyTime: number,
|
||||||
|
deadTime: number,
|
||||||
|
downloadTimes: number,
|
||||||
|
localPath: string
|
||||||
|
}>
|
||||||
|
}): void;
|
||||||
|
|
||||||
onSendMsgError(j2: unknown, contact: unknown, i2: unknown, str: unknown): void;
|
onSendMsgError(j2: unknown, contact: unknown, i2: unknown, str: unknown): void;
|
||||||
|
|
||||||
@@ -192,7 +226,7 @@ export interface IKernelMsgListener {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface NodeIKernelMsgListener {
|
export interface NodeIKernelMsgListener extends IKernelMsgListener {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-misused-new
|
// eslint-disable-next-line @typescript-eslint/no-misused-new
|
||||||
new(listener: IKernelMsgListener): NodeIKernelMsgListener;
|
new(listener: IKernelMsgListener): NodeIKernelMsgListener;
|
||||||
}
|
}
|
||||||
|
@@ -1,8 +1,8 @@
|
|||||||
import { User } from '@/core/entities';
|
import { User, UserDetailInfoListenerArg } from '@/core/entities';
|
||||||
|
|
||||||
interface IProfileListener {
|
interface IProfileListener {
|
||||||
onProfileSimpleChanged(...args: unknown[]): void;
|
onProfileSimpleChanged(...args: unknown[]): void;
|
||||||
|
onUserDetailInfoChanged(arg: UserDetailInfoListenerArg): void;
|
||||||
onProfileDetailInfoChanged(profile: User): void;
|
onProfileDetailInfoChanged(profile: User): void;
|
||||||
|
|
||||||
onStatusUpdate(...args: unknown[]): void;
|
onStatusUpdate(...args: unknown[]): void;
|
||||||
@@ -18,6 +18,9 @@ export interface NodeIKernelProfileListener extends IProfileListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class ProfileListener implements IProfileListener {
|
export class ProfileListener implements IProfileListener {
|
||||||
|
onUserDetailInfoChanged(arg: UserDetailInfoListenerArg): void {
|
||||||
|
|
||||||
|
}
|
||||||
onProfileSimpleChanged(...args: unknown[]) {
|
onProfileSimpleChanged(...args: unknown[]) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
44
src/core/src/listeners/NodeIKernelRecentContactListener.ts
Normal file
44
src/core/src/listeners/NodeIKernelRecentContactListener.ts
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
interface IKernelRecentContactListener {
|
||||||
|
onDeletedContactsNotify(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
onRecentContactNotification(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
onMsgUnreadCountUpdate(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
onGuildDisplayRecentContactListChanged(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
onRecentContactListChanged(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
onRecentContactListChangedVer2(...args: unknown[]): unknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface NodeIKernelRecentContactListener extends IKernelRecentContactListener {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-misused-new
|
||||||
|
new(listener: IKernelRecentContactListener): NodeIKernelRecentContactListener;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class KernelRecentContactListener implements IKernelRecentContactListener {
|
||||||
|
onDeletedContactsNotify(...args: unknown[]) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
onRecentContactNotification(...args: unknown[]) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
onMsgUnreadCountUpdate(...args: unknown[]) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
onGuildDisplayRecentContactListChanged(...args: unknown[]) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
onRecentContactListChanged(...args: unknown[]) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
onRecentContactListChangedVer2(...args: unknown[]) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@@ -1,19 +1,44 @@
|
|||||||
import { Friend } from '@/core/entities';
|
import { Friend } from '@/core/entities';
|
||||||
import { GeneralCallResult } from '@/core/services/common';
|
import { GeneralCallResult } from '@/core/services/common';
|
||||||
import { NodeIKernelBuddyListener } from '@/core/listeners';
|
import { NodeIKernelBuddyListener } from '@/core/listeners';
|
||||||
|
export enum BuddyListReqType {
|
||||||
|
KNOMAL,
|
||||||
|
KLETTER
|
||||||
|
}
|
||||||
export interface NodeIKernelBuddyService {
|
export interface NodeIKernelBuddyService {
|
||||||
// 以下为自行添加的,wrapper.node中并没有这些方法,目的是简化调用
|
// 26702 以上
|
||||||
friends: Friend[];
|
getBuddyListV2(callFrom: string, reqType: BuddyListReqType): Promise<GeneralCallResult & {
|
||||||
|
data: Array<{
|
||||||
getFriend(uidOrUin: string): Promise<Friend>;
|
categoryId: number,
|
||||||
|
categorySortId: number,
|
||||||
|
categroyName: string,
|
||||||
|
categroyMbCount: number,
|
||||||
|
onlineCount: number,
|
||||||
|
buddyUids: Array<string>
|
||||||
|
}>
|
||||||
|
}>;
|
||||||
|
//26702 以上
|
||||||
|
getBuddyListFromCache(callFrom: string): Promise<Array<
|
||||||
|
{
|
||||||
|
categoryId: number,//9999应该跳过 那是兜底数据吧
|
||||||
|
categorySortId: number,//排序方式
|
||||||
|
categroyName: string,//分类名
|
||||||
|
categroyMbCount: number,//不懂
|
||||||
|
onlineCount: number,//在线数目
|
||||||
|
buddyUids: Array<string>//Uids
|
||||||
|
}>>;
|
||||||
// 以下为原生方法
|
// 以下为原生方法
|
||||||
addKernelBuddyListener(listener: NodeIKernelBuddyListener): number;
|
addKernelBuddyListener(listener: NodeIKernelBuddyListener): number;
|
||||||
|
|
||||||
|
getAllBuddyCount(): number;
|
||||||
|
|
||||||
removeKernelBuddyListener(listener: unknown): void;
|
removeKernelBuddyListener(listener: unknown): void;
|
||||||
|
|
||||||
getBuddyList(bool: boolean): Promise<GeneralCallResult>;
|
/**
|
||||||
|
* @deprecated
|
||||||
|
* @param nocache 使用缓存
|
||||||
|
*/
|
||||||
|
getBuddyList(nocache: boolean): Promise<GeneralCallResult>;
|
||||||
|
|
||||||
getBuddyNick(uid: number): string;
|
getBuddyNick(uid: number): string;
|
||||||
|
|
||||||
|
3
src/core/src/services/NodeIKernelECDHService.ts
Normal file
3
src/core/src/services/NodeIKernelECDHService.ts
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
export interface NodeIKernelECDHService{
|
||||||
|
|
||||||
|
}
|
@@ -8,7 +8,90 @@ import {
|
|||||||
} from '@/core/entities';
|
} from '@/core/entities';
|
||||||
import { GeneralCallResult } from '@/core/services/common';
|
import { GeneralCallResult } from '@/core/services/common';
|
||||||
|
|
||||||
|
//高版本的接口不应该随意使用 使用应该严格进行pr审核 同时部分ipc中未出现的接口不要过于依赖 应该做好数据兜底
|
||||||
|
|
||||||
export interface NodeIKernelGroupService {
|
export interface NodeIKernelGroupService {
|
||||||
|
getMemberCommonInfo(Req: {
|
||||||
|
groupCode: string,
|
||||||
|
startUin: string,
|
||||||
|
identifyFlag: string,
|
||||||
|
uinList: string[],
|
||||||
|
memberCommonFilter: {
|
||||||
|
memberUin: number,
|
||||||
|
uinFlag: number,
|
||||||
|
uinFlagExt: number,
|
||||||
|
uinMobileFlag: number,
|
||||||
|
shutUpTime: number,
|
||||||
|
privilege: number,
|
||||||
|
},
|
||||||
|
memberNum: number,
|
||||||
|
filterMethod: string,
|
||||||
|
onlineFlag: string,
|
||||||
|
realSpecialTitleFlag: number
|
||||||
|
}): Promise<unknown>;
|
||||||
|
//26702
|
||||||
|
getGroupMemberLevelInfo(groupCode: string): Promise<unknown>;
|
||||||
|
//26702
|
||||||
|
getGroupHonorList(groupCodes: Array<string>): unknown;
|
||||||
|
|
||||||
|
getUinByUids(uins: string[]): Promise<{
|
||||||
|
errCode: number,
|
||||||
|
errMsg: string,
|
||||||
|
uins: Map<string, string>
|
||||||
|
}>;
|
||||||
|
|
||||||
|
getUidByUins(uins: string[]): Promise<{
|
||||||
|
errCode: number,
|
||||||
|
errMsg: string,
|
||||||
|
uids: Map<string, string>
|
||||||
|
}>;
|
||||||
|
//26702(其实更早 但是我不知道)
|
||||||
|
checkGroupMemberCache(arrayList: Array<string>): Promise<unknown>;
|
||||||
|
|
||||||
|
//26702(其实更早 但是我不知道)
|
||||||
|
getGroupLatestEssenceList(groupCode: string): Promise<unknown>;
|
||||||
|
|
||||||
|
//26702(其实更早 但是我不知道)
|
||||||
|
shareDigest(Req: {
|
||||||
|
appId: string,
|
||||||
|
appType: number,
|
||||||
|
msgStyle: number,
|
||||||
|
recvUin: string,
|
||||||
|
sendType: number,
|
||||||
|
clientInfo: {
|
||||||
|
platform: number
|
||||||
|
},
|
||||||
|
richMsg: {
|
||||||
|
usingArk: boolean,
|
||||||
|
title: string,
|
||||||
|
summary: string,
|
||||||
|
url: string,
|
||||||
|
pictureUrl: string,
|
||||||
|
brief: string
|
||||||
|
}
|
||||||
|
}): Promise<unknown>;
|
||||||
|
//26702(其实更早 但是我不知道)
|
||||||
|
isEssenceMsg(Req: { groupCode: string, msgRandom: number, msgSeq: number }): Promise<unknown>;
|
||||||
|
//26702(其实更早 但是我不知道)
|
||||||
|
queryCachedEssenceMsg(Req: { groupCode: string, msgRandom: number, msgSeq: number }): Promise<unknown>;
|
||||||
|
//26702(其实更早 但是我不知道)
|
||||||
|
fetchGroupEssenceList(Req: { groupCode: string, pageStart: number, pageLimit: number }, Arg: unknown): Promise<unknown>;
|
||||||
|
//26702
|
||||||
|
getAllMemberList(groupCode: string, forceFetch: boolean): Promise<{
|
||||||
|
errCode: number,
|
||||||
|
errMsg: string,
|
||||||
|
result: {
|
||||||
|
ids: Array<{
|
||||||
|
uid: string,
|
||||||
|
index: number//0
|
||||||
|
}>,
|
||||||
|
infos: {},
|
||||||
|
finish: true,
|
||||||
|
hasRobot: false
|
||||||
|
}
|
||||||
|
}>;
|
||||||
|
|
||||||
|
setHeader(uid: string, path: string): unknown;
|
||||||
|
|
||||||
addKernelGroupListener(listener: NodeIKernelGroupListener): number;
|
addKernelGroupListener(listener: NodeIKernelGroupListener): number;
|
||||||
|
|
||||||
@@ -16,7 +99,7 @@ export interface NodeIKernelGroupService {
|
|||||||
|
|
||||||
createMemberListScene(groupCode: string, scene: string): string;
|
createMemberListScene(groupCode: string, scene: string): string;
|
||||||
|
|
||||||
destroyMemberListScene(): void;
|
destroyMemberListScene(SceneId:string): void;
|
||||||
//About Arg (a) name: lastId 根据手Q来看为object {index:?(number),uid:string}
|
//About Arg (a) name: lastId 根据手Q来看为object {index:?(number),uid:string}
|
||||||
getNextMemberList(sceneId: string, a: undefined, num: number): Promise<{
|
getNextMemberList(sceneId: string, a: undefined, num: number): Promise<{
|
||||||
errCode: number, errMsg: string,
|
errCode: number, errMsg: string,
|
||||||
@@ -27,9 +110,9 @@ export interface NodeIKernelGroupService {
|
|||||||
|
|
||||||
monitorMemberList(): unknown;
|
monitorMemberList(): unknown;
|
||||||
|
|
||||||
searchMember(uid: string): unknown;
|
searchMember(sceneId: string, keywords: string[]): unknown;
|
||||||
|
|
||||||
getMemberInfo(uid: string): unknown;
|
getMemberInfo(group_id: string, uids: string[], forceFetch: boolean): Promise<GeneralCallResult>;
|
||||||
//getMemberInfo [ '56729xxxx', [ 'u_4Nj08cwW5Hxxxxx' ], true ]
|
//getMemberInfo [ '56729xxxx', [ 'u_4Nj08cwW5Hxxxxx' ], true ]
|
||||||
|
|
||||||
kickMember(groupCode: string, memberUids: string[], refuseForever: boolean, kickReason: string): Promise<void>;
|
kickMember(groupCode: string, memberUids: string[], refuseForever: boolean, kickReason: string): Promise<void>;
|
||||||
@@ -38,7 +121,7 @@ export interface NodeIKernelGroupService {
|
|||||||
|
|
||||||
modifyMemberCardName(groupCode: string, uid: string, cardName: string): void;
|
modifyMemberCardName(groupCode: string, uid: string, cardName: string): void;
|
||||||
|
|
||||||
getTransferableMemberInfo(uid: string): unknown;
|
getTransferableMemberInfo(groupCode: string): unknown;//获取整个群的
|
||||||
|
|
||||||
transferGroup(uid: string): void;
|
transferGroup(uid: string): void;
|
||||||
|
|
||||||
@@ -149,5 +232,18 @@ export interface NodeIKernelGroupService {
|
|||||||
|
|
||||||
modifyGroupExtInfo(groupCode: string, arg: unknown): void;
|
modifyGroupExtInfo(groupCode: string, arg: unknown): void;
|
||||||
|
|
||||||
|
//需要提前判断是否存在 高版本新增
|
||||||
|
addGroupEssence(param: {
|
||||||
|
groupCode: string
|
||||||
|
msgRandom: number,
|
||||||
|
msgSeq: number
|
||||||
|
}): Promise<unknown>;
|
||||||
|
//需要提前判断是否存在 高版本新增
|
||||||
|
removeGroupEssence(param: {
|
||||||
|
groupCode: string
|
||||||
|
msgRandom: number,
|
||||||
|
msgSeq: number
|
||||||
|
}): Promise<unknown>;
|
||||||
|
|
||||||
isNull(): boolean;
|
isNull(): boolean;
|
||||||
}
|
}
|
||||||
|
3
src/core/src/services/NodeIKernelMSFService.ts
Normal file
3
src/core/src/services/NodeIKernelMSFService.ts
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
export interface NodeIKernelMSFService {
|
||||||
|
getServerTime(): string;
|
||||||
|
}
|
@@ -1,11 +1,24 @@
|
|||||||
import { ChatType, ElementType, Peer, RawMessage, SendMessageElement } from '@/core/entities';
|
import { ElementType, MessageElement, Peer, RawMessage, SendMessageElement } from '@/core/entities';
|
||||||
import { NodeIKernelMsgListener } from '@/core/listeners/NodeIKernelMsgListener';
|
import { NodeIKernelMsgListener } from '@/core/listeners/NodeIKernelMsgListener';
|
||||||
import { GeneralCallResult } from '@/core/services/common';
|
import { GeneralCallResult } from '@/core/services/common';
|
||||||
|
|
||||||
|
export interface QueryMsgsParams {
|
||||||
|
chatInfo: Peer,
|
||||||
|
filterMsgType: [],
|
||||||
|
filterSendersUid: string[],
|
||||||
|
filterMsgFromTime: string,
|
||||||
|
filterMsgToTime: string,
|
||||||
|
pageLimit: number,
|
||||||
|
isReverseOrder: boolean,
|
||||||
|
isIncludeCurrent: boolean
|
||||||
|
}
|
||||||
export interface NodeIKernelMsgService {
|
export interface NodeIKernelMsgService {
|
||||||
|
|
||||||
|
generateMsgUniqueId(chatType: number, time: string): string;
|
||||||
|
|
||||||
addKernelMsgListener(nodeIKernelMsgListener: NodeIKernelMsgListener): number;
|
addKernelMsgListener(nodeIKernelMsgListener: NodeIKernelMsgListener): number;
|
||||||
|
|
||||||
sendMsg(msgId: string, peer: Peer, msgElements: SendMessageElement[], map: Map<any, any>): Promise<unknown>;
|
sendMsg(msgId: string, peer: Peer, msgElements: SendMessageElement[], map: Map<any, any>): Promise<GeneralCallResult>;
|
||||||
|
|
||||||
recallMsg(peer: Peer, msgIds: string[]): Promise<GeneralCallResult>;
|
recallMsg(peer: Peer, msgIds: string[]): Promise<GeneralCallResult>;
|
||||||
|
|
||||||
@@ -21,7 +34,7 @@ export interface NodeIKernelMsgService {
|
|||||||
|
|
||||||
getAutoReplyTextList(...args: unknown[]): unknown;
|
getAutoReplyTextList(...args: unknown[]): unknown;
|
||||||
|
|
||||||
getOnLineDev(): Promise<any>;
|
getOnLineDev(): void;
|
||||||
|
|
||||||
kickOffLine(DevInfo: Object): unknown;
|
kickOffLine(DevInfo: Object): unknown;
|
||||||
|
|
||||||
@@ -73,7 +86,7 @@ export interface NodeIKernelMsgService {
|
|||||||
|
|
||||||
cancelSendMsg(...args: unknown[]): unknown;
|
cancelSendMsg(...args: unknown[]): unknown;
|
||||||
|
|
||||||
switchToOfflineSendMsg(...args: unknown[]): unknown;
|
switchToOfflineSendMsg(peer: Peer, MsgId: string): unknown;
|
||||||
|
|
||||||
reqToOfflineSendMsg(...args: unknown[]): unknown;
|
reqToOfflineSendMsg(...args: unknown[]): unknown;
|
||||||
|
|
||||||
@@ -115,9 +128,9 @@ export interface NodeIKernelMsgService {
|
|||||||
|
|
||||||
addLocalTofuRecordMsg(...args: unknown[]): unknown;
|
addLocalTofuRecordMsg(...args: unknown[]): unknown;
|
||||||
|
|
||||||
addLocalRecordMsg(...args: unknown[]): unknown;
|
addLocalRecordMsg(Peer: Peer, msgId: string, ele: MessageElement, attr: Array<any> | number, front: boolean): Promise<unknown>;
|
||||||
|
|
||||||
deleteMsg(...args: unknown[]): unknown;
|
deleteMsg(Peer: Peer, msgIds: Array<string>): Promise<any>;
|
||||||
|
|
||||||
updateElementExtBufForUI(...args: unknown[]): unknown;
|
updateElementExtBufForUI(...args: unknown[]): unknown;
|
||||||
|
|
||||||
@@ -141,7 +154,7 @@ export interface NodeIKernelMsgService {
|
|||||||
|
|
||||||
getLastMessageList(peer: Peer[]): Promise<unknown>;
|
getLastMessageList(peer: Peer[]): Promise<unknown>;
|
||||||
|
|
||||||
getAioFirstViewLatestMsgs(...args: unknown[]): unknown;
|
getAioFirstViewLatestMsgs(peer: Peer, num: number): unknown;
|
||||||
|
|
||||||
getMsgs(peer: Peer, msgId: string, count: unknown, queryOrder: boolean): Promise<unknown>;
|
getMsgs(peer: Peer, msgId: string, count: unknown, queryOrder: boolean): Promise<unknown>;
|
||||||
|
|
||||||
@@ -154,7 +167,7 @@ export interface NodeIKernelMsgService {
|
|||||||
// this.$clientSeq = j3;
|
// this.$clientSeq = j3;
|
||||||
// this.$cnt = i2;
|
// this.$cnt = i2;
|
||||||
|
|
||||||
getMsgsWithMsgTimeAndClientSeqForC2C(...args: unknown[]): Promise<unknown>;
|
getMsgsWithMsgTimeAndClientSeqForC2C(...args: unknown[]): Promise<GeneralCallResult & { msgList: RawMessage[] }>;
|
||||||
|
|
||||||
getMsgsWithStatus(params: {
|
getMsgsWithStatus(params: {
|
||||||
peer: Peer
|
peer: Peer
|
||||||
@@ -164,9 +177,9 @@ export interface NodeIKernelMsgService {
|
|||||||
queryOrder: boolean
|
queryOrder: boolean
|
||||||
isIncludeSelf: boolean
|
isIncludeSelf: boolean
|
||||||
appid: unknown
|
appid: unknown
|
||||||
}): Promise<unknown>;
|
}): Promise<GeneralCallResult & { msgList: RawMessage[] }>;
|
||||||
|
|
||||||
getMsgsBySeqRange(peer: Peer, startSeq: string, endSeq: string): Promise<unknown>;
|
getMsgsBySeqRange(peer: Peer, startSeq: string, endSeq: string): Promise<GeneralCallResult & { msgList: RawMessage[] }>;
|
||||||
|
|
||||||
getMsgsBySeqAndCount(peer: Peer, seq: string, count: number, desc: boolean, unknownArg: boolean): Promise<GeneralCallResult & { msgList: RawMessage[] }>;
|
getMsgsBySeqAndCount(peer: Peer, seq: string, count: number, desc: boolean, unknownArg: boolean): Promise<GeneralCallResult & { msgList: RawMessage[] }>;
|
||||||
|
|
||||||
@@ -174,39 +187,48 @@ export interface NodeIKernelMsgService {
|
|||||||
|
|
||||||
getRecallMsgsByMsgId(peer: Peer, MsgId: string[]): Promise<unknown>;
|
getRecallMsgsByMsgId(peer: Peer, MsgId: string[]): Promise<unknown>;
|
||||||
|
|
||||||
getMsgsBySeqList(peer: Peer, seqList: string[]): Promise<unknown>;
|
getMsgsBySeqList(peer: Peer, seqList: string[]): Promise<GeneralCallResult & { msgList: RawMessage[] }>;
|
||||||
|
|
||||||
getSingleMsg(Peer: Peer, msgSeq: string): unknown;
|
getSingleMsg(Peer: Peer, msgSeq: string): Promise<GeneralCallResult & { msgList: RawMessage[] }>;
|
||||||
|
|
||||||
getSourceOfReplyMsg(...args: unknown[]): unknown;
|
getSourceOfReplyMsg(peer: Peer, MsgId: string, SourceSeq: string): unknown;
|
||||||
|
|
||||||
getSourceOfReplyMsgV2(...args: unknown[]): unknown;
|
getSourceOfReplyMsgV2(peer: Peer, RootMsgId: string, ReplyMsgId: string): unknown;
|
||||||
|
|
||||||
getMsgByClientSeqAndTime(...args: unknown[]): unknown;
|
getMsgByClientSeqAndTime(peer: Peer, clientSeq: string, time: string): unknown;
|
||||||
|
|
||||||
getSourceOfReplyMsgByClientSeqAndTime(...args: unknown[]): unknown;
|
getSourceOfReplyMsgByClientSeqAndTime(peer: Peer, clientSeq: string, time: string): unknown;
|
||||||
|
//cnt clientSeq?并不是吧
|
||||||
|
getMsgsByTypeFilter(peer: Peer, msgId: string, cnt: unknown, queryOrder: boolean, typeFilter: { type: number, subtype: Array<number> }): unknown;
|
||||||
|
|
||||||
getMsgsByTypeFilter(peer: Peer, msgId: string, cnt: unknown, queryOrder: boolean, typeFilters: unknown): unknown;
|
getMsgsByTypeFilters(peer: Peer, msgId: string, cnt: unknown, queryOrder: boolean, typeFilters: Array<{ type: number, subtype: Array<number> }>): unknown;
|
||||||
|
|
||||||
getMsgsByTypeFilters(...args: unknown[]): unknown;
|
|
||||||
|
|
||||||
getMsgWithAbstractByFilterParam(...args: unknown[]): unknown;
|
getMsgWithAbstractByFilterParam(...args: unknown[]): unknown;
|
||||||
|
|
||||||
queryMsgsWithFilter(...args: unknown[]): unknown;
|
queryMsgsWithFilter(...args: unknown[]): unknown;
|
||||||
|
|
||||||
queryMsgsWithFilterVer2(MsgId: string, MsgTime: string, param: {
|
/**
|
||||||
chatInfo: {
|
* @deprecated 该函数已被标记为废弃,请使用新的替代方法。
|
||||||
chatType: number,
|
* 使用过滤条件查询消息列表的版本2接口。
|
||||||
peerUid: string
|
*
|
||||||
},
|
* 该函数通过一系列过滤条件来查询特定聊天中的消息列表。这些条件包括消息类型、发送者、时间范围等。
|
||||||
filterMsgType: [],
|
* 函数返回一个Promise,解析为查询结果的未知类型对象。
|
||||||
filterSendersUid: [],
|
*
|
||||||
filterMsgFromTime: string,
|
* @param MsgId 消息ID,用于特定消息的查询。
|
||||||
filterMsgToTime: string,
|
* @param MsgTime 消息时间,用于指定消息的时间范围。
|
||||||
pageLimit: number,
|
* @param param 查询参数对象,包含详细的过滤条件和分页信息。
|
||||||
isReverseOrder: boolean,
|
* @param param.chatInfo 聊天信息,包括聊天类型和对方用户ID。
|
||||||
isIncludeCurrent: boolean
|
* @param param.filterMsgType 需要过滤的消息类型数组,留空表示不过滤。
|
||||||
}): Promise<unknown>;
|
* @param param.filterSendersUid 需要过滤的发送者用户ID数组。
|
||||||
|
* @param param.filterMsgFromTime 查询消息的起始时间。
|
||||||
|
* @param param.filterMsgToTime 查询消息的结束时间。
|
||||||
|
* @param param.pageLimit 每页的消息数量限制。
|
||||||
|
* @param param.isReverseOrder 是否按时间顺序倒序返回消息。
|
||||||
|
* @param param.isIncludeCurrent 是否包含当前页码。
|
||||||
|
* @returns 返回一个Promise,解析为查询结果的未知类型对象。
|
||||||
|
*/
|
||||||
|
queryMsgsWithFilterVer2(MsgId: string, MsgTime: string, param: QueryMsgsParams): Promise<unknown>;
|
||||||
|
|
||||||
// this.chatType = i2;
|
// this.chatType = i2;
|
||||||
// this.peerUid = str;
|
// this.peerUid = str;
|
||||||
|
|
||||||
@@ -222,46 +244,23 @@ export interface NodeIKernelMsgService {
|
|||||||
// this.isReverseOrder = z;
|
// this.isReverseOrder = z;
|
||||||
// this.isIncludeCurrent = z2;
|
// this.isIncludeCurrent = z2;
|
||||||
//queryMsgsWithFilterEx(0L, 0L, 0L, new QueryMsgsParams(new ChatInfo(2, str), new ArrayList(), new ArrayList(), 0L, 0L, 250, false, true))
|
//queryMsgsWithFilterEx(0L, 0L, 0L, new QueryMsgsParams(new ChatInfo(2, str), new ArrayList(), new ArrayList(), 0L, 0L, 250, false, true))
|
||||||
queryMsgsWithFilterEx(msgId: string, msgTime: string, megSeq: string, param: {
|
queryMsgsWithFilterEx(msgId: string, msgTime: string, megSeq: string, param: QueryMsgsParams): Promise<GeneralCallResult & {
|
||||||
chatInfo: {
|
msgList: RawMessage[]
|
||||||
chatType: number,
|
}>;
|
||||||
peerUid: string
|
|
||||||
},
|
|
||||||
filterMsgType: [],
|
|
||||||
filterSendersUid: [],
|
|
||||||
filterMsgFromTime: string,
|
|
||||||
filterMsgToTime: string,
|
|
||||||
pageLimit: number,
|
|
||||||
isReverseOrder: boolean,
|
|
||||||
isIncludeCurrent: boolean
|
|
||||||
|
|
||||||
}): Promise<unknown>;
|
|
||||||
//queryMsgsWithFilterEx(this.$msgId, this.$msgTime, this.$msgSeq, this.$param)
|
//queryMsgsWithFilterEx(this.$msgId, this.$msgTime, this.$msgSeq, this.$param)
|
||||||
queryFileMsgsDesktop(...args: unknown[]): unknown;
|
queryFileMsgsDesktop(...args: unknown[]): unknown;
|
||||||
|
|
||||||
setMsgRichInfoFlag(...args: unknown[]): unknown;
|
setMsgRichInfoFlag(...args: unknown[]): unknown;
|
||||||
|
|
||||||
queryPicOrVideoMsgs(msgId: string, msgTime: string, megSeq: string, param: {
|
queryPicOrVideoMsgs(msgId: string, msgTime: string, megSeq: string, param: QueryMsgsParams): Promise<unknown>;
|
||||||
chatInfo: {
|
|
||||||
chatType: number,
|
|
||||||
peerUid: string
|
|
||||||
},
|
|
||||||
filterMsgType: [],
|
|
||||||
filterSendersUid: [],
|
|
||||||
filterMsgFromTime: string,
|
|
||||||
filterMsgToTime: string,
|
|
||||||
pageLimit: number,
|
|
||||||
isReverseOrder: boolean,
|
|
||||||
isIncludeCurrent: boolean
|
|
||||||
}):Promise<unknown>;
|
|
||||||
|
|
||||||
queryPicOrVideoMsgsDesktop(...args: unknown[]): unknown;
|
queryPicOrVideoMsgsDesktop(...args: unknown[]): unknown;
|
||||||
|
|
||||||
queryEmoticonMsgs(...args: unknown[]): unknown;
|
queryEmoticonMsgs(msgId: string, msgTime: string, msgSeq: string, Params: QueryMsgsParams): Promise<unknown>;
|
||||||
|
|
||||||
queryTroopEmoticonMsgs(...args: unknown[]): unknown;
|
queryTroopEmoticonMsgs(msgId: string, msgTime: string, msgSeq: string, Params: QueryMsgsParams): Promise<unknown>;
|
||||||
|
|
||||||
queryMsgsAndAbstractsWithFilter(...args: unknown[]): unknown;
|
queryMsgsAndAbstractsWithFilter(msgId: string, msgTime: string, megSeq: string, param: QueryMsgsParams): unknown;
|
||||||
|
|
||||||
setFocusOnGuild(...args: unknown[]): unknown;
|
setFocusOnGuild(...args: unknown[]): unknown;
|
||||||
|
|
||||||
@@ -299,7 +298,7 @@ export interface NodeIKernelMsgService {
|
|||||||
|
|
||||||
setMsgRead(peer: Peer): Promise<GeneralCallResult>;
|
setMsgRead(peer: Peer): Promise<GeneralCallResult>;
|
||||||
|
|
||||||
setAllC2CAndGroupMsgRead(...args: unknown[]): unknown;
|
setAllC2CAndGroupMsgRead(): Promise<unknown>;
|
||||||
|
|
||||||
setGuildMsgRead(...args: unknown[]): unknown;
|
setGuildMsgRead(...args: unknown[]): unknown;
|
||||||
|
|
||||||
@@ -327,7 +326,7 @@ export interface NodeIKernelMsgService {
|
|||||||
|
|
||||||
setGuildTabUserFlag(...args: unknown[]): unknown;
|
setGuildTabUserFlag(...args: unknown[]): unknown;
|
||||||
|
|
||||||
setBuildMode(...args: unknown[]): unknown;
|
setBuildMode(flag: number/*0 1 3*/): unknown;
|
||||||
|
|
||||||
setConfigurationServiceData(...args: unknown[]): unknown;
|
setConfigurationServiceData(...args: unknown[]): unknown;
|
||||||
|
|
||||||
@@ -355,12 +354,39 @@ export interface NodeIKernelMsgService {
|
|||||||
getFileThumbSavePathForSend(...args: unknown[]): unknown;
|
getFileThumbSavePathForSend(...args: unknown[]): unknown;
|
||||||
|
|
||||||
getFileThumbSavePath(...args: unknown[]): unknown;
|
getFileThumbSavePath(...args: unknown[]): unknown;
|
||||||
|
//猜测居多
|
||||||
translatePtt2Text(...args: unknown[]): unknown;
|
translatePtt2Text(MsgId: string, Peer: {}, MsgElement: {}): unknown;
|
||||||
|
|
||||||
setPttPlayedState(...args: unknown[]): unknown;
|
setPttPlayedState(...args: unknown[]): unknown;
|
||||||
|
// NodeIQQNTWrapperSession fetchFavEmojiList [
|
||||||
fetchFavEmojiList(...args: unknown[]): unknown;
|
// "",
|
||||||
|
// 48,
|
||||||
|
// true,
|
||||||
|
// true
|
||||||
|
// ]
|
||||||
|
fetchFavEmojiList(str: string, num: number, uk1: boolean, uk2: boolean): Promise<GeneralCallResult & {
|
||||||
|
emojiInfoList: Array<{
|
||||||
|
uin: string,
|
||||||
|
emoId: number,
|
||||||
|
emoPath: string,
|
||||||
|
isExist: boolean,
|
||||||
|
resId: string,
|
||||||
|
url: string,
|
||||||
|
md5: string,
|
||||||
|
emoOriginalPath: string,
|
||||||
|
thumbPath: string,
|
||||||
|
RomaingType: string,
|
||||||
|
isAPNG: false,
|
||||||
|
isMarkFace: false,
|
||||||
|
eId: string,
|
||||||
|
epId: string,
|
||||||
|
ocrWord: string,
|
||||||
|
modifyWord: string,
|
||||||
|
exposeNum: number,
|
||||||
|
clickNum: number,
|
||||||
|
desc: string
|
||||||
|
}>
|
||||||
|
}>;
|
||||||
|
|
||||||
addFavEmoji(...args: unknown[]): unknown;
|
addFavEmoji(...args: unknown[]): unknown;
|
||||||
|
|
||||||
@@ -406,7 +432,7 @@ export interface NodeIKernelMsgService {
|
|||||||
|
|
||||||
getEmojiResourcePath(...args: unknown[]): unknown;
|
getEmojiResourcePath(...args: unknown[]): unknown;
|
||||||
|
|
||||||
JoinDragonGroupEmoji(...args: unknown[]): unknown;
|
JoinDragonGroupEmoji(JoinDragonGroupEmojiReq: any/*joinDragonGroupEmojiReq*/): unknown;
|
||||||
|
|
||||||
getMsgAbstracts(...args: unknown[]): unknown;
|
getMsgAbstracts(...args: unknown[]): unknown;
|
||||||
|
|
||||||
@@ -430,7 +456,10 @@ export interface NodeIKernelMsgService {
|
|||||||
|
|
||||||
downloadRichMedia(...args: unknown[]): unknown;
|
downloadRichMedia(...args: unknown[]): unknown;
|
||||||
|
|
||||||
getFirstUnreadMsgSeq(...args: unknown[]): unknown;
|
getFirstUnreadMsgSeq(args: {
|
||||||
|
peerUid: string
|
||||||
|
guildId: string
|
||||||
|
}): unknown;
|
||||||
|
|
||||||
getFirstUnreadCommonMsg(...args: unknown[]): unknown;
|
getFirstUnreadCommonMsg(...args: unknown[]): unknown;
|
||||||
|
|
||||||
@@ -448,7 +477,19 @@ export interface NodeIKernelMsgService {
|
|||||||
|
|
||||||
setMsgEmojiLikes(...args: unknown[]): unknown;
|
setMsgEmojiLikes(...args: unknown[]): unknown;
|
||||||
|
|
||||||
getMsgEmojiLikesList(...args: unknown[]): unknown;
|
getMsgEmojiLikesList(peer: Peer, msgSeq: string, emojiId: string, emojiType: string, cookie: string, bForward: boolean, number: number): Promise<{
|
||||||
|
result: number,
|
||||||
|
errMsg: string,
|
||||||
|
emojiLikesList:
|
||||||
|
Array<{
|
||||||
|
tinyId: string,
|
||||||
|
nickName: string,
|
||||||
|
headUrl: string
|
||||||
|
}>,
|
||||||
|
cookie: string,
|
||||||
|
isLastPage: boolean,
|
||||||
|
isFirstPage: boolean
|
||||||
|
}>;
|
||||||
|
|
||||||
setMsgEmojiLikesForRole(...args: unknown[]): unknown;
|
setMsgEmojiLikesForRole(...args: unknown[]): unknown;
|
||||||
|
|
||||||
@@ -470,13 +511,13 @@ export interface NodeIKernelMsgService {
|
|||||||
|
|
||||||
queryCalendar(...args: unknown[]): unknown;
|
queryCalendar(...args: unknown[]): unknown;
|
||||||
|
|
||||||
queryFirstMsgSeq(...args: unknown[]): unknown;
|
queryFirstMsgSeq(peer: Peer, ...args: unknown[]): unknown;
|
||||||
|
|
||||||
queryRoamCalendar(...args: unknown[]): unknown;
|
queryRoamCalendar(...args: unknown[]): unknown;
|
||||||
|
|
||||||
queryFirstRoamMsg(...args: unknown[]): unknown;
|
queryFirstRoamMsg(...args: unknown[]): unknown;
|
||||||
|
|
||||||
fetchLongMsg(...args: unknown[]): unknown;
|
fetchLongMsg(peer: Peer, msgId: string): unknown;
|
||||||
|
|
||||||
fetchLongMsgWithCb(...args: unknown[]): unknown;
|
fetchLongMsgWithCb(...args: unknown[]): unknown;
|
||||||
|
|
||||||
@@ -510,9 +551,9 @@ export interface NodeIKernelMsgService {
|
|||||||
|
|
||||||
deleteReplyDraft(...args: unknown[]): unknown;
|
deleteReplyDraft(...args: unknown[]): unknown;
|
||||||
|
|
||||||
getFirstUnreadAtMsg(...args: unknown[]): unknown;
|
getFirstUnreadAtMsg(peer: Peer): unknown;
|
||||||
|
|
||||||
clearMsgRecords(...args: unknown[]): unknown;//设置已读后调用我觉得比较好 清理记录
|
clearMsgRecords(...args: unknown[]): unknown;//设置已读后调用我觉得比较好 清理记录 现在别了
|
||||||
|
|
||||||
IsExistOldDb(...args: unknown[]): unknown;
|
IsExistOldDb(...args: unknown[]): unknown;
|
||||||
|
|
||||||
@@ -532,7 +573,7 @@ export interface NodeIKernelMsgService {
|
|||||||
|
|
||||||
getCurChatImportStatusByUin(...args: unknown[]): unknown;
|
getCurChatImportStatusByUin(...args: unknown[]): unknown;
|
||||||
|
|
||||||
getDataImportUserLevel(...args: unknown[]): unknown;
|
getDataImportUserLevel(): unknown;
|
||||||
|
|
||||||
getMsgQRCode(...args: unknown[]): unknown;
|
getMsgQRCode(...args: unknown[]): unknown;
|
||||||
|
|
||||||
@@ -564,6 +605,8 @@ export interface NodeIKernelMsgService {
|
|||||||
// this.gameSession = tempChatGameSession;
|
// this.gameSession = tempChatGameSession;
|
||||||
prepareTempChat(args: unknown): unknown;//主动临时消息 不做
|
prepareTempChat(args: unknown): unknown;//主动临时消息 不做
|
||||||
|
|
||||||
|
sendSsoCmdReqByContend(cmd: string, param: string): Promise<unknown>;
|
||||||
|
|
||||||
//chattype,uid->Promise<any>
|
//chattype,uid->Promise<any>
|
||||||
getTempChatInfo(ChatType: number, Uid: string): unknown;
|
getTempChatInfo(ChatType: number, Uid: string): unknown;
|
||||||
|
|
||||||
@@ -587,7 +630,7 @@ export interface NodeIKernelMsgService {
|
|||||||
|
|
||||||
getABatchOfContactMsgBoxInfo(...args: unknown[]): unknown;
|
getABatchOfContactMsgBoxInfo(...args: unknown[]): unknown;
|
||||||
|
|
||||||
insertMsgToMsgBox(...args: unknown[]): unknown;
|
insertMsgToMsgBox(peer: Peer, msgId: string, arg: 2006): unknown;
|
||||||
|
|
||||||
isHitEmojiKeyword(...args: unknown[]): unknown;
|
isHitEmojiKeyword(...args: unknown[]): unknown;
|
||||||
|
|
||||||
@@ -627,7 +670,25 @@ export interface NodeIKernelMsgService {
|
|||||||
|
|
||||||
dataMigrationStopOperation(...args: unknown[]): unknown;
|
dataMigrationStopOperation(...args: unknown[]): unknown;
|
||||||
|
|
||||||
dataMigrationImportMsgPbRecord(...args: unknown[]): unknown;
|
//新的希望
|
||||||
|
dataMigrationImportMsgPbRecord(DataMigrationMsgInfo: Array<{
|
||||||
|
extensionData: string//"Hex"
|
||||||
|
extraData: string //""
|
||||||
|
chatType: number
|
||||||
|
chatUin: string
|
||||||
|
msgType: number
|
||||||
|
msgTime: string
|
||||||
|
msgSeq: string
|
||||||
|
msgRandom: string
|
||||||
|
}>, DataMigrationResourceInfo: {
|
||||||
|
extraData: string
|
||||||
|
filePath: string
|
||||||
|
fileSize: string
|
||||||
|
msgRandom: string
|
||||||
|
msgSeq: string
|
||||||
|
msgSubType: number
|
||||||
|
msgType: number
|
||||||
|
}): unknown;
|
||||||
|
|
||||||
dataMigrationGetResourceLocalDestinyPath(...args: unknown[]): unknown;
|
dataMigrationGetResourceLocalDestinyPath(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
import { BuddyProfileLikeReq } from "../entities/user";
|
import { BuddyProfileLikeReq } from "../entities/user";
|
||||||
|
import { GeneralCallResult } from "./common";
|
||||||
|
|
||||||
export interface NodeIKernelProfileLikeService {
|
export interface NodeIKernelProfileLikeService {
|
||||||
addKernelProfileLikeListener(listener: NodeIKernelProfileLikeService): void;
|
addKernelProfileLikeListener(listener: NodeIKernelProfileLikeService): void;
|
||||||
@@ -7,7 +8,13 @@ export interface NodeIKernelProfileLikeService {
|
|||||||
|
|
||||||
setBuddyProfileLike(...args: unknown[]): { result: number, errMsg: string, succCounts: number };
|
setBuddyProfileLike(...args: unknown[]): { result: number, errMsg: string, succCounts: number };
|
||||||
|
|
||||||
getBuddyProfileLike(req: BuddyProfileLikeReq): void;
|
getBuddyProfileLike(req: BuddyProfileLikeReq): Promise<GeneralCallResult & {
|
||||||
|
"info": {
|
||||||
|
"userLikeInfos": Array<any>,
|
||||||
|
"friendMaxVotes": number,
|
||||||
|
"start": number
|
||||||
|
}
|
||||||
|
}>;
|
||||||
|
|
||||||
getProfileLikeScidResourceInfo(...args: unknown[]): void;
|
getProfileLikeScidResourceInfo(...args: unknown[]): void;
|
||||||
|
|
||||||
|
@@ -1,10 +1,36 @@
|
|||||||
import { AnyCnameRecord } from 'node:dns';
|
import { AnyCnameRecord } from 'node:dns';
|
||||||
import { BizKey, ModifyProfileParams, UserDetailInfoByUin } from '../entities';
|
import { BaseInfo, BizKey, CoreInfo, ModifyProfileParams, SimpleInfo, UserDetailInfoByUin } from '../entities';
|
||||||
import { NodeIKernelProfileListener } from '../listeners';
|
import { NodeIKernelProfileListener } from '../listeners';
|
||||||
import { GeneralCallResult } from '@/core/services/common';
|
import { GeneralCallResult } from '@/core/services/common';
|
||||||
|
export enum UserDetailSource {
|
||||||
|
KDB,
|
||||||
|
KSERVER
|
||||||
|
}
|
||||||
|
export enum ProfileBizType {
|
||||||
|
KALL,
|
||||||
|
KBASEEXTEND,
|
||||||
|
KVAS,
|
||||||
|
KQZONE,
|
||||||
|
KOTHER
|
||||||
|
}
|
||||||
export interface NodeIKernelProfileService {
|
export interface NodeIKernelProfileService {
|
||||||
|
|
||||||
|
getUidByUin(callfrom: string, uin: Array<string>): Promise<Map<string,string>>;//uin->uid
|
||||||
|
|
||||||
|
getUinByUid(callfrom: string, uid: Array<string>): Promise<Map<string,string>>;
|
||||||
|
// {
|
||||||
|
// coreInfo: CoreInfo,
|
||||||
|
// baseInfo: BaseInfo,
|
||||||
|
// status: null,
|
||||||
|
// vasInfo: null,
|
||||||
|
// relationFlags: null,
|
||||||
|
// otherFlags: null,
|
||||||
|
// intimate: null
|
||||||
|
// }
|
||||||
|
getCoreAndBaseInfo(callfrom: string, uids: string[]): Promise<Map<string, SimpleInfo>>;
|
||||||
|
|
||||||
|
fetchUserDetailInfo(trace: string, uids: string[], arg2: number, arg3: number[]): Promise<unknown>;
|
||||||
|
|
||||||
addKernelProfileListener(listener: NodeIKernelProfileListener): number;
|
addKernelProfileListener(listener: NodeIKernelProfileListener): number;
|
||||||
|
|
||||||
removeKernelProfileListener(listenerId: number): void;
|
removeKernelProfileListener(listenerId: number): void;
|
||||||
|
@@ -1,5 +1,28 @@
|
|||||||
import { Peer } from "../entities";
|
import { ChatType, Peer } from "../entities";
|
||||||
|
import { NodeIKernelRecentContactListener } from "../listeners/NodeIKernelRecentContactListener";
|
||||||
|
import { GeneralCallResult } from "./common";
|
||||||
|
export interface FSABRecentContactParams {
|
||||||
|
anchorPointContact: {
|
||||||
|
contactId: string;
|
||||||
|
sortField: string;
|
||||||
|
pos: number;
|
||||||
|
},
|
||||||
|
relativeMoveCount: number;
|
||||||
|
listType: number;
|
||||||
|
count: number;
|
||||||
|
fetchOld: boolean;
|
||||||
|
}
|
||||||
|
// {
|
||||||
|
// "anchorPointContact": {
|
||||||
|
// "contactId": "",
|
||||||
|
// "sortField": "",
|
||||||
|
// "pos": 0
|
||||||
|
// },
|
||||||
|
// "relativeMoveCount": 0,
|
||||||
|
// "listType": 1,
|
||||||
|
// "count": 200,
|
||||||
|
// "fetchOld": true
|
||||||
|
// }
|
||||||
export interface NodeIKernelRecentContactService {
|
export interface NodeIKernelRecentContactService {
|
||||||
setGuildDisplayStatus(...args: unknown[]): unknown; // 2 arguments
|
setGuildDisplayStatus(...args: unknown[]): unknown; // 2 arguments
|
||||||
|
|
||||||
@@ -11,7 +34,20 @@ export interface NodeIKernelRecentContactService {
|
|||||||
|
|
||||||
enterOrExitMsgList(...args: unknown[]): unknown; // 1 arguments
|
enterOrExitMsgList(...args: unknown[]): unknown; // 1 arguments
|
||||||
|
|
||||||
getRecentContactListSnapShot(...args: unknown[]): unknown; // 1 arguments
|
/*!---!*/getRecentContactListSnapShot(count: number): Promise<GeneralCallResult & {
|
||||||
|
info: {
|
||||||
|
errCode: number,
|
||||||
|
errMsg: string,
|
||||||
|
sortedContactList: Array<number>,
|
||||||
|
changedList: Array<{
|
||||||
|
remark: any;
|
||||||
|
peerName: any;
|
||||||
|
sendMemberName: any;
|
||||||
|
sendNickName: any;
|
||||||
|
peerUid: string; peerUin: string, msgTime: string, chatType: ChatType, msgId: string
|
||||||
|
}>
|
||||||
|
}
|
||||||
|
}>; // 1 arguments
|
||||||
|
|
||||||
clearMsgUnreadCount(...args: unknown[]): unknown; // 1 arguments
|
clearMsgUnreadCount(...args: unknown[]): unknown; // 1 arguments
|
||||||
|
|
||||||
@@ -19,7 +55,7 @@ export interface NodeIKernelRecentContactService {
|
|||||||
|
|
||||||
jumpToSpecifyRecentContact(...args: unknown[]): unknown; // 1 arguments
|
jumpToSpecifyRecentContact(...args: unknown[]): unknown; // 1 arguments
|
||||||
|
|
||||||
fetchAndSubscribeABatchOfRecentContact(...args: unknown[]): unknown; // 1 arguments
|
/*!---!*/fetchAndSubscribeABatchOfRecentContact(params: FSABRecentContactParams): unknown; // 1 arguments
|
||||||
|
|
||||||
addRecentContact(peer: Peer): unknown;
|
addRecentContact(peer: Peer): unknown;
|
||||||
|
|
||||||
@@ -31,9 +67,9 @@ export interface NodeIKernelRecentContactService {
|
|||||||
|
|
||||||
updateGameMsgConfigs(...args: unknown[]): unknown; // 1 arguments
|
updateGameMsgConfigs(...args: unknown[]): unknown; // 1 arguments
|
||||||
|
|
||||||
removeKernelRecentContactListener(...args: unknown[]): unknown; // 1 arguments
|
removeKernelRecentContactListener(listenerid: number): unknown; // 1 arguments
|
||||||
|
|
||||||
addKernelRecentContactListener(...args: unknown[]): unknown; // 1 arguments
|
addKernelRecentContactListener(listener: NodeIKernelRecentContactListener): void;
|
||||||
|
|
||||||
clearRecentContactsByChatType(...args: unknown[]): unknown; // 1 arguments
|
clearRecentContactsByChatType(...args: unknown[]): unknown; // 1 arguments
|
||||||
|
|
||||||
@@ -43,7 +79,7 @@ export interface NodeIKernelRecentContactService {
|
|||||||
|
|
||||||
deleteRecentContactsVer2(...args: unknown[]): unknown; // 1 arguments
|
deleteRecentContactsVer2(...args: unknown[]): unknown; // 1 arguments
|
||||||
|
|
||||||
getRecentContactList(): unknown;
|
getRecentContactList(): Promise<any>;
|
||||||
|
|
||||||
getMsgUnreadCount(): unknown;
|
getMsgUnreadCount(): unknown;
|
||||||
|
|
||||||
|
@@ -1,6 +1,50 @@
|
|||||||
import { GetFileListParam, Peer } from "../entities";
|
import { GetFileListParam, MessageElement, Peer, SendMessageElement } from "../entities";
|
||||||
import { GeneralCallResult } from "./common";
|
import { GeneralCallResult } from "./common";
|
||||||
|
export enum UrlFileDownloadType {
|
||||||
|
KUNKNOWN,
|
||||||
|
KURLFILEDOWNLOADPRIVILEGEICON,
|
||||||
|
KURLFILEDOWNLOADPHOTOWALL,
|
||||||
|
KURLFILEDOWNLOADQZONE,
|
||||||
|
KURLFILEDOWNLOADCOMMON,
|
||||||
|
KURLFILEDOWNLOADINSTALLAPP
|
||||||
|
}
|
||||||
|
export enum RMBizTypeEnum {
|
||||||
|
KUNKNOWN,
|
||||||
|
KC2CFILE,
|
||||||
|
KGROUPFILE,
|
||||||
|
KC2CPIC,
|
||||||
|
KGROUPPIC,
|
||||||
|
KDISCPIC,
|
||||||
|
KC2CVIDEO,
|
||||||
|
KGROUPVIDEO,
|
||||||
|
KC2CPTT,
|
||||||
|
KGROUPPTT,
|
||||||
|
KFEEDCOMMENTPIC,
|
||||||
|
KGUILDFILE,
|
||||||
|
KGUILDPIC,
|
||||||
|
KGUILDPTT,
|
||||||
|
KGUILDVIDEO
|
||||||
|
}
|
||||||
|
export interface CommonFileInfo {
|
||||||
|
bizType: number;
|
||||||
|
chatType: number;
|
||||||
|
elemId: string;
|
||||||
|
favId: string;
|
||||||
|
fileModelId: string;
|
||||||
|
fileName: string;
|
||||||
|
fileSize: string;
|
||||||
|
md5: string;
|
||||||
|
md510m: string;
|
||||||
|
msgId: string;
|
||||||
|
msgTime: string;
|
||||||
|
parent: string;
|
||||||
|
peerUid: string;
|
||||||
|
picThumbPath: Array<string>
|
||||||
|
sha: string;
|
||||||
|
sha3: string;
|
||||||
|
subId: string;
|
||||||
|
uuid: string;
|
||||||
|
}
|
||||||
export interface NodeIKernelRichMediaService {
|
export interface NodeIKernelRichMediaService {
|
||||||
//getVideoPlayUrl(peer, msgId, elemId, videoCodecFormat, VideoRequestWay.KHAND, cb);
|
//getVideoPlayUrl(peer, msgId, elemId, videoCodecFormat, VideoRequestWay.KHAND, cb);
|
||||||
// public enum VideoCodecFormatType {
|
// public enum VideoCodecFormatType {
|
||||||
@@ -46,7 +90,7 @@ export interface NodeIKernelRichMediaService {
|
|||||||
}
|
}
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
getRichMediaFileDir(arg1: unknown, arg2: unknown, arg3: unknown): unknown;
|
getRichMediaFileDir(elementType: number, downType: number, isTemp: boolean): unknown;
|
||||||
|
|
||||||
// this.senderUid = "";
|
// this.senderUid = "";
|
||||||
// this.peerUid = "";
|
// this.peerUid = "";
|
||||||
@@ -65,16 +109,43 @@ export interface NodeIKernelRichMediaService {
|
|||||||
// this.elem = msgElement;
|
// this.elem = msgElement;
|
||||||
// this.useHttps = num;
|
// this.useHttps = num;
|
||||||
|
|
||||||
getVideoPlayUrlInVisit(arg: unknown): unknown;
|
getVideoPlayUrlInVisit(arg: {
|
||||||
|
downloadType: number,
|
||||||
|
thumbSize: number,
|
||||||
|
msgId: string,
|
||||||
|
msgRandom: string,
|
||||||
|
msgSeq: string,
|
||||||
|
msgTime: string,
|
||||||
|
chatType: number,
|
||||||
|
senderUid: string,
|
||||||
|
peerUid: string,
|
||||||
|
guildId: string,
|
||||||
|
ele: MessageElement,
|
||||||
|
useHttps: boolean
|
||||||
|
}): Promise<unknown>;
|
||||||
|
|
||||||
isFileExpired(arg: unknown): unknown;
|
//arg双端number
|
||||||
|
isFileExpired(arg: number): unknown;
|
||||||
|
|
||||||
deleteGroupFolder(GroupCode: string, FolderId: string): Promise<GeneralCallResult & { groupFileCommonResult: { retCode: number, retMsg: string, clientWording: string } }>;
|
deleteGroupFolder(GroupCode: string, FolderId: string): Promise<GeneralCallResult & { groupFileCommonResult: { retCode: number, retMsg: string, clientWording: string } }>;
|
||||||
|
|
||||||
//参数与getVideoPlayUrlInVisit一样
|
//参数与getVideoPlayUrlInVisit一样
|
||||||
downloadRichMediaInVisit(arg: unknown): unknown;
|
downloadRichMediaInVisit(arg: {
|
||||||
|
downloadType: number,
|
||||||
downloadFileForModelId(peer: Peer, arg: unknown[], arg3: string): unknown;
|
thumbSize: number,
|
||||||
|
msgId: string,
|
||||||
|
msgRandom: string,
|
||||||
|
msgSeq: string,
|
||||||
|
msgTime: string,
|
||||||
|
chatType: number,
|
||||||
|
senderUid: string,
|
||||||
|
peerUid: string,
|
||||||
|
guildId: string,
|
||||||
|
ele: MessageElement,
|
||||||
|
useHttps: boolean
|
||||||
|
}): unknown;
|
||||||
|
//arg3为“”
|
||||||
|
downloadFileForModelId(peer: Peer, ModelId: string[], arg3: string): unknown;
|
||||||
//第三个参数 Array<Type>
|
//第三个参数 Array<Type>
|
||||||
// this.fileId = "";
|
// this.fileId = "";
|
||||||
// this.fileName = "";
|
// this.fileName = "";
|
||||||
@@ -83,25 +154,30 @@ export interface NodeIKernelRichMediaService {
|
|||||||
// this.fileSize = j2;
|
// this.fileSize = j2;
|
||||||
// this.fileModelId = j3;
|
// this.fileModelId = j3;
|
||||||
|
|
||||||
downloadFileForFileUuid(peer: Peer, arg1: string, arg3: unknown[]): unknown;
|
downloadFileForFileUuid(peer: Peer, uuid: string, arg3: {
|
||||||
|
fileId: string,
|
||||||
|
fileName: string,
|
||||||
|
fileSize: string,
|
||||||
|
fileModelId: string
|
||||||
|
}[]): Promise<unknown>;
|
||||||
|
|
||||||
downloadFileByUrlListtransgroupfile(arg1: unknown, arg2: unknown): unknown;
|
downloadFileByUrlList(fileDownloadTyp: UrlFileDownloadType, urlList: Array<string>): unknown;
|
||||||
|
|
||||||
downloadFileForFileInfotransgroupfile(arg1: unknown, arg2: unknown): unknown;
|
downloadFileForFileInfo(fileInfo: CommonFileInfo[], savePath: string): unknown;
|
||||||
|
|
||||||
createGroupFolder(GroupCode: string, FolderName: string): Promise<GeneralCallResult & { resultWithGroupItem: { result: any, groupItem: Array<any> } }>
|
createGroupFolder(GroupCode: string, FolderName: string): Promise<GeneralCallResult & { resultWithGroupItem: { result: any, groupItem: Array<any> } }>
|
||||||
|
|
||||||
downloadFile(arg1: unknown, arg2: unknown, arg3: unknown, arg4: unknown): unknown;
|
downloadFile(commonFile: CommonFileInfo, arg2: unknown, arg3: unknown, savePath: string): unknown;
|
||||||
|
|
||||||
createGroupFoldertransgroupfile(arg1: unknown, arg2: unknown): unknown;
|
createGroupFolder(arg1: unknown, arg2: unknown): unknown;
|
||||||
|
|
||||||
downloadGroupFolder(arg1: unknown, arg2: unknown, arg3: unknown): unknown;
|
downloadGroupFolder(arg1: unknown, arg2: unknown, arg3: unknown): unknown;
|
||||||
|
|
||||||
renameGroupFolder(arg1: unknown, arg2: unknown, arg3: unknown): unknown;
|
renameGroupFolder(arg1: unknown, arg2: unknown, arg3: unknown): unknown;
|
||||||
|
|
||||||
deleteGroupFoldertransgroupfile(arg1: unknown, arg2: unknown): unknown;
|
deleteGroupFolder(arg1: unknown, arg2: unknown): unknown;
|
||||||
|
|
||||||
deleteTransferInfotransgroupfile(arg1: unknown, arg2: unknown): unknown;
|
deleteTransferInfo(arg1: unknown, arg2: unknown): unknown;
|
||||||
|
|
||||||
cancelTransferTask(arg1: unknown, arg2: unknown, arg3: unknown): unknown;
|
cancelTransferTask(arg1: unknown, arg2: unknown, arg3: unknown): unknown;
|
||||||
|
|
||||||
@@ -122,11 +198,9 @@ export interface NodeIKernelRichMediaService {
|
|||||||
}
|
}
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
getGroupFileInfotransgroupfile(arg1: unknown, arg2: unknown): unknown;
|
getGroupFileInfo(arg1: unknown, arg2: unknown): unknown;
|
||||||
|
|
||||||
getGroupFileListtransgroupfile(arg1: unknown, arg2: unknown): unknown;
|
getGroupTransferList(arg1: unknown, arg2: unknown): unknown;
|
||||||
|
|
||||||
getGroupTransferListtransgroupfile(arg1: unknown, arg2: unknown): unknown;
|
|
||||||
|
|
||||||
renameGroupFile(arg1: unknown, arg2: unknown, arg3: unknown, arg4: unknown, arg5: unknown): unknown;
|
renameGroupFile(arg1: unknown, arg2: unknown, arg3: unknown, arg4: unknown, arg5: unknown): unknown;
|
||||||
|
|
||||||
@@ -134,6 +208,16 @@ export interface NodeIKernelRichMediaService {
|
|||||||
|
|
||||||
transGroupFile(arg1: unknown, arg2: unknown): unknown;
|
transGroupFile(arg1: unknown, arg2: unknown): unknown;
|
||||||
|
|
||||||
|
searchGroupFile(
|
||||||
|
keywords: Array<string>,
|
||||||
|
param: {
|
||||||
|
groupIds: Array<string>,
|
||||||
|
fileType: number,
|
||||||
|
context: string,
|
||||||
|
count: number,
|
||||||
|
sortType: number,
|
||||||
|
groupNames: Array<string>
|
||||||
|
}): Promise<unknown>;
|
||||||
searchGroupFileByWord(arg1: unknown, arg2: unknown, arg3: unknown, arg4: unknown, arg5: unknown): unknown;
|
searchGroupFileByWord(arg1: unknown, arg2: unknown, arg3: unknown, arg4: unknown, arg5: unknown): unknown;
|
||||||
|
|
||||||
deleteGroupFile(GroupCode: string, params: Array<number>, Files: Array<string>): Promise<GeneralCallResult & {
|
deleteGroupFile(GroupCode: string, params: Array<number>, Files: Array<string>): Promise<GeneralCallResult & {
|
||||||
@@ -152,19 +236,31 @@ export interface NodeIKernelRichMediaService {
|
|||||||
|
|
||||||
queryPicDownloadSize(arg: unknown): unknown;
|
queryPicDownloadSize(arg: unknown): unknown;
|
||||||
|
|
||||||
searchGroupFiletransgroupfile(arg1: unknown, arg2: unknown): unknown;
|
searchGroupFile(arg1: unknown, arg2: unknown): unknown;
|
||||||
|
|
||||||
searchMoreGroupFile(arg: unknown): unknown;
|
searchMoreGroupFile(arg: unknown): unknown;
|
||||||
|
|
||||||
cancelSearcheGroupFile(arg1: unknown, arg2: unknown, arg3: unknown): unknown;
|
cancelSearcheGroupFile(arg1: unknown, arg2: unknown, arg3: unknown): unknown;
|
||||||
|
|
||||||
onlyDownloadFile(arg1: unknown, arg2: unknown, arg3: unknown): unknown;
|
onlyDownloadFile(peer: Peer, arg2: unknown, arg3: Array<{
|
||||||
|
fileId: string,
|
||||||
|
fileName: string,
|
||||||
|
fileSize: string,
|
||||||
|
fileModelId: string
|
||||||
|
}
|
||||||
|
>): unknown;
|
||||||
|
|
||||||
onlyUploadFiletransgroupfile(arg1: unknown, arg2: unknown): unknown;
|
onlyUploadFile(arg1: unknown, arg2: unknown): unknown;
|
||||||
|
|
||||||
isExtraLargePic(arg1: unknown, arg2: unknown, arg3: unknown): unknown;
|
isExtraLargePic(arg1: unknown, arg2: unknown, arg3: unknown): unknown;
|
||||||
|
|
||||||
uploadRMFileWithoutMsg(arg: unknown): unknown;
|
uploadRMFileWithoutMsg(arg: {
|
||||||
|
bizType: RMBizTypeEnum,
|
||||||
|
filePath: string,
|
||||||
|
peerUid: string,
|
||||||
|
transferId: string
|
||||||
|
useNTV2: string
|
||||||
|
}): Promise<unknown>;
|
||||||
|
|
||||||
isNull(): boolean;
|
isNull(): boolean;
|
||||||
}
|
}
|
@@ -21,7 +21,7 @@ export interface NodeIKernelRobotService {
|
|||||||
|
|
||||||
removeKernelRobotListener(ListenerId: number): unknown;
|
removeKernelRobotListener(ListenerId: number): unknown;
|
||||||
|
|
||||||
getAllRobotFriendsFromCache(): unknown;
|
getAllRobotFriendsFromCache(): Promise<unknown>;
|
||||||
|
|
||||||
fetchAllRobots(arg1: unknown, arg2: unknown): unknown;
|
fetchAllRobots(arg1: unknown, arg2: unknown): unknown;
|
||||||
|
|
||||||
@@ -30,5 +30,6 @@ export interface NodeIKernelRobotService {
|
|||||||
setRobotPickTts(arg1: unknown, arg2: unknown): unknown;
|
setRobotPickTts(arg1: unknown, arg2: unknown): unknown;
|
||||||
|
|
||||||
getRobotUinRange(data: any): Promise<{ response: { robotUinRanges: any } }>
|
getRobotUinRange(data: any): Promise<{ response: { robotUinRanges: any } }>
|
||||||
|
|
||||||
isNull(): boolean;
|
isNull(): boolean;
|
||||||
}
|
}
|
||||||
|
@@ -1,79 +1,129 @@
|
|||||||
export interface NodeIKernelSearchService{
|
import { ChatType } from "../entities";
|
||||||
addKernelSearchListenerr(...args: any[]): unknown;// needs 1 arguments
|
|
||||||
|
|
||||||
removeKernelSearchListenerr(...args: any[]): unknown;// needs 1 arguments
|
export interface NodeIKernelSearchService {
|
||||||
|
addKernelSearchListener(...args: any[]): unknown;// needs 1 arguments
|
||||||
|
|
||||||
searchStrangerr(...args: any[]): unknown;// needs 3 arguments
|
removeKernelSearchListener(...args: any[]): unknown;// needs 1 arguments
|
||||||
|
|
||||||
searchGroupr(...args: any[]): unknown;// needs 1 arguments
|
searchStranger(...args: any[]): unknown;// needs 3 arguments
|
||||||
|
|
||||||
searchLocalInfor(...args: any[]): unknown;// needs 2 arguments
|
searchGroup(...args: any[]): unknown;// needs 1 arguments
|
||||||
|
|
||||||
cancelSearchLocalInfor(...args: any[]): unknown;// needs 3 arguments
|
searchLocalInfo(keywords: string, unknown: number/*4*/): unknown;
|
||||||
|
|
||||||
searchBuddyChatInfor(...args: any[]): unknown;// needs 2 arguments
|
cancelSearchLocalInfo(...args: any[]): unknown;// needs 3 arguments
|
||||||
|
|
||||||
searchMoreBuddyChatInfor(...args: any[]): unknown;// needs 1 arguments
|
searchBuddyChatInfo(...args: any[]): unknown;// needs 2 arguments
|
||||||
|
|
||||||
cancelSearchBuddyChatInfor(...args: any[]): unknown;// needs 3 arguments
|
searchMoreBuddyChatInfo(...args: any[]): unknown;// needs 1 arguments
|
||||||
|
|
||||||
searchContactr(...args: any[]): unknown;// needs 2 arguments
|
cancelSearchBuddyChatInfo(...args: any[]): unknown;// needs 3 arguments
|
||||||
|
|
||||||
searchMoreContactr(...args: any[]): unknown;// needs 1 arguments
|
searchContact(...args: any[]): unknown;// needs 2 arguments
|
||||||
|
|
||||||
cancelSearchContactr(...args: any[]): unknown;// needs 3 arguments
|
searchMoreContact(...args: any[]): unknown;// needs 1 arguments
|
||||||
|
|
||||||
searchGroupChatInfor(...args: any[]): unknown;// needs 3 arguments
|
cancelSearchContact(...args: any[]): unknown;// needs 3 arguments
|
||||||
|
|
||||||
resetSearchGroupChatInfoSortTyper(...args: any[]): unknown;// needs 3 arguments
|
searchGroupChatInfo(...args: any[]): unknown;// needs 3 arguments
|
||||||
|
|
||||||
resetSearchGroupChatInfoFilterMembersr(...args: any[]): unknown;// needs 3 arguments
|
resetSearchGroupChatInfoSortType(...args: any[]): unknown;// needs 3 arguments
|
||||||
|
|
||||||
searchMoreGroupChatInfor(...args: any[]): unknown;// needs 1 arguments
|
resetSearchGroupChatInfoFilterMembers(...args: any[]): unknown;// needs 3 arguments
|
||||||
|
|
||||||
cancelSearchGroupChatInfor(...args: any[]): unknown;// needs 3 arguments
|
searchMoreGroupChatInfo(...args: any[]): unknown;// needs 1 arguments
|
||||||
|
|
||||||
searchChatsWithKeywordsr(...args: any[]): unknown;// needs 3 arguments
|
cancelSearchGroupChatInfo(...args: any[]): unknown;// needs 3 arguments
|
||||||
|
|
||||||
searchMoreChatsWithKeywordsr(...args: any[]): unknown;// needs 1 arguments
|
searchChatsWithKeywords(...args: any[]): unknown;// needs 3 arguments
|
||||||
|
|
||||||
cancelSearchChatsWithKeywordsr(...args: any[]): unknown;// needs 3 arguments
|
searchMoreChatsWithKeywords(...args: any[]): unknown;// needs 1 arguments
|
||||||
|
|
||||||
searchChatMsgsr(...args: any[]): unknown;// needs 2 arguments
|
cancelSearchChatsWithKeywords(...args: any[]): unknown;// needs 3 arguments
|
||||||
|
|
||||||
searchMoreChatMsgsr(...args: any[]): unknown;// needs 1 arguments
|
searchChatMsgs(...args: any[]): unknown;// needs 2 arguments
|
||||||
|
|
||||||
cancelSearchChatMsgsr(...args: any[]): unknown;// needs 3 arguments
|
searchMoreChatMsgs(...args: any[]): unknown;// needs 1 arguments
|
||||||
|
|
||||||
searchMsgWithKeywordsr(...args: any[]): unknown;// needs 2 arguments
|
cancelSearchChatMsgs(...args: any[]): unknown;// needs 3 arguments
|
||||||
|
|
||||||
searchMoreMsgWithKeywordsr(...args: any[]): unknown;// needs 1 arguments
|
searchMsgWithKeywords(...args: any[]): unknown;// needs 2 arguments
|
||||||
|
|
||||||
cancelSearchMsgWithKeywordsr(...args: any[]): unknown;// needs 3 arguments
|
|
||||||
|
|
||||||
searchFileWithKeywordsr(...args: any[]): unknown;// needs 2 arguments
|
searchMoreMsgWithKeywords(...args: any[]): unknown;// needs 1 arguments
|
||||||
|
|
||||||
searchMoreFileWithKeywordsr(...args: any[]): unknown;// needs 1 arguments
|
cancelSearchMsgWithKeywords(...args: any[]): unknown;// needs 3 arguments
|
||||||
|
|
||||||
cancelSearchFileWithKeywordsr(...args: any[]): unknown;// needs 3 arguments
|
searchFileWithKeywords(keywords: string[], source: number): Promise<string>;// needs 2 arguments
|
||||||
|
|
||||||
searchAtMeChatsr(...args: any[]): unknown;// needs 3 arguments
|
searchMoreFileWithKeywords(...args: any[]): unknown;// needs 1 arguments
|
||||||
|
|
||||||
searchMoreAtMeChatsr(...args: any[]): unknown;// needs 1 arguments
|
cancelSearchFileWithKeywords(...args: any[]): unknown;// needs 3 arguments
|
||||||
|
|
||||||
cancelSearchAtMeChatsr(...args: any[]): unknown;// needs 3 arguments
|
searchAtMeChats(...args: any[]): unknown;// needs 3 arguments
|
||||||
|
|
||||||
searchChatAtMeMsgsr(...args: any[]): unknown;// needs 1 arguments
|
searchMoreAtMeChats(...args: any[]): unknown;// needs 1 arguments
|
||||||
|
|
||||||
searchMoreChatAtMeMsgsr(...args: any[]): unknown;// needs 1 arguments
|
cancelSearchAtMeChats(...args: any[]): unknown;// needs 3 arguments
|
||||||
|
|
||||||
cancelSearchChatAtMeMsgsr(...args: any[]): unknown;// needs 3 arguments
|
searchChatAtMeMsgs(...args: any[]): unknown;// needs 1 arguments
|
||||||
|
|
||||||
addSearchHistoryr(...args: any[]): unknown;// needs 1 arguments
|
searchMoreChatAtMeMsgs(...args: any[]): unknown;// needs 1 arguments
|
||||||
|
|
||||||
removeSearchHistoryr(...args: any[]): unknown;// needs 1 arguments
|
cancelSearchChatAtMeMsgs(...args: any[]): unknown;// needs 3 arguments
|
||||||
|
|
||||||
searchCacher(...args: any[]): unknown;// needs 3 arguments
|
addSearchHistory(param: {
|
||||||
|
type: number,//4
|
||||||
|
contactList: [],
|
||||||
|
id: number,//-1
|
||||||
|
groupInfos: [],
|
||||||
|
msgs: [],
|
||||||
|
fileInfos: [
|
||||||
|
{
|
||||||
|
chatType: ChatType,
|
||||||
|
buddyChatInfo: Array<{ category_name: string, peerUid: string, peerUin: string, remark: string }>,
|
||||||
|
discussChatInfo: [],
|
||||||
|
groupChatInfo: Array<
|
||||||
|
{
|
||||||
|
groupCode: string,
|
||||||
|
isConf: boolean,
|
||||||
|
hasModifyConfGroupFace: boolean,
|
||||||
|
hasModifyConfGroupName: boolean,
|
||||||
|
groupName: string,
|
||||||
|
remark: string
|
||||||
|
}>
|
||||||
|
,
|
||||||
|
dataLineChatInfo: [],
|
||||||
|
tmpChatInfo: [],
|
||||||
|
msgId: string,
|
||||||
|
msgSeq: string,
|
||||||
|
msgTime: string,
|
||||||
|
senderUid: string,
|
||||||
|
senderNick: string,
|
||||||
|
senderRemark: string,
|
||||||
|
senderCard: string,
|
||||||
|
elemId: string,
|
||||||
|
elemType: string,//3
|
||||||
|
fileSize: string,
|
||||||
|
filePath: string,
|
||||||
|
fileName: string,
|
||||||
|
hits: Array<
|
||||||
|
{
|
||||||
|
start: 12,
|
||||||
|
end: 14
|
||||||
|
}
|
||||||
|
>
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
clearSearchCacher(...args: any[]): unknown;// needs 1 arguments
|
}): Promise<{
|
||||||
|
result: number,
|
||||||
|
errMsg: string,
|
||||||
|
id?: number
|
||||||
|
}>;
|
||||||
|
|
||||||
|
removeSearchHistory(...args: any[]): unknown;// needs 1 arguments
|
||||||
|
|
||||||
|
searchCache(...args: any[]): unknown;// needs 3 arguments
|
||||||
|
|
||||||
|
clearSearchCache(...args: any[]): unknown;// needs 1 arguments
|
||||||
}
|
}
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
export interface NodeIKernelUixConvertService {
|
export interface NodeIKernelUixConvertService {
|
||||||
getUin(uid: string[]): Promise<{ uidInfo: Map<string, string> }>;
|
getUin(uid: string[]): Promise<{ uinInfo: Map<string, string> }>;
|
||||||
|
|
||||||
getUid(uin: string[]): Promise<{ uinInfo: Map<string, string> }>;
|
getUid(uin: string[]): Promise<{ uidInfo: Map<string, string> }>;
|
||||||
}
|
}
|
14
src/core/src/services/NodeIkernelTestPerformanceService.ts
Normal file
14
src/core/src/services/NodeIkernelTestPerformanceService.ts
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import { MessageElement, Peer } from "../entities";
|
||||||
|
|
||||||
|
export interface NodeIkernelTestPerformanceService {
|
||||||
|
insertMsg(MsgParam: {
|
||||||
|
peer: Peer
|
||||||
|
msgTime: string
|
||||||
|
msgId: string
|
||||||
|
msgSeq: string
|
||||||
|
batchNums: number
|
||||||
|
timesPerBatch: number
|
||||||
|
numPerTime: number
|
||||||
|
}, msg: Array<MessageElement>): Promise<unknown>;
|
||||||
|
|
||||||
|
}
|
@@ -1,4 +1,4 @@
|
|||||||
import { appid, qqPkgInfo, qqVersionConfigInfo } from '@/common/utils/QQBasicInfo';
|
import { getFullQQVesion, QQVersionAppid } from '@/common/utils/QQBasicInfo';
|
||||||
import { hostname, systemName, systemVersion } from '@/common/utils/system';
|
import { hostname, systemName, systemVersion } from '@/common/utils/system';
|
||||||
import path from 'node:path';
|
import path from 'node:path';
|
||||||
import fs from 'node:fs';
|
import fs from 'node:fs';
|
||||||
@@ -34,42 +34,42 @@ export interface WrapperSessionInitConfig {
|
|||||||
account_path: string // 可以通过NodeQQNTWrapperUtil().getNTUserDataInfoConfig()获取
|
account_path: string // 可以通过NodeQQNTWrapperUtil().getNTUserDataInfoConfig()获取
|
||||||
}
|
}
|
||||||
clientVer: string // 9.9.8-22355
|
clientVer: string // 9.9.8-22355
|
||||||
a2: '',
|
a2: string,
|
||||||
d2: '',
|
d2: string,
|
||||||
d2Key: '',
|
d2Key: string,
|
||||||
machineId: '',
|
machineId: string,
|
||||||
platform: 3, // 3是Windows?
|
platform: PlatformType, // 3是Windows?
|
||||||
platVer: string, // 系统版本号, 应该可以固定
|
platVer: string, // 系统版本号, 应该可以固定
|
||||||
appid: string,
|
appid: string,
|
||||||
rdeliveryConfig: {
|
rdeliveryConfig: {
|
||||||
appKey: '',
|
appKey: string,
|
||||||
systemId: 0,
|
systemId: number,
|
||||||
appId: '',
|
appId: string,
|
||||||
logicEnvironment: '',
|
logicEnvironment: string,
|
||||||
platform: 3,
|
platform: PlatformType,
|
||||||
language: '',
|
language: string,
|
||||||
sdkVersion: '',
|
sdkVersion: string,
|
||||||
userId: '',
|
userId: string,
|
||||||
appVersion: '',
|
appVersion: string,
|
||||||
osVersion: '',
|
osVersion: string,
|
||||||
bundleId: '',
|
bundleId: string,
|
||||||
serverUrl: '',
|
serverUrl: string,
|
||||||
fixedAfterHitKeys: ['']
|
fixedAfterHitKeys: string[]
|
||||||
}
|
}
|
||||||
'defaultFileDownloadPath': string, // 这个可以通过环境变量获取?
|
defaultFileDownloadPath: string, // 这个可以通过环境变量获取?
|
||||||
'deviceInfo': {
|
deviceInfo: {
|
||||||
'guid': string,
|
guid: string,
|
||||||
'buildVer': string,
|
buildVer: string,
|
||||||
'localId': 2052,
|
localId: number,
|
||||||
'devName': string,
|
devName: string,
|
||||||
'devType': string,
|
devType: string,
|
||||||
'vendorName': '',
|
vendorName: string,
|
||||||
'osVer': string,
|
osVer: string,
|
||||||
'vendorOsName': string,
|
vendorOsName: string,
|
||||||
'setMute': false,
|
setMute: boolean,
|
||||||
'vendorType': 0
|
vendorType: VendorType
|
||||||
},
|
},
|
||||||
'deviceConfig': '{"appearance":{"isSplitViewMode":true},"msg":{}}'
|
deviceConfig: '{"appearance":{"isSplitViewMode":true},"msg":{}}'
|
||||||
}
|
}
|
||||||
|
|
||||||
export const sessionConfig: WrapperSessionInitConfig | any = {};
|
export const sessionConfig: WrapperSessionInitConfig | any = {};
|
||||||
@@ -79,7 +79,7 @@ export async function genSessionConfig(selfUin: string, selfUid: string, account
|
|||||||
fs.mkdirSync(downloadPath, { recursive: true });
|
fs.mkdirSync(downloadPath, { recursive: true });
|
||||||
let guid: string = await getMachineId();
|
let guid: string = await getMachineId();
|
||||||
//console.log(guid);
|
//console.log(guid);
|
||||||
// guid = '52afb776-82f6-4e59-9d38-44705b112d0a';
|
// guid = '52afb776-82f6-4e59-9d38-44705b112d0a';
|
||||||
//let guid: string = await getMachineId();
|
//let guid: string = await getMachineId();
|
||||||
const config: WrapperSessionInitConfig = {
|
const config: WrapperSessionInitConfig = {
|
||||||
selfUin,
|
selfUin,
|
||||||
@@ -87,20 +87,20 @@ export async function genSessionConfig(selfUin: string, selfUid: string, account
|
|||||||
desktopPathConfig: {
|
desktopPathConfig: {
|
||||||
account_path // 可以通过NodeQQNTWrapperUtil().getNTUserDataInfoConfig()获取
|
account_path // 可以通过NodeQQNTWrapperUtil().getNTUserDataInfoConfig()获取
|
||||||
},
|
},
|
||||||
clientVer: qqVersionConfigInfo.curVersion, // 9.9.8-22355
|
clientVer: getFullQQVesion(), // 9.9.8-22355
|
||||||
a2: '',
|
a2: '',
|
||||||
d2: '',
|
d2: '',
|
||||||
d2Key: '',
|
d2Key: '',
|
||||||
machineId: '',
|
machineId: '',
|
||||||
platform: 3, // 3是Windows?
|
platform: PlatformType.KWINDOWS, // 3是Windows?
|
||||||
platVer: systemVersion, // 系统版本号, 应该可以固定
|
platVer: systemVersion, // 系统版本号, 应该可以固定
|
||||||
appid: appid,
|
appid: QQVersionAppid,
|
||||||
rdeliveryConfig: {
|
rdeliveryConfig: {
|
||||||
appKey: '',
|
appKey: '',
|
||||||
systemId: 0,
|
systemId: 0,
|
||||||
appId: '',
|
appId: '',
|
||||||
logicEnvironment: '',
|
logicEnvironment: '',
|
||||||
platform: 3,
|
platform: PlatformType.KWINDOWS,
|
||||||
language: '',
|
language: '',
|
||||||
sdkVersion: '',
|
sdkVersion: '',
|
||||||
userId: '',
|
userId: '',
|
||||||
@@ -110,22 +110,21 @@ export async function genSessionConfig(selfUin: string, selfUid: string, account
|
|||||||
serverUrl: '',
|
serverUrl: '',
|
||||||
fixedAfterHitKeys: ['']
|
fixedAfterHitKeys: ['']
|
||||||
},
|
},
|
||||||
'defaultFileDownloadPath': downloadPath,
|
defaultFileDownloadPath: downloadPath,
|
||||||
'deviceInfo': {
|
deviceInfo: {
|
||||||
guid,
|
guid,
|
||||||
'buildVer': qqPkgInfo.version,
|
buildVer: getFullQQVesion(),
|
||||||
'localId': 2052,
|
localId: 2052,
|
||||||
'devName': hostname,
|
devName: hostname,
|
||||||
'devType': systemName,
|
devType: systemName,
|
||||||
'vendorName': '',
|
vendorName: '',
|
||||||
'osVer': systemVersion,
|
osVer: systemVersion,
|
||||||
'vendorOsName': systemName,
|
vendorOsName: systemName,
|
||||||
'setMute': false,
|
setMute: false,
|
||||||
'vendorType': 0
|
vendorType: VendorType.KNOSETONIOS
|
||||||
},
|
},
|
||||||
'deviceConfig': '{"appearance":{"isSplitViewMode":true},"msg":{}}'
|
deviceConfig: '{"appearance":{"isSplitViewMode":true},"msg":{}}'
|
||||||
};
|
};
|
||||||
Object.assign(sessionConfig, config);
|
Object.assign(sessionConfig, config);
|
||||||
// log(sessionConfig);
|
|
||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
|
@@ -12,18 +12,16 @@ export interface NapCatConfig {
|
|||||||
consoleLogLevel: LogLevel,
|
consoleLogLevel: LogLevel,
|
||||||
}
|
}
|
||||||
|
|
||||||
class Config extends ConfigBase<NapCatConfig> implements NapCatConfig{
|
class Config extends ConfigBase<NapCatConfig> implements NapCatConfig {
|
||||||
|
name: string = 'napcat'
|
||||||
fileLog = true;
|
fileLog = true;
|
||||||
consoleLog = true;
|
consoleLog = true;
|
||||||
fileLogLevel = LogLevel.DEBUG;
|
fileLogLevel = LogLevel.DEBUG;
|
||||||
consoleLogLevel = LogLevel.INFO;
|
consoleLogLevel = LogLevel.INFO;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
getConfigPath() {
|
|
||||||
return path.join(this.getConfigDir(), `napcat_${selfInfo.uin}.json`);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const napCatConfig = new Config();
|
export const napCatConfig = new Config();
|
||||||
|
@@ -26,7 +26,6 @@ import {
|
|||||||
NodeIKernelRichMediaService,
|
NodeIKernelRichMediaService,
|
||||||
NodeIKernelAvatarService,
|
NodeIKernelAvatarService,
|
||||||
} from './services';
|
} from './services';
|
||||||
import { qqVersionConfigInfo } from '@/common/utils/QQBasicInfo';
|
|
||||||
import { NodeIKernelStorageCleanService } from './services/NodeIKernelStorageCleanService';
|
import { NodeIKernelStorageCleanService } from './services/NodeIKernelStorageCleanService';
|
||||||
import { NodeIKernelRobotService } from './services/NodeIKernelRobotService';
|
import { NodeIKernelRobotService } from './services/NodeIKernelRobotService';
|
||||||
import { dirname } from "node:path"
|
import { dirname } from "node:path"
|
||||||
@@ -39,6 +38,11 @@ import { NodeIKernelTianShuService } from './services/NodeIKernelTianShuService'
|
|||||||
import { NodeIKernelUnitedConfigService } from './services/NodeIKernelUnitedConfigService';
|
import { NodeIKernelUnitedConfigService } from './services/NodeIKernelUnitedConfigService';
|
||||||
import { NodeIKernelSearchService } from './services/NodeIKernelSearchService';
|
import { NodeIKernelSearchService } from './services/NodeIKernelSearchService';
|
||||||
import { NodeIKernelCollectionService } from './services/NodeIKernelCollectionService';
|
import { NodeIKernelCollectionService } from './services/NodeIKernelCollectionService';
|
||||||
|
import { NodeIKernelRecentContactService } from './services/NodeIKernelRecentContactService';
|
||||||
|
import { NodeIKernelMSFService } from './services/NodeIKernelMSFService';
|
||||||
|
import { NodeIkernelTestPerformanceService } from './services/NodeIkernelTestPerformanceService';
|
||||||
|
import { NodeIKernelECDHService } from './services/NodeIKernelECDHService';
|
||||||
|
import { getFullQQVesion } from '@/common/utils/QQBasicInfo';
|
||||||
|
|
||||||
|
|
||||||
const __filename = fileURLToPath(import.meta.url);
|
const __filename = fileURLToPath(import.meta.url);
|
||||||
@@ -154,6 +158,10 @@ export interface NodeIQQNTWrapperSession {
|
|||||||
|
|
||||||
startNT(): void;
|
startNT(): void;
|
||||||
|
|
||||||
|
getBdhUploadService(): unknown;
|
||||||
|
|
||||||
|
getECDHService(): NodeIKernelECDHService;
|
||||||
|
|
||||||
getMsgService(): NodeIKernelMsgService;
|
getMsgService(): NodeIKernelMsgService;
|
||||||
|
|
||||||
getProfileService(): NodeIKernelProfileService;
|
getProfileService(): NodeIKernelProfileService;
|
||||||
@@ -208,7 +216,7 @@ export interface NodeIQQNTWrapperSession {
|
|||||||
|
|
||||||
getSkinService(): unknown;
|
getSkinService(): unknown;
|
||||||
|
|
||||||
getTestPerformanceService(): unknown;
|
getTestPerformanceService(): NodeIkernelTestPerformanceService;
|
||||||
|
|
||||||
getQQPlayService(): unknown;
|
getQQPlayService(): unknown;
|
||||||
|
|
||||||
@@ -230,13 +238,13 @@ export interface NodeIQQNTWrapperSession {
|
|||||||
|
|
||||||
getLockService(): unknown;
|
getLockService(): unknown;
|
||||||
|
|
||||||
getMSFService(): unknown
|
getMSFService(): NodeIKernelMSFService;
|
||||||
|
|
||||||
getGuildHotUpdateService(): unknown;
|
getGuildHotUpdateService(): unknown;
|
||||||
|
|
||||||
getAVSDKService(): unknown;
|
getAVSDKService(): unknown;
|
||||||
|
|
||||||
getRecentContactService(): unknown;
|
getRecentContactService(): NodeIKernelRecentContactService;
|
||||||
|
|
||||||
getConfigMgrService(): unknown;
|
getConfigMgrService(): unknown;
|
||||||
}
|
}
|
||||||
@@ -284,7 +292,7 @@ export interface WrapperNodeApi {
|
|||||||
|
|
||||||
let wrapperNodePath = path.resolve(path.dirname(process.execPath), './resources/app/wrapper.node');
|
let wrapperNodePath = path.resolve(path.dirname(process.execPath), './resources/app/wrapper.node');
|
||||||
if (!fs.existsSync(wrapperNodePath)) {
|
if (!fs.existsSync(wrapperNodePath)) {
|
||||||
wrapperNodePath = path.join(path.dirname(process.execPath), `resources/app/versions/${qqVersionConfigInfo.curVersion}/wrapper.node`);
|
wrapperNodePath = path.join(path.dirname(process.execPath), `resources/app/versions/${getFullQQVesion()}/wrapper.node`);
|
||||||
}
|
}
|
||||||
const nativemodule: any = { exports: {} };
|
const nativemodule: any = { exports: {} };
|
||||||
process.dlopen(nativemodule, wrapperNodePath);
|
process.dlopen(nativemodule, wrapperNodePath);
|
||||||
|
30
src/index.ts
30
src/index.ts
@@ -13,13 +13,15 @@ import { deleteOldFiles, UpdateConfig } from './common/utils/helper';
|
|||||||
import { dirname } from 'node:path';
|
import { dirname } from 'node:path';
|
||||||
import { fileURLToPath } from 'node:url';
|
import { fileURLToPath } from 'node:url';
|
||||||
import chalk from 'chalk';
|
import chalk from 'chalk';
|
||||||
|
import { randomInt } from 'crypto';
|
||||||
|
import { MessageUnique } from './common/utils/MessageUnique';
|
||||||
|
|
||||||
|
|
||||||
const __filename = fileURLToPath(import.meta.url);
|
const __filename = fileURLToPath(import.meta.url);
|
||||||
const __dirname = dirname(__filename);
|
const __dirname = dirname(__filename);
|
||||||
const tagColor = chalk.cyan;
|
const tagColor = chalk.cyan;
|
||||||
program
|
program
|
||||||
.option('-q, --qq <type>', 'QQ号')
|
.option('-q, --qq [type]', 'QQ号')
|
||||||
.parse(process.argv);
|
.parse(process.argv);
|
||||||
|
|
||||||
//deleteOldFiles(path.join(__dirname, 'logs'), 3).then().catch();
|
//deleteOldFiles(path.join(__dirname, 'logs'), 3).then().catch();
|
||||||
@@ -67,7 +69,18 @@ const showQRCode = async (url: string, base64: string, buffer: Buffer) => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
const quickLoginQQ = cmdOptions.qq;
|
|
||||||
|
let quickLoginQQ = cmdOptions.qq; // undefine、true、string
|
||||||
|
const QuickLoginList = await napCatCore.getQuickLoginList();
|
||||||
|
if (quickLoginQQ == true) {
|
||||||
|
if (QuickLoginList.LocalLoginInfoList.length > 0) {
|
||||||
|
quickLoginQQ = QuickLoginList.LocalLoginInfoList[0].uin;
|
||||||
|
log('-q 指令指定使用最近的QQ进行快速登录');
|
||||||
|
} else {
|
||||||
|
quickLoginQQ = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// napCatCore.on('system.login.error', (result) => {
|
// napCatCore.on('system.login.error', (result) => {
|
||||||
// console.error('登录失败', result);
|
// console.error('登录失败', result);
|
||||||
// napCatCore.qrLogin().then().catch(console.error);
|
// napCatCore.qrLogin().then().catch(console.error);
|
||||||
@@ -79,9 +92,9 @@ napCatCore.getQuickLoginList().then((res) => {
|
|||||||
|
|
||||||
WebUiDataRuntime.setQQQuickLoginCall(async (uin: string) => {
|
WebUiDataRuntime.setQQQuickLoginCall(async (uin: string) => {
|
||||||
const QuickLogin: Promise<{ result: boolean, message: string }> = new Promise((resolve, reject) => {
|
const QuickLogin: Promise<{ result: boolean, message: string }> = new Promise((resolve, reject) => {
|
||||||
if (quickLoginQQ) {
|
if (uin) {
|
||||||
log('正在快速登录 ', quickLoginQQ);
|
log('正在快速登录 ', uin);
|
||||||
napCatCore.quickLogin(quickLoginQQ).then(res => {
|
napCatCore.quickLogin(uin).then(res => {
|
||||||
if (res.loginErrorInfo.errMsg) {
|
if (res.loginErrorInfo.errMsg) {
|
||||||
resolve({ result: false, message: res.loginErrorInfo.errMsg });
|
resolve({ result: false, message: res.loginErrorInfo.errMsg });
|
||||||
}
|
}
|
||||||
@@ -105,11 +118,14 @@ if (quickLoginQQ) {
|
|||||||
logError('快速登录错误:', res.loginErrorInfo.errMsg);
|
logError('快速登录错误:', res.loginErrorInfo.errMsg);
|
||||||
}
|
}
|
||||||
}).catch((e) => {
|
}).catch((e) => {
|
||||||
logError(e);
|
logError('快速登录错误:', e);
|
||||||
napCatCore.qrLogin(showQRCode);
|
napCatCore.qrLogin(showQRCode);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
log('没有 -q 参数指定快速登录的QQ,将使用二维码登录方式');
|
log('没有 -q 指令指定快速登录,将使用二维码登录方式');
|
||||||
|
if (QuickLoginList.LocalLoginInfoList.length > 0) {
|
||||||
|
log(`可用于快速登录的QQ:${QuickLoginList.LocalLoginInfoList.map((u, index) => `\n${index}: ${u.uin} ${u.nickName}`)}`);
|
||||||
|
}
|
||||||
napCatCore.qrLogin(showQRCode);
|
napCatCore.qrLogin(showQRCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -15,6 +15,6 @@ export class SetConfigAction extends BaseAction<OB11Config, void> {
|
|||||||
actionName = ActionName.SetConfig;
|
actionName = ActionName.SetConfig;
|
||||||
|
|
||||||
protected async _handle(payload: OB11Config): Promise<void> {
|
protected async _handle(payload: OB11Config): Promise<void> {
|
||||||
ob11Config.save(payload);
|
ob11Config.save(payload, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
21
src/onebot11/action/extends/FetchCustomFace.ts
Normal file
21
src/onebot11/action/extends/FetchCustomFace.ts
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||||
|
import BaseAction from '../BaseAction';
|
||||||
|
import { ActionName } from '../types';
|
||||||
|
import { NTQQMsgApi } from '@/core/apis';
|
||||||
|
const SchemaData = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
count: { type: 'number' },
|
||||||
|
}
|
||||||
|
} as const satisfies JSONSchema;
|
||||||
|
|
||||||
|
type Payload = FromSchema<typeof SchemaData>;
|
||||||
|
|
||||||
|
export class FetchCustomFace extends BaseAction<Payload, string[]> {
|
||||||
|
actionName = ActionName.FetchCustomFace;
|
||||||
|
PayloadSchema = SchemaData;
|
||||||
|
protected async _handle(payload: Payload) {
|
||||||
|
const ret = await NTQQMsgApi.fetchFavEmojiList(payload.count || 48);
|
||||||
|
return ret.emojiInfoList.map(e => e.url);
|
||||||
|
}
|
||||||
|
}
|
32
src/onebot11/action/extends/FetchEmojioLike.ts
Normal file
32
src/onebot11/action/extends/FetchEmojioLike.ts
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
//getMsgEmojiLikesList
|
||||||
|
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||||
|
import BaseAction from '../BaseAction';
|
||||||
|
import { ActionName } from '../types';
|
||||||
|
import { NTQQMsgApi } from '@/core/apis';
|
||||||
|
import { MessageUnique } from '@/common/utils/MessageUnique';
|
||||||
|
const SchemaData = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
user_id: { type: 'string' },
|
||||||
|
group_id: { type: 'string' },
|
||||||
|
emojiId: { type: 'string' },
|
||||||
|
emojiType: { type: 'string' },
|
||||||
|
message_id: { type: ['string', 'number'] },
|
||||||
|
count: { type: 'number' }
|
||||||
|
},
|
||||||
|
required: ['emojiId', 'emojiType', 'message_id']
|
||||||
|
} as const satisfies JSONSchema;
|
||||||
|
|
||||||
|
type Payload = FromSchema<typeof SchemaData>;
|
||||||
|
|
||||||
|
export class FetchEmojioLike extends BaseAction<Payload, any> {
|
||||||
|
actionName = ActionName.FetchEmojioLike;
|
||||||
|
PayloadSchema = SchemaData;
|
||||||
|
protected async _handle(payload: Payload) {
|
||||||
|
let msgIdPeer = MessageUnique.getMsgIdAndPeerByShortId(parseInt(payload.message_id.toString()));
|
||||||
|
if(!msgIdPeer) throw new Error('消息不存在');
|
||||||
|
let msg = (await NTQQMsgApi.getMsgsByMsgId(msgIdPeer.Peer, [msgIdPeer.MsgId])).msgList[0];
|
||||||
|
const ret = await NTQQMsgApi.getMsgEmojiLikesList(msgIdPeer.Peer,msg.msgSeq,payload.emojiId,payload.emojiType,payload.count);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
@@ -1,12 +1,19 @@
|
|||||||
import { rawFriends } from '@/core/data';
|
import { requireMinNTQQBuild } from '@/common/utils/QQBasicInfo';
|
||||||
import BaseAction from '../BaseAction';
|
import BaseAction from '../BaseAction';
|
||||||
import { ActionName } from '../types';
|
import { ActionName } from '../types';
|
||||||
import { BuddyCategoryType } from '@/core/entities/';
|
import { BuddyCategoryType } from '@/core/entities/';
|
||||||
|
import { NTQQFriendApi } from '@/core';
|
||||||
|
import { OB11Constructor } from '@/onebot11/constructor';
|
||||||
|
|
||||||
export class GetFriendWithCategory extends BaseAction<void, Array<BuddyCategoryType>> {
|
export class GetFriendWithCategory extends BaseAction<void, any> {
|
||||||
actionName = ActionName.GetFriendsWithCategory;
|
actionName = ActionName.GetFriendsWithCategory;
|
||||||
|
|
||||||
protected async _handle(payload: void) {
|
protected async _handle(payload: void) {
|
||||||
return rawFriends;
|
if (requireMinNTQQBuild('26702')) {
|
||||||
|
//全新逻辑
|
||||||
|
return OB11Constructor.friendsV2(await NTQQFriendApi.getBuddyV2ExWithCate(true));
|
||||||
|
} else {
|
||||||
|
throw new Error('this ntqq version not support, must be 26702 or later');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
16
src/onebot11/action/extends/GetProfileLike.ts
Normal file
16
src/onebot11/action/extends/GetProfileLike.ts
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import { selfInfo } from '@/core/data';
|
||||||
|
import BaseAction from '../BaseAction';
|
||||||
|
import { ActionName } from '../types';
|
||||||
|
import { NTQQUserApi } from '@/core/apis';
|
||||||
|
|
||||||
|
export class GetProfileLike extends BaseAction<void, any> {
|
||||||
|
actionName = ActionName.GetProfileLike;
|
||||||
|
protected async _handle(payload: void) {
|
||||||
|
const ret = await NTQQUserApi.getProfileLike(selfInfo.uid);
|
||||||
|
const listdata: any[] = ret.info.userLikeInfos[0].favoriteInfo.userInfos;
|
||||||
|
for (let i = 0; i < listdata.length; i++) {
|
||||||
|
listdata[i].uin = parseInt((await NTQQUserApi.getUinByUid(listdata[i].uid)) || '');
|
||||||
|
}
|
||||||
|
return listdata;
|
||||||
|
}
|
||||||
|
}
|
57
src/onebot11/action/extends/SetGroupHeader.ts
Normal file
57
src/onebot11/action/extends/SetGroupHeader.ts
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
import BaseAction from '../BaseAction';
|
||||||
|
import { ActionName, BaseCheckResult } from '../types';
|
||||||
|
import * as fs from 'node:fs';
|
||||||
|
import { NTQQUserApi } from '@/core/apis/user';
|
||||||
|
import { checkFileReceived, uri2local } from '@/common/utils/file';
|
||||||
|
import { NTQQGroupApi } from '@/core';
|
||||||
|
// import { log } from "../../../common/utils";
|
||||||
|
|
||||||
|
interface Payload {
|
||||||
|
file: string,
|
||||||
|
groupCode: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class SetGroupHeader extends BaseAction<Payload, any> {
|
||||||
|
actionName = ActionName.SetGroupHeader;
|
||||||
|
// 用不着复杂检测
|
||||||
|
protected async check(payload: Payload): Promise<BaseCheckResult> {
|
||||||
|
if (!payload.file || typeof payload.file != 'string' || !payload.groupCode || typeof payload.groupCode != 'string') {
|
||||||
|
return {
|
||||||
|
valid: false,
|
||||||
|
message: 'file和groupCode字段不能为空或者类型错误',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
valid: true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
protected async _handle(payload: Payload): Promise<any> {
|
||||||
|
const { path, isLocal, errMsg } = (await uri2local(payload.file));
|
||||||
|
if (errMsg) {
|
||||||
|
throw `头像${payload.file}设置失败,file字段可能格式不正确`;
|
||||||
|
}
|
||||||
|
if (path) {
|
||||||
|
await checkFileReceived(path, 5000); // 文件不存在QQ会崩溃,需要提前判断
|
||||||
|
const ret = await NTQQGroupApi.setGroupAvatar(payload.groupCode,path);
|
||||||
|
if (!isLocal) {
|
||||||
|
fs.unlink(path, () => { });
|
||||||
|
}
|
||||||
|
if (!ret) {
|
||||||
|
throw `头像${payload.file}设置失败,api无返回`;
|
||||||
|
}
|
||||||
|
// log(`头像设置返回:${JSON.stringify(ret)}`)
|
||||||
|
// if (ret['result'] == 1004022) {
|
||||||
|
// throw `头像${payload.file}设置失败,文件可能不是图片格式`;
|
||||||
|
// } else if (ret['result'] != 0) {
|
||||||
|
// throw `头像${payload.file}设置失败,未知的错误,${ret['result']}:${ret['errMsg']}`;
|
||||||
|
// }
|
||||||
|
return ret;
|
||||||
|
} else {
|
||||||
|
if (!isLocal) {
|
||||||
|
fs.unlink(path, () => { });
|
||||||
|
}
|
||||||
|
throw `头像${payload.file}设置失败,无法获取头像,文件可能不存在`;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
32
src/onebot11/action/extends/TestApi01.ts
Normal file
32
src/onebot11/action/extends/TestApi01.ts
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
import BaseAction from '../BaseAction';
|
||||||
|
import { ActionName, BaseCheckResult } from '../types';
|
||||||
|
import * as fs from 'node:fs';
|
||||||
|
import { NTQQUserApi } from '@/core/apis/user';
|
||||||
|
import { checkFileReceived, uri2local } from '@/common/utils/file';
|
||||||
|
import { napCatCore, NTQQGroupApi } from '@/core';
|
||||||
|
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||||
|
// import { log } from "../../../common/utils";
|
||||||
|
|
||||||
|
const SchemaData = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
cmd: { type: 'string' },
|
||||||
|
param: { type: 'string' }
|
||||||
|
},
|
||||||
|
required: ['cmd', 'param'],
|
||||||
|
} as const satisfies JSONSchema;
|
||||||
|
|
||||||
|
type Payload = FromSchema<typeof SchemaData>;
|
||||||
|
|
||||||
|
export default class TestApi01 extends BaseAction<Payload, any> {
|
||||||
|
actionName = ActionName.TestApi01;
|
||||||
|
// 用不着复杂检测
|
||||||
|
protected async check(payload: Payload): Promise<BaseCheckResult> {
|
||||||
|
return {
|
||||||
|
valid: true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
protected async _handle(payload: Payload): Promise<any> {
|
||||||
|
return await napCatCore.session.getMsgService().sendSsoCmdReqByContend(payload.cmd, payload.param);
|
||||||
|
}
|
||||||
|
}
|
@@ -1,15 +1,12 @@
|
|||||||
import BaseAction from '../BaseAction';
|
import BaseAction from '../BaseAction';
|
||||||
import fs from 'fs/promises';
|
import fs from 'fs/promises';
|
||||||
import { dbUtil } from '@/common/utils/db';
|
|
||||||
import { ob11Config } from '@/onebot11/config';
|
import { ob11Config } from '@/onebot11/config';
|
||||||
import { log, logDebug } from '@/common/utils/log';
|
import { UUIDConverter } from '@/common/utils/helper';
|
||||||
import { sleep } from '@/common/utils/helper';
|
|
||||||
import { uri2local } from '@/common/utils/file';
|
|
||||||
import { ActionName, BaseCheckResult } from '../types';
|
import { ActionName, BaseCheckResult } from '../types';
|
||||||
import { FileElement, RawMessage, VideoElement } from '@/core/entities';
|
import { ChatType, ElementType, FileElement, Peer, RawMessage, VideoElement } from '@/core/entities';
|
||||||
import { NTQQFileApi } from '@/core/apis';
|
import { NTQQFileApi, NTQQFriendApi, NTQQMsgApi, NTQQUserApi } from '@/core/apis';
|
||||||
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||||
import Ajv from 'ajv';
|
import { getGroup } from '@/core/data';
|
||||||
|
|
||||||
export interface GetFilePayload {
|
export interface GetFilePayload {
|
||||||
file: string; // 文件名或者fileUuid
|
file: string; // 文件名或者fileUuid
|
||||||
@@ -45,69 +42,163 @@ export class GetFileBase extends BaseAction<GetFilePayload, GetFileResponse> {
|
|||||||
return { id: element.elementId, element: element.fileElement };
|
return { id: element.elementId, element: element.fileElement };
|
||||||
}
|
}
|
||||||
protected async _handle(payload: GetFilePayload): Promise<GetFileResponse> {
|
protected async _handle(payload: GetFilePayload): Promise<GetFileResponse> {
|
||||||
let cache = await dbUtil.getFileCacheByName(payload.file);
|
|
||||||
if (!cache) {
|
|
||||||
cache = await dbUtil.getFileCacheByUuid(payload.file);
|
|
||||||
}
|
|
||||||
if (!cache) {
|
|
||||||
throw new Error('file not found');
|
|
||||||
}
|
|
||||||
const { enableLocalFile2Url } = ob11Config;
|
const { enableLocalFile2Url } = ob11Config;
|
||||||
|
let UuidData: {
|
||||||
|
high: string;
|
||||||
|
low: string;
|
||||||
|
} | undefined;
|
||||||
try {
|
try {
|
||||||
await fs.access(cache.path, fs.constants.F_OK);
|
UuidData = UUIDConverter.decode(payload.file);
|
||||||
} catch (e) {
|
if (UuidData) {
|
||||||
logDebug('local file not found, start download...');
|
const peerUin = UuidData.high;
|
||||||
// if (cache.url) {
|
const msgId = UuidData.low;
|
||||||
// const downloadResult = await uri2local(cache.url);
|
const isGroup = await getGroup(peerUin);
|
||||||
// if (downloadResult.success) {
|
let peer: Peer | undefined;
|
||||||
// cache.path = downloadResult.path;
|
//识别Peer
|
||||||
// dbUtil.updateFileCache(cache).then();
|
if (isGroup) {
|
||||||
// } else {
|
peer = { chatType: ChatType.group, peerUid: peerUin };
|
||||||
// throw new Error('file download failed. ' + downloadResult.errMsg);
|
}
|
||||||
// }
|
const PeerUid = await NTQQUserApi.getUidByUin(peerUin);
|
||||||
// } else {
|
if (PeerUid) {
|
||||||
// // 没有url的可能是私聊文件或者群文件,需要自己下载
|
const isBuddy = await NTQQFriendApi.isBuddy(PeerUid);
|
||||||
// log('需要调用 NTQQ 下载文件api');
|
if (isBuddy) {
|
||||||
let msg = await dbUtil.getMsgByLongId(cache.msgId);
|
peer = { chatType: ChatType.friend, peerUid: PeerUid };
|
||||||
// log('文件 msg', msg);
|
} else {
|
||||||
if (msg) {
|
peer = { chatType: ChatType.temp, peerUid: PeerUid };
|
||||||
// 构建下载函数
|
}
|
||||||
const downloadPath = await NTQQFileApi.downloadMedia(msg.msgId, msg.chatType, msg.peerUid,
|
}
|
||||||
cache.elementId, '', '');
|
if (!peer) {
|
||||||
// await sleep(1000);
|
throw new Error('chattype not support');
|
||||||
|
}
|
||||||
// log('download result', downloadPath);
|
const msgList = await NTQQMsgApi.getMsgsByMsgId(peer, [msgId]);
|
||||||
msg = await dbUtil.getMsgByLongId(cache.msgId);
|
if (msgList.msgList.length == 0) {
|
||||||
// log('下载完成后的msg', msg);
|
throw new Error('msg not found');
|
||||||
cache.path = downloadPath!;
|
}
|
||||||
dbUtil.updateFileCache(cache).then();
|
const msg = msgList.msgList[0];
|
||||||
// log('下载完成后的msg', msg);
|
const findEle = msg.elements.find(e => e.elementType == ElementType.VIDEO || e.elementType == ElementType.FILE || e.elementType == ElementType.PTT);
|
||||||
// }
|
if (!findEle) {
|
||||||
|
throw new Error('element not found');
|
||||||
|
}
|
||||||
|
const downloadPath = await NTQQFileApi.downloadMedia(msgId, msg.chatType, msg.peerUid, findEle.elementId, '', '');
|
||||||
|
const fileSize = findEle?.videoElement?.fileSize || findEle?.fileElement?.fileSize || findEle?.pttElement?.fileSize || '0';
|
||||||
|
const fileName = findEle?.videoElement?.fileName || findEle?.fileElement?.fileName || findEle?.pttElement?.fileName || '';
|
||||||
|
const res: GetFileResponse = {
|
||||||
|
file: downloadPath,
|
||||||
|
url: downloadPath,
|
||||||
|
file_size: fileSize,
|
||||||
|
file_name: fileName
|
||||||
|
};
|
||||||
|
if (enableLocalFile2Url) {
|
||||||
|
try {
|
||||||
|
res.base64 = await fs.readFile(downloadPath, 'base64');
|
||||||
|
} catch (e) {
|
||||||
|
throw new Error('文件下载失败. ' + e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//不手动删除?文件持久化了
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
|
} catch {
|
||||||
|
|
||||||
}
|
}
|
||||||
// log('file found', cache);
|
|
||||||
const res: GetFileResponse = {
|
const NTSearchNameResult = (await NTQQFileApi.searchfile([payload.file])).resultItems;
|
||||||
file: cache.path,
|
if (NTSearchNameResult.length !== 0) {
|
||||||
url: cache.url,
|
const MsgId = NTSearchNameResult[0].msgId;
|
||||||
file_size: cache.size.toString(),
|
let peer: Peer | undefined = undefined;
|
||||||
file_name: cache.name
|
if (NTSearchNameResult[0].chatType == ChatType.group) {
|
||||||
};
|
peer = { chatType: ChatType.group, peerUid: NTSearchNameResult[0].groupChatInfo[0].groupCode };
|
||||||
if (enableLocalFile2Url) {
|
}
|
||||||
if (!cache.url) {
|
if (!peer) {
|
||||||
|
throw new Error('chattype not support');
|
||||||
|
}
|
||||||
|
const msgList: RawMessage[] = (await NTQQMsgApi.getMsgsByMsgId(peer, [MsgId]))?.msgList;
|
||||||
|
if (!msgList || msgList.length == 0) {
|
||||||
|
throw new Error('msg not found');
|
||||||
|
}
|
||||||
|
const msg = msgList[0];
|
||||||
|
const file = msg.elements.filter(e => e.elementType == NTSearchNameResult[0].elemType);
|
||||||
|
if (file.length == 0) {
|
||||||
|
throw new Error('file not found');
|
||||||
|
}
|
||||||
|
const downloadPath = await NTQQFileApi.downloadMedia(msg.msgId, msg.chatType, msg.peerUid, file[0].elementId, '', '');
|
||||||
|
const res: GetFileResponse = {
|
||||||
|
file: downloadPath,
|
||||||
|
url: downloadPath,
|
||||||
|
file_size: NTSearchNameResult[0].fileSize.toString(),
|
||||||
|
file_name: NTSearchNameResult[0].fileName
|
||||||
|
};
|
||||||
|
if (enableLocalFile2Url) {
|
||||||
try {
|
try {
|
||||||
res.base64 = await fs.readFile(cache.path, 'base64');
|
res.base64 = await fs.readFile(downloadPath, 'base64');
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
throw new Error('文件下载失败. ' + e);
|
throw new Error('文件下载失败. ' + e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
//不手动删除?文件持久化了
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
// if (autoDeleteFile) {
|
throw new Error('file not found');
|
||||||
// setTimeout(() => {
|
// let cache = await dbUtil.getFileCacheByName(payload.file);
|
||||||
// fs.unlink(cache.filePath)
|
// if (!cache) {
|
||||||
// }, autoDeleteFileSecond * 1000)
|
// cache = await dbUtil.getFileCacheByUuid(payload.file);
|
||||||
// }
|
// }
|
||||||
return res;
|
// if (!cache) {
|
||||||
|
// throw new Error('file not found');
|
||||||
|
// }
|
||||||
|
// const { enableLocalFile2Url } = ob11Config;
|
||||||
|
// try {
|
||||||
|
// await fs.access(cache.path, fs.constants.F_OK);
|
||||||
|
// } catch (e) {
|
||||||
|
// logDebug('local file not found, start download...');
|
||||||
|
// // if (cache.url) {
|
||||||
|
// // const downloadResult = await uri2local(cache.url);
|
||||||
|
// // if (downloadResult.success) {
|
||||||
|
// // cache.path = downloadResult.path;
|
||||||
|
// // dbUtil.updateFileCache(cache).then();
|
||||||
|
// // } else {
|
||||||
|
// // throw new Error('file download failed. ' + downloadResult.errMsg);
|
||||||
|
// // }
|
||||||
|
// // } else {
|
||||||
|
// // // 没有url的可能是私聊文件或者群文件,需要自己下载
|
||||||
|
// // log('需要调用 NTQQ 下载文件api');
|
||||||
|
// let peer = MessageUnique.getPeerByMsgId(cache.msgId);
|
||||||
|
// let msg = await NTQQMsgApi.getMsgsByMsgId(peer?.Peer!,cache.msgId);
|
||||||
|
// // log('文件 msg', msg);
|
||||||
|
// if (msg) {
|
||||||
|
// // 构建下载函数
|
||||||
|
// const downloadPath = await NTQQFileApi.downloadMedia(msg.msgId, msg.chatType, msg.peerUid,
|
||||||
|
// cache.elementId, '', '');
|
||||||
|
// // await sleep(1000);
|
||||||
|
|
||||||
|
// // log('download result', downloadPath);
|
||||||
|
// let peer = MessageUnique.getPeerByMsgId(cache.msgId);
|
||||||
|
// msg = await NTQQMsgApi.getMsgsByMsgId(peer?.Peer!,cache.msgId);
|
||||||
|
// // log('下载完成后的msg', msg);
|
||||||
|
// cache.path = downloadPath!;
|
||||||
|
// dbUtil.updateFileCache(cache).then();
|
||||||
|
// // log('下载完成后的msg', msg);
|
||||||
|
// // }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// }
|
||||||
|
// // log('file found', cache);
|
||||||
|
// const res: GetFileResponse = {
|
||||||
|
// file: cache.path,
|
||||||
|
// url: cache.url,
|
||||||
|
// file_size: cache.size.toString(),
|
||||||
|
// file_name: cache.name
|
||||||
|
// };
|
||||||
|
// if (enableLocalFile2Url) {
|
||||||
|
// if (!cache.url) {
|
||||||
|
// try {
|
||||||
|
// res.base64 = await fs.readFile(cache.path, 'base64');
|
||||||
|
// } catch (e) {
|
||||||
|
// throw new Error('文件下载失败. ' + e);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//return res;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,11 +1,10 @@
|
|||||||
import BaseAction from '../BaseAction';
|
import BaseAction from '../BaseAction';
|
||||||
import { ActionName, BaseCheckResult } from '../types';
|
import { ActionName } from '../types';
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import { join as joinPath } from 'node:path';
|
import { join as joinPath } from 'node:path';
|
||||||
import { calculateFileMD5, getTempDir, httpDownload } from '@/common/utils/file';
|
import { calculateFileMD5, getTempDir, httpDownload } from '@/common/utils/file';
|
||||||
import { v4 as uuid4 } from 'uuid';
|
import { randomUUID } from 'crypto';
|
||||||
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||||
import Ajv from 'ajv';
|
|
||||||
interface FileResponse {
|
interface FileResponse {
|
||||||
file: string;
|
file: string;
|
||||||
}
|
}
|
||||||
@@ -32,7 +31,7 @@ export default class GoCQHTTPDownloadFile extends BaseAction<Payload, FileRespon
|
|||||||
PayloadSchema = SchemaData;
|
PayloadSchema = SchemaData;
|
||||||
protected async _handle(payload: Payload): Promise<FileResponse> {
|
protected async _handle(payload: Payload): Promise<FileResponse> {
|
||||||
const isRandomName = !payload.name;
|
const isRandomName = !payload.name;
|
||||||
const name = payload.name || uuid4();
|
const name = payload.name || randomUUID();
|
||||||
const filePath = joinPath(getTempDir(), name);
|
const filePath = joinPath(getTempDir(), name);
|
||||||
|
|
||||||
if (payload.base64) {
|
if (payload.base64) {
|
||||||
|
@@ -1,11 +1,10 @@
|
|||||||
import BaseAction from '../BaseAction';
|
import BaseAction from '../BaseAction';
|
||||||
import { OB11ForwardMessage, OB11Message, OB11MessageData } from '../../types';
|
import { OB11ForwardMessage, OB11Message, OB11MessageData } from '../../types';
|
||||||
import { NTQQMsgApi } from '@/core/apis';
|
import { NTQQMsgApi } from '@/core/apis';
|
||||||
import { dbUtil } from '@/common/utils/db';
|
|
||||||
import { OB11Constructor } from '../../constructor';
|
import { OB11Constructor } from '../../constructor';
|
||||||
import { ActionName, BaseCheckResult } from '../types';
|
import { ActionName, BaseCheckResult } from '../types';
|
||||||
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||||
import Ajv from 'ajv';
|
import { MessageUnique } from '@/common/utils/MessageUnique';
|
||||||
|
|
||||||
const SchemaData = {
|
const SchemaData = {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
@@ -29,24 +28,19 @@ export class GoCQHTTPGetForwardMsgAction extends BaseAction<Payload, any> {
|
|||||||
if (!msgId) {
|
if (!msgId) {
|
||||||
throw Error('message_id is required');
|
throw Error('message_id is required');
|
||||||
}
|
}
|
||||||
let rootMsg = await dbUtil.getMsgByLongId(msgId);
|
const rootMsgId = MessageUnique.getShortIdByMsgId(msgId);
|
||||||
|
const rootMsg = MessageUnique.getMsgIdAndPeerByShortId(rootMsgId || parseInt(msgId));
|
||||||
if (!rootMsg) {
|
if (!rootMsg) {
|
||||||
rootMsg = await dbUtil.getMsgByShortId(parseInt(msgId));
|
throw Error('msg not found');
|
||||||
if (!rootMsg) {
|
|
||||||
throw Error('msg not found');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
const data = await NTQQMsgApi.getMultiMsg({
|
const data = await NTQQMsgApi.getMultiMsg(rootMsg.Peer, rootMsg.MsgId, rootMsg.MsgId);
|
||||||
chatType: rootMsg.chatType,
|
|
||||||
peerUid: rootMsg.peerUid
|
|
||||||
}, rootMsg.msgId, rootMsg.msgId);
|
|
||||||
if (!data || data.result !== 0) {
|
if (!data || data.result !== 0) {
|
||||||
throw Error('找不到相关的聊天记录' + data?.errMsg);
|
throw Error('找不到相关的聊天记录' + data?.errMsg);
|
||||||
}
|
}
|
||||||
const msgList = data.msgList;
|
const msgList = data.msgList;
|
||||||
const messages = await Promise.all(msgList.map(async msg => {
|
const messages = await Promise.all(msgList.map(async msg => {
|
||||||
const resMsg = await OB11Constructor.message(msg);
|
const resMsg = await OB11Constructor.message(msg);
|
||||||
resMsg.message_id = await dbUtil.addMsg(msg);
|
resMsg.message_id = await MessageUnique.createMsg({ guildId:'',chatType:msg.chatType,peerUid:msg.peerUid },msg.msgId)!;
|
||||||
return resMsg;
|
return resMsg;
|
||||||
}));
|
}));
|
||||||
messages.map(msg => {
|
messages.map(msg => {
|
||||||
|
@@ -2,11 +2,11 @@ import BaseAction from '../BaseAction';
|
|||||||
import { OB11Message, OB11User } from '../../types';
|
import { OB11Message, OB11User } from '../../types';
|
||||||
import { ActionName } from '../types';
|
import { ActionName } from '../types';
|
||||||
import { ChatType } from '@/core/entities';
|
import { ChatType } from '@/core/entities';
|
||||||
import { dbUtil } from '@/common/utils/db';
|
|
||||||
import { NTQQMsgApi } from '@/core/apis/msg';
|
import { NTQQMsgApi } from '@/core/apis/msg';
|
||||||
import { OB11Constructor } from '../../constructor';
|
import { OB11Constructor } from '../../constructor';
|
||||||
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||||
import { NTQQFriendApi, NTQQUserApi } from '@/core';
|
import { NTQQFriendApi, NTQQUserApi } from '@/core';
|
||||||
|
import { MessageUnique } from '@/common/utils/MessageUnique';
|
||||||
|
|
||||||
interface Response {
|
interface Response {
|
||||||
messages: OB11Message[];
|
messages: OB11Message[];
|
||||||
@@ -32,7 +32,7 @@ export default class GetFriendMsgHistory extends BaseAction<Payload, Response> {
|
|||||||
if (!uid) {
|
if (!uid) {
|
||||||
throw `记录${payload.user_id}不存在`;
|
throw `记录${payload.user_id}不存在`;
|
||||||
}
|
}
|
||||||
const startMsgId = (await dbUtil.getMsgByShortId(payload.message_seq))?.msgId || '0';
|
const startMsgId = (await MessageUnique.getMsgIdAndPeerByShortId(payload.message_seq))?.MsgId || '0';
|
||||||
const friend = await NTQQFriendApi.isBuddy(uid);
|
const friend = await NTQQFriendApi.isBuddy(uid);
|
||||||
const historyResult = (await NTQQMsgApi.getMsgHistory({
|
const historyResult = (await NTQQMsgApi.getMsgHistory({
|
||||||
chatType: friend ? ChatType.friend : ChatType.temp,
|
chatType: friend ? ChatType.friend : ChatType.temp,
|
||||||
@@ -41,7 +41,7 @@ export default class GetFriendMsgHistory extends BaseAction<Payload, Response> {
|
|||||||
//logDebug(historyResult);
|
//logDebug(historyResult);
|
||||||
const msgList = historyResult.msgList;
|
const msgList = historyResult.msgList;
|
||||||
await Promise.all(msgList.map(async msg => {
|
await Promise.all(msgList.map(async msg => {
|
||||||
msg.id = await dbUtil.addMsg(msg);
|
msg.id = await MessageUnique.createMsg({ guildId: '', chatType: msg.chatType, peerUid: msg.peerUid }, msg.msgId);
|
||||||
}));
|
}));
|
||||||
const ob11MsgList = await Promise.all(msgList.map(msg => OB11Constructor.message(msg)));
|
const ob11MsgList = await Promise.all(msgList.map(msg => OB11Constructor.message(msg)));
|
||||||
return { 'messages': ob11MsgList };
|
return { 'messages': ob11MsgList };
|
||||||
|
@@ -2,12 +2,11 @@ import BaseAction from '../BaseAction';
|
|||||||
import { OB11Message, OB11User } from '../../types';
|
import { OB11Message, OB11User } from '../../types';
|
||||||
import { getGroup, groups } from '@/core/data';
|
import { getGroup, groups } from '@/core/data';
|
||||||
import { ActionName } from '../types';
|
import { ActionName } from '../types';
|
||||||
import { ChatType } from '@/core/entities';
|
import { ChatType, RawMessage } from '@/core/entities';
|
||||||
import { dbUtil } from '@/common/utils/db';
|
|
||||||
import { NTQQMsgApi } from '@/core/apis/msg';
|
import { NTQQMsgApi } from '@/core/apis/msg';
|
||||||
import { OB11Constructor } from '../../constructor';
|
import { OB11Constructor } from '../../constructor';
|
||||||
import { logDebug } from '@/common/utils/log';
|
|
||||||
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||||
|
import { MessageUnique } from '@/common/utils/MessageUnique';
|
||||||
interface Response {
|
interface Response {
|
||||||
messages: OB11Message[];
|
messages: OB11Message[];
|
||||||
}
|
}
|
||||||
@@ -15,11 +14,11 @@ interface Response {
|
|||||||
const SchemaData = {
|
const SchemaData = {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
properties: {
|
properties: {
|
||||||
group_id: { type: [ 'number' , 'string' ] },
|
group_id: { type: ['number', 'string'] },
|
||||||
message_seq: { type: 'number' },
|
message_seq: { type: 'number' },
|
||||||
count: { type: 'number' }
|
count: { type: 'number' }
|
||||||
},
|
},
|
||||||
required: ['group_id', 'message_seq', 'count']
|
required: ['group_id']
|
||||||
} as const satisfies JSONSchema;
|
} as const satisfies JSONSchema;
|
||||||
|
|
||||||
type Payload = FromSchema<typeof SchemaData>;
|
type Payload = FromSchema<typeof SchemaData>;
|
||||||
@@ -32,16 +31,20 @@ export default class GoCQHTTPGetGroupMsgHistory extends BaseAction<Payload, Resp
|
|||||||
if (!group) {
|
if (!group) {
|
||||||
throw `群${payload.group_id}不存在`;
|
throw `群${payload.group_id}不存在`;
|
||||||
}
|
}
|
||||||
const startMsgId = (await dbUtil.getMsgByShortId(payload.message_seq))?.msgId || '0';
|
let targetMsgShortId, count = parseInt(payload.count?.toString() ?? '20');
|
||||||
// log("startMsgId", startMsgId)
|
const peer = {
|
||||||
const historyResult = (await NTQQMsgApi.getMsgHistory({
|
|
||||||
chatType: ChatType.group,
|
chatType: ChatType.group,
|
||||||
peerUid: group.groupCode
|
peerUid: group.groupCode
|
||||||
}, startMsgId, parseInt(payload.count?.toString()) || 20));
|
};
|
||||||
//logDebug(historyResult);
|
let msgList: RawMessage[];
|
||||||
const msgList = historyResult.msgList;
|
if (!payload.message_seq) {
|
||||||
|
msgList = (await NTQQMsgApi.getLastestMsgByUids(peer, count)).msgList;
|
||||||
|
} else {
|
||||||
|
const startMsgId = (await MessageUnique.getMsgIdAndPeerByShortId(targetMsgShortId ?? (payload.message_seq ?? 0)))?.MsgId || '0';
|
||||||
|
msgList = (await NTQQMsgApi.getMsgHistory(peer, startMsgId, count)).msgList;
|
||||||
|
}
|
||||||
await Promise.all(msgList.map(async msg => {
|
await Promise.all(msgList.map(async msg => {
|
||||||
msg.id = await dbUtil.addMsg(msg);
|
msg.id = await MessageUnique.createMsg({ guildId: '', chatType: msg.chatType, peerUid: msg.peerUid }, msg.msgId);
|
||||||
}));
|
}));
|
||||||
const ob11MsgList = await Promise.all(msgList.map(msg => OB11Constructor.message(msg)));
|
const ob11MsgList = await Promise.all(msgList.map(msg => OB11Constructor.message(msg)));
|
||||||
return { 'messages': ob11MsgList };
|
return { 'messages': ob11MsgList };
|
||||||
|
@@ -3,6 +3,7 @@ import BaseAction from '../BaseAction';
|
|||||||
import { ActionName } from '../types';
|
import { ActionName } from '../types';
|
||||||
import { JSONSchema } from 'json-schema-to-ts';
|
import { JSONSchema } from 'json-schema-to-ts';
|
||||||
import { NTQQSystemApi } from '@/core';
|
import { NTQQSystemApi } from '@/core';
|
||||||
|
import { sleep } from '@/common/utils/helper';
|
||||||
|
|
||||||
const SchemaData = {
|
const SchemaData = {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
@@ -15,7 +16,8 @@ export class GetOnlineClient extends BaseAction<void, Array<any>> {
|
|||||||
actionName = ActionName.GetOnlineClient;
|
actionName = ActionName.GetOnlineClient;
|
||||||
|
|
||||||
protected async _handle(payload: void) {
|
protected async _handle(payload: void) {
|
||||||
//console.log(await NTQQSystemApi.getOnlineDev());
|
NTQQSystemApi.getOnlineDev();
|
||||||
|
await sleep(500);
|
||||||
return DeviceList;
|
return DeviceList;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -5,6 +5,7 @@ import { ActionName } from '../types';
|
|||||||
import { NTQQUserApi } from '@/core/apis/user';
|
import { NTQQUserApi } from '@/core/apis/user';
|
||||||
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||||
import { calcQQLevel } from '@/common/utils/qqlevel';
|
import { calcQQLevel } from '@/common/utils/qqlevel';
|
||||||
|
import { requireMinNTQQBuild } from '@/common/utils/QQBasicInfo';
|
||||||
|
|
||||||
const SchemaData = {
|
const SchemaData = {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
@@ -20,24 +21,46 @@ export default class GoCQHTTPGetStrangerInfo extends BaseAction<Payload, OB11Use
|
|||||||
actionName = ActionName.GoCQHTTP_GetStrangerInfo;
|
actionName = ActionName.GoCQHTTP_GetStrangerInfo;
|
||||||
|
|
||||||
protected async _handle(payload: Payload): Promise<OB11User> {
|
protected async _handle(payload: Payload): Promise<OB11User> {
|
||||||
const user_id = payload.user_id.toString();
|
if (!requireMinNTQQBuild('26702')) {
|
||||||
const extendData = await NTQQUserApi.getUserDetailInfoByUin(user_id);
|
const user_id = payload.user_id.toString();
|
||||||
const uid = (await NTQQUserApi.getUidByUin(user_id))!;
|
const extendData = await NTQQUserApi.getUserDetailInfoByUin(user_id);
|
||||||
if (!uid || uid.indexOf('*') != -1) {
|
const uid = (await NTQQUserApi.getUidByUin(user_id))!;
|
||||||
const ret = {
|
if (!uid || uid.indexOf('*') != -1) {
|
||||||
...extendData,
|
const ret = {
|
||||||
user_id: parseInt(extendData.info.uin) || 0,
|
...extendData,
|
||||||
nickname: extendData.info.nick,
|
user_id: parseInt(extendData.info.uin) || 0,
|
||||||
sex: OB11UserSex.unknown,
|
nickname: extendData.info.nick,
|
||||||
age: (extendData.info.birthday_year == 0) ? 0 : new Date().getFullYear() - extendData.info.birthday_year,
|
sex: OB11UserSex.unknown,
|
||||||
qid: extendData.info.qid,
|
age: (extendData.info.birthday_year == 0) ? 0 : new Date().getFullYear() - extendData.info.birthday_year,
|
||||||
level: extendData.info.qqLevel && calcQQLevel(extendData.info.qqLevel) || 0,
|
qid: extendData.info.qid,
|
||||||
login_days: 0,
|
level: extendData.info.qqLevel && calcQQLevel(extendData.info.qqLevel) || 0,
|
||||||
uid: ''
|
login_days: 0,
|
||||||
};
|
uid: ''
|
||||||
return ret;
|
};
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
const data = { ...extendData, ...(await NTQQUserApi.getUserDetailInfo(uid)) };
|
||||||
|
return OB11Constructor.stranger(data);
|
||||||
|
} else {
|
||||||
|
const user_id = payload.user_id.toString();
|
||||||
|
const extendData = await NTQQUserApi.getUserDetailInfoByUinV2(user_id);
|
||||||
|
//console.log(extendData);
|
||||||
|
const uid = (await NTQQUserApi.getUidByUin(user_id))!;
|
||||||
|
if (!uid || uid.indexOf('*') != -1) {
|
||||||
|
const ret = {
|
||||||
|
...extendData,
|
||||||
|
user_id: parseInt(extendData.detail.uin) || 0,
|
||||||
|
nickname: extendData.detail.simpleInfo.coreInfo.nick,
|
||||||
|
sex: OB11UserSex.unknown,
|
||||||
|
age: 0,
|
||||||
|
level: extendData.detail.commonExt.qqLevel && calcQQLevel(extendData.detail.commonExt.qqLevel) || 0,
|
||||||
|
login_days: 0,
|
||||||
|
uid: ''
|
||||||
|
};
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
const data = { ...extendData, ...(await NTQQUserApi.getUserDetailInfo(uid)) };
|
||||||
|
return OB11Constructor.stranger(data);
|
||||||
}
|
}
|
||||||
const data = { ...extendData, ...(await NTQQUserApi.getUserDetailInfo(uid)) };
|
|
||||||
return OB11Constructor.stranger(data);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
51
src/onebot11/action/go-cqhttp/UploadPrivareFile.ts
Normal file
51
src/onebot11/action/go-cqhttp/UploadPrivareFile.ts
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
import BaseAction from '../BaseAction';
|
||||||
|
import { getGroup } from '@/core/data';
|
||||||
|
import { ActionName } from '../types';
|
||||||
|
import { SendMsgElementConstructor } from '@/core/entities/constructor';
|
||||||
|
import { ChatType, Peer, SendFileElement } from '@/core/entities';
|
||||||
|
import fs from 'fs';
|
||||||
|
import { SendMsg, sendMsg } from '@/onebot11/action/msg/SendMsg';
|
||||||
|
import { uri2local } from '@/common/utils/file';
|
||||||
|
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||||
|
import { NTQQFriendApi, NTQQUserApi } from '@/core';
|
||||||
|
const SchemaData = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
user_id: { type: ['number', 'string'] },
|
||||||
|
file: { type: 'string' },
|
||||||
|
name: { type: 'string' }
|
||||||
|
},
|
||||||
|
required: ['user_id', 'file', 'name']
|
||||||
|
} as const satisfies JSONSchema;
|
||||||
|
|
||||||
|
type Payload = FromSchema<typeof SchemaData>;
|
||||||
|
|
||||||
|
export default class GoCQHTTPUploadPrivateFile extends BaseAction<Payload, null> {
|
||||||
|
actionName = ActionName.GOCQHTTP_UploadPrivateFile;
|
||||||
|
PayloadSchema = SchemaData;
|
||||||
|
async getPeer(payload: Payload): Promise<Peer> {
|
||||||
|
if (payload.user_id) {
|
||||||
|
const peerUid = await NTQQUserApi.getUidByUin(payload.user_id.toString());
|
||||||
|
if (!peerUid) {
|
||||||
|
throw `私聊${payload.user_id}不存在`;
|
||||||
|
}
|
||||||
|
const isBuddy = await NTQQFriendApi.isBuddy(peerUid);
|
||||||
|
return { chatType: isBuddy ? ChatType.friend : ChatType.temp, peerUid };
|
||||||
|
}
|
||||||
|
throw '缺少参数 user_id';
|
||||||
|
}
|
||||||
|
protected async _handle(payload: Payload): Promise<null> {
|
||||||
|
const peer = await this.getPeer(payload);
|
||||||
|
let file = payload.file;
|
||||||
|
if (fs.existsSync(file)) {
|
||||||
|
file = `file://${file}`;
|
||||||
|
}
|
||||||
|
const downloadResult = await uri2local(file);
|
||||||
|
if (downloadResult.errMsg) {
|
||||||
|
throw new Error(downloadResult.errMsg);
|
||||||
|
}
|
||||||
|
const sendFileEle: SendFileElement = await SendMsgElementConstructor.file(downloadResult.path, payload.name);
|
||||||
|
await sendMsg(peer, [sendFileEle], [], true);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
31
src/onebot11/action/group/DelEssenceMsg.ts
Normal file
31
src/onebot11/action/group/DelEssenceMsg.ts
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
|
||||||
|
import BaseAction from '../BaseAction';
|
||||||
|
import { ActionName } from '../types';
|
||||||
|
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||||
|
import { NTQQGroupApi } from '@/core';
|
||||||
|
import { MessageUnique } from '@/common/utils/MessageUnique';
|
||||||
|
|
||||||
|
const SchemaData = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
message_id: { type: ['number', 'string'] }
|
||||||
|
},
|
||||||
|
required: ['message_id']
|
||||||
|
} as const satisfies JSONSchema;
|
||||||
|
|
||||||
|
type Payload = FromSchema<typeof SchemaData>;
|
||||||
|
|
||||||
|
export default class DelEssenceMsg extends BaseAction<Payload, any> {
|
||||||
|
actionName = ActionName.DelEssenceMsg;
|
||||||
|
PayloadSchema = SchemaData;
|
||||||
|
protected async _handle(payload: Payload): Promise<any> {
|
||||||
|
const msg = await MessageUnique.getMsgIdAndPeerByShortId(parseInt(payload.message_id.toString()));
|
||||||
|
if (!msg) {
|
||||||
|
throw new Error('msg not found');
|
||||||
|
}
|
||||||
|
return await NTQQGroupApi.removeGroupEssence(
|
||||||
|
msg.Peer.peerUid,
|
||||||
|
msg.MsgId
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@@ -2,10 +2,8 @@ import { OB11Group } from '../../types';
|
|||||||
import { OB11Constructor } from '../../constructor';
|
import { OB11Constructor } from '../../constructor';
|
||||||
import BaseAction from '../BaseAction';
|
import BaseAction from '../BaseAction';
|
||||||
import { ActionName } from '../types';
|
import { ActionName } from '../types';
|
||||||
import { groups } from '@/core/data';
|
|
||||||
import { NTQQGroupApi } from '@/core/apis';
|
import { NTQQGroupApi } from '@/core/apis';
|
||||||
import { Group } from '@/core/entities';
|
import { Group } from '@/core/entities';
|
||||||
import { log } from '@/common/utils/log';
|
|
||||||
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||||
// no_cache get时传字符串
|
// no_cache get时传字符串
|
||||||
const SchemaData = {
|
const SchemaData = {
|
||||||
@@ -21,11 +19,7 @@ class GetGroupList extends BaseAction<Payload, OB11Group[]> {
|
|||||||
actionName = ActionName.GetGroupList;
|
actionName = ActionName.GetGroupList;
|
||||||
PayloadSchema = SchemaData;
|
PayloadSchema = SchemaData;
|
||||||
protected async _handle(payload: Payload) {
|
protected async _handle(payload: Payload) {
|
||||||
let groupList: Group[] = Array.from(groups.values());
|
let groupList: Group[] = await NTQQGroupApi.getGroups(payload?.no_cache === true || payload.no_cache === 'true');
|
||||||
if (groupList.length === 0 || payload?.no_cache === true || payload.no_cache === 'true') {
|
|
||||||
groupList = await NTQQGroupApi.getGroups(true);
|
|
||||||
// log('get groups', groups);
|
|
||||||
}
|
|
||||||
return OB11Constructor.groups(groupList);
|
return OB11Constructor.groups(groupList);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,16 +1,14 @@
|
|||||||
import { OB11GroupMember } from '../../types';
|
import { OB11GroupMember } from '../../types';
|
||||||
import { getGroup, getGroupMember, groupMembers } from '@/core/data';
|
|
||||||
import { OB11Constructor } from '../../constructor';
|
import { OB11Constructor } from '../../constructor';
|
||||||
import BaseAction from '../BaseAction';
|
import BaseAction from '../BaseAction';
|
||||||
import { ActionName } from '../types';
|
import { ActionName } from '../types';
|
||||||
import { NTQQUserApi } from '@/core/apis/user';
|
import { NTQQUserApi } from '@/core/apis/user';
|
||||||
import { log, logDebug } from '@/common/utils/log';
|
import { logDebug } from '@/common/utils/log';
|
||||||
import { isNull } from '../../../common/utils/helper';
|
|
||||||
import { WebApi } from '@/core/apis/webapi';
|
import { WebApi } from '@/core/apis/webapi';
|
||||||
import { NTQQGroupApi } from '@/core';
|
import { NTQQGroupApi } from '@/core';
|
||||||
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||||
|
import { getGroupMember, selfInfo } from '@/core/data';
|
||||||
// no_cache get时传字符串
|
import { requireMinNTQQBuild } from '@/common/utils/QQBasicInfo';
|
||||||
const SchemaData = {
|
const SchemaData = {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
properties: {
|
properties: {
|
||||||
@@ -27,39 +25,55 @@ class GetGroupMemberInfo extends BaseAction<Payload, OB11GroupMember> {
|
|||||||
actionName = ActionName.GetGroupMemberInfo;
|
actionName = ActionName.GetGroupMemberInfo;
|
||||||
PayloadSchema = SchemaData;
|
PayloadSchema = SchemaData;
|
||||||
protected async _handle(payload: Payload) {
|
protected async _handle(payload: Payload) {
|
||||||
const group = await getGroup(payload.group_id.toString());
|
const isNocache = payload.no_cache == true || payload.no_cache === 'true';
|
||||||
if (!group) {
|
let uid = await NTQQUserApi.getUidByUin(payload.user_id.toString());
|
||||||
throw (`群(${payload.group_id})不存在`);
|
if (!uid) {
|
||||||
|
throw (`Uin2Uid Error ${payload.user_id}不存在`);
|
||||||
}
|
}
|
||||||
const webGroupMembers = await WebApi.getGroupMembers(payload.group_id.toString());
|
let member = await NTQQGroupApi.getGroupMemberV2(payload.group_id.toString(), uid, isNocache);
|
||||||
if (payload.no_cache == true || payload.no_cache === 'true') {
|
if (!member) {
|
||||||
groupMembers.set(group.groupCode, await NTQQGroupApi.getGroupMembers(payload.group_id.toString()));
|
|
||||||
}
|
|
||||||
const member = await getGroupMember(payload.group_id.toString(), payload.user_id.toString());
|
|
||||||
// log(member);
|
|
||||||
if (member) {
|
|
||||||
logDebug('获取群成员详细信息');
|
|
||||||
try {
|
|
||||||
const info = (await NTQQUserApi.getUserDetailInfo(member.uid));
|
|
||||||
logDebug('群成员详细信息结果', info);
|
|
||||||
Object.assign(member, info);
|
|
||||||
} catch (e) {
|
|
||||||
logDebug('获取群成员详细信息失败, 只能返回基础信息', e);
|
|
||||||
}
|
|
||||||
const retMember = OB11Constructor.groupMember(payload.group_id.toString(), member);
|
|
||||||
for (let i = 0, len = webGroupMembers.length; i < len; i++) {
|
|
||||||
if (webGroupMembers[i]?.uin && webGroupMembers[i].uin === retMember.user_id) {
|
|
||||||
retMember.join_time = webGroupMembers[i]?.join_time;
|
|
||||||
retMember.last_sent_time = webGroupMembers[i]?.last_speak_time;
|
|
||||||
retMember.qage = webGroupMembers[i]?.qage;
|
|
||||||
retMember.level = webGroupMembers[i]?.lv.level.toString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return retMember;
|
|
||||||
} else {
|
|
||||||
throw (`群(${payload.group_id})成员${payload.user_id}不存在`);
|
throw (`群(${payload.group_id})成员${payload.user_id}不存在`);
|
||||||
}
|
}
|
||||||
|
try {
|
||||||
|
const info = (await NTQQUserApi.getUserDetailInfo(member.uid));
|
||||||
|
logDebug('群成员详细信息结果', info);
|
||||||
|
Object.assign(member, info);
|
||||||
|
} catch (e) {
|
||||||
|
logDebug('获取群成员详细信息失败, 只能返回基础信息', e);
|
||||||
|
}
|
||||||
|
const date = Math.round(Date.now() / 1000);
|
||||||
|
const retMember = OB11Constructor.groupMember(payload.group_id.toString(), member);
|
||||||
|
if (!requireMinNTQQBuild('26702')) {
|
||||||
|
let SelfInfoInGroup = await NTQQGroupApi.getGroupMemberV2(payload.group_id.toString(), selfInfo.uid, isNocache);
|
||||||
|
let isPrivilege = false;
|
||||||
|
if (SelfInfoInGroup) {
|
||||||
|
isPrivilege = SelfInfoInGroup.role === 3 || SelfInfoInGroup.role === 4;
|
||||||
|
}
|
||||||
|
if (isPrivilege) {
|
||||||
|
const webGroupMembers = await WebApi.getGroupMembers(payload.group_id.toString());
|
||||||
|
for (let i = 0, len = webGroupMembers.length; i < len; i++) {
|
||||||
|
if (webGroupMembers[i]?.uin && webGroupMembers[i].uin === retMember.user_id) {
|
||||||
|
retMember.join_time = webGroupMembers[i]?.join_time;
|
||||||
|
retMember.last_sent_time = webGroupMembers[i]?.last_speak_time;
|
||||||
|
retMember.qage = webGroupMembers[i]?.qage;
|
||||||
|
retMember.level = webGroupMembers[i]?.lv.level.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const LastestMsgList = await NTQQGroupApi.getLastestMsg(payload.group_id.toString(), [payload.user_id.toString()]);
|
||||||
|
if (LastestMsgList?.msgList?.length && LastestMsgList?.msgList?.length > 0) {
|
||||||
|
const last_send_time = LastestMsgList.msgList[0].msgTime;
|
||||||
|
if (last_send_time && last_send_time != '0' && last_send_time != '') {
|
||||||
|
retMember.last_sent_time = parseInt(last_send_time);
|
||||||
|
retMember.join_time = Math.round(Date.now() / 1000);//兜底数据 防止群管乱杀
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
retMember.last_sent_time = parseInt((await getGroupMember(payload.group_id.toString(), retMember.user_id))?.lastSpeakTime || date.toString());
|
||||||
|
retMember.join_time = parseInt((await getGroupMember(payload.group_id.toString(), retMember.user_id))?.joinTime || date.toString());
|
||||||
|
}
|
||||||
|
return retMember;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
export default GetGroupMemberInfo;
|
||||||
export default GetGroupMemberInfo;
|
|
84
src/onebot11/action/group/GetGroupMemberInfoOld.ts
Normal file
84
src/onebot11/action/group/GetGroupMemberInfoOld.ts
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
import { OB11GroupMember } from '../../types';
|
||||||
|
import { getGroup, getGroupMember, groupMembers, selfInfo } from '@/core/data';
|
||||||
|
import { OB11Constructor } from '../../constructor';
|
||||||
|
import BaseAction from '../BaseAction';
|
||||||
|
import { ActionName } from '../types';
|
||||||
|
import { NTQQUserApi } from '@/core/apis/user';
|
||||||
|
import { logDebug } from '@/common/utils/log';
|
||||||
|
import { WebApi } from '@/core/apis/webapi';
|
||||||
|
import { NTQQGroupApi } from '@/core';
|
||||||
|
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||||
|
import { requireMinNTQQBuild } from '@/common/utils/QQBasicInfo';
|
||||||
|
|
||||||
|
// no_cache get时传字符串
|
||||||
|
const SchemaData = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
group_id: { type: ['number', 'string'] },
|
||||||
|
user_id: { type: ['number', 'string'] },
|
||||||
|
no_cache: { type: ['boolean', 'string'] },
|
||||||
|
},
|
||||||
|
required: ['group_id', 'user_id']
|
||||||
|
} as const satisfies JSONSchema;
|
||||||
|
|
||||||
|
type Payload = FromSchema<typeof SchemaData>;
|
||||||
|
|
||||||
|
class GetGroupMemberInfo extends BaseAction<Payload, OB11GroupMember> {
|
||||||
|
actionName = ActionName.GetGroupMemberInfo;
|
||||||
|
PayloadSchema = SchemaData;
|
||||||
|
protected async _handle(payload: Payload) {
|
||||||
|
if (requireMinNTQQBuild('26702')) {
|
||||||
|
let V2Data = await NTQQGroupApi.getGroupMemberV2(payload.group_id.toString(), payload.user_id.toString(), payload.no_cache == true || payload.no_cache === 'true');
|
||||||
|
if (V2Data) {
|
||||||
|
return OB11Constructor.groupMember(payload.group_id.toString(), V2Data);
|
||||||
|
} else {
|
||||||
|
throw (`群(${payload.group_id})成员${payload.user_id}不存在`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const group = await getGroup(payload.group_id.toString());
|
||||||
|
const role = (await getGroupMember(payload.group_id, selfInfo.uin))?.role;
|
||||||
|
const isPrivilege = role === 3 || role === 4;
|
||||||
|
if (!group) {
|
||||||
|
throw (`群(${payload.group_id})不存在`);
|
||||||
|
}
|
||||||
|
if (payload.no_cache == true || payload.no_cache === 'true') {
|
||||||
|
groupMembers.set(group.groupCode, await NTQQGroupApi.getGroupMembers(payload.group_id.toString()));
|
||||||
|
}
|
||||||
|
const member = await getGroupMember(payload.group_id.toString(), payload.user_id.toString());
|
||||||
|
//早返回
|
||||||
|
if (!member) {
|
||||||
|
throw (`群(${payload.group_id})成员${payload.user_id}不存在`);
|
||||||
|
}
|
||||||
|
//console.log('GetGroupMemberInfo', JSON.stringify(await NTQQGroupApi.getGroupMemberV2(payload.group_id.toString(), member.uid, true), null, 4));
|
||||||
|
try {
|
||||||
|
const info = (await NTQQUserApi.getUserDetailInfo(member.uid));
|
||||||
|
logDebug('群成员详细信息结果', info);
|
||||||
|
Object.assign(member, info);
|
||||||
|
} catch (e) {
|
||||||
|
logDebug('获取群成员详细信息失败, 只能返回基础信息', e);
|
||||||
|
}
|
||||||
|
const retMember = OB11Constructor.groupMember(payload.group_id.toString(), member);
|
||||||
|
if (isPrivilege) {
|
||||||
|
const webGroupMembers = await WebApi.getGroupMembers(payload.group_id.toString());
|
||||||
|
for (let i = 0, len = webGroupMembers.length; i < len; i++) {
|
||||||
|
if (webGroupMembers[i]?.uin && webGroupMembers[i].uin === retMember.user_id) {
|
||||||
|
retMember.join_time = webGroupMembers[i]?.join_time;
|
||||||
|
retMember.last_sent_time = webGroupMembers[i]?.last_speak_time;
|
||||||
|
retMember.qage = webGroupMembers[i]?.qage;
|
||||||
|
retMember.level = webGroupMembers[i]?.lv.level.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const LastestMsgList = await NTQQGroupApi.getLastestMsg(payload.group_id.toString(), [payload.user_id.toString()]);
|
||||||
|
if (LastestMsgList?.msgList?.length && LastestMsgList?.msgList?.length > 0) {
|
||||||
|
const last_send_time = LastestMsgList.msgList[0].msgTime;
|
||||||
|
if (last_send_time && last_send_time != '0' && last_send_time != '') {
|
||||||
|
retMember.last_sent_time = parseInt(last_send_time);
|
||||||
|
retMember.join_time = Math.round(Date.now() / 1000);//兜底数据 防止群管乱杀
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return retMember;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export default GetGroupMemberInfo;
|
@@ -1,15 +1,12 @@
|
|||||||
import { getGroup, getGroupMember, groupMembers, selfInfo } from '@/core/data';
|
import { getGroup, getGroupMember, selfInfo } from '@/core/data';
|
||||||
import { OB11GroupMember } from '../../types';
|
import { OB11GroupMember } from '../../types';
|
||||||
import { OB11Constructor } from '../../constructor';
|
import { OB11Constructor } from '../../constructor';
|
||||||
import BaseAction from '../BaseAction';
|
import BaseAction from '../BaseAction';
|
||||||
import { ActionName } from '../types';
|
import { ActionName } from '../types';
|
||||||
import { napCatCore, NTQQGroupApi, NTQQUserApi } from '@/core';
|
import { NTQQGroupApi } from '@/core';
|
||||||
import { WebApi } from '@/core/apis/webapi';
|
import { WebApi } from '@/core/apis/webapi';
|
||||||
import { logDebug } from '@/common/utils/log';
|
|
||||||
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||||
import { ob11Config } from '@/onebot11/config';
|
import { requireMinNTQQBuild } from '@/common/utils/QQBasicInfo';
|
||||||
import { dbUtil } from '@/common/utils/db';
|
|
||||||
import { TypeConvert } from '@/common/utils/type';
|
|
||||||
|
|
||||||
const SchemaData = {
|
const SchemaData = {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
@@ -26,51 +23,67 @@ class GetGroupMemberList extends BaseAction<Payload, OB11GroupMember[]> {
|
|||||||
actionName = ActionName.GetGroupMemberList;
|
actionName = ActionName.GetGroupMemberList;
|
||||||
PayloadSchema = SchemaData;
|
PayloadSchema = SchemaData;
|
||||||
protected async _handle(payload: Payload) {
|
protected async _handle(payload: Payload) {
|
||||||
const role = (await getGroupMember(payload.group_id, selfInfo.uin))?.role;
|
const isNocache = payload.no_cache == true || payload.no_cache === 'true';
|
||||||
|
|
||||||
const group = await getGroup(payload.group_id.toString());
|
const GroupList = await NTQQGroupApi.getGroups(isNocache);
|
||||||
|
const group = GroupList.find(item => item.groupCode == payload.group_id);
|
||||||
if (!group) {
|
if (!group) {
|
||||||
throw (`群${payload.group_id}不存在`);
|
throw (`群${payload.group_id}不存在`);
|
||||||
}
|
}
|
||||||
|
let groupMembers = await NTQQGroupApi.getGroupMembers(payload.group_id.toString());
|
||||||
// 从Data里面获取
|
let _groupMembers = Array.from(groupMembers.values())
|
||||||
let _groupMembers: OB11GroupMember[] = OB11Constructor.groupMembers(group);
|
.map(item => { return OB11Constructor.groupMember(group.groupCode, item); });
|
||||||
if (payload.no_cache == true || payload.no_cache === 'true') {
|
|
||||||
// webGroupMembers = await WebApi.getGroupMembers(payload.group_id.toString());'
|
|
||||||
const _groupMembers = await NTQQGroupApi.getGroupMembers(payload.group_id.toString());
|
|
||||||
groupMembers.set(group.groupCode, _groupMembers);
|
|
||||||
}
|
|
||||||
|
|
||||||
const MemberMap: Map<number, OB11GroupMember> = new Map<number, OB11GroupMember>();
|
const MemberMap: Map<number, OB11GroupMember> = new Map<number, OB11GroupMember>();
|
||||||
// 转为Map 方便索引
|
// 转为Map 方便索引
|
||||||
|
const date = Math.round(Date.now() / 1000);
|
||||||
for (let i = 0, len = _groupMembers.length; i < len; i++) {
|
for (let i = 0, len = _groupMembers.length; i < len; i++) {
|
||||||
|
// 保证基础数据有这个 同时避免群管插件过于依赖这个杀了
|
||||||
|
_groupMembers[i].join_time = date;
|
||||||
|
_groupMembers[i].last_sent_time = date;
|
||||||
MemberMap.set(_groupMembers[i].user_id, _groupMembers[i]);
|
MemberMap.set(_groupMembers[i].user_id, _groupMembers[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
const isPrivilege = role === 3 || role === 4;
|
if (!requireMinNTQQBuild('26702')) {
|
||||||
if (isPrivilege) {
|
const selfRole = groupMembers.get(selfInfo.uid)?.role;
|
||||||
const webGroupMembers = await WebApi.getGroupMembers(payload.group_id.toString());
|
const isPrivilege = selfRole === 3 || selfRole === 4;
|
||||||
for (let i = 0, len = webGroupMembers.length; i < len; i++) {
|
|
||||||
if (!webGroupMembers[i]?.uin) {
|
if (isPrivilege) {
|
||||||
continue;
|
const webGroupMembers = await WebApi.getGroupMembers(payload.group_id.toString());
|
||||||
|
for (let i = 0, len = webGroupMembers.length; i < len; i++) {
|
||||||
|
if (!webGroupMembers[i]?.uin) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const MemberData = MemberMap.get(webGroupMembers[i]?.uin);
|
||||||
|
if (MemberData) {
|
||||||
|
MemberData.join_time = webGroupMembers[i]?.join_time;
|
||||||
|
MemberData.last_sent_time = webGroupMembers[i]?.last_speak_time;
|
||||||
|
MemberData.qage = webGroupMembers[i]?.qage;
|
||||||
|
MemberData.level = webGroupMembers[i]?.lv.level.toString();
|
||||||
|
MemberMap.set(webGroupMembers[i]?.uin, MemberData);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
const MemberData = MemberMap.get(webGroupMembers[i]?.uin);
|
} else {
|
||||||
if (MemberData) {
|
if (isNocache) {
|
||||||
MemberData.join_time = webGroupMembers[i]?.join_time;
|
const DateMap = await NTQQGroupApi.getGroupMemberLastestSendTimeCache(payload.group_id.toString());//开始从本地拉取
|
||||||
MemberData.last_sent_time = webGroupMembers[i]?.last_speak_time;
|
for (const DateUin of DateMap.keys()) {
|
||||||
MemberData.qage = webGroupMembers[i]?.qage;
|
const MemberData = MemberMap.get(parseInt(DateUin));
|
||||||
MemberData.level = webGroupMembers[i]?.lv.level.toString();
|
if (MemberData) {
|
||||||
MemberMap.set(webGroupMembers[i]?.uin, MemberData);
|
MemberData.last_sent_time = parseInt(DateMap.get(DateUin)!);
|
||||||
|
//join_time 有基础数据兜底
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
_groupMembers.forEach(item => {
|
||||||
|
item.last_sent_time = date;
|
||||||
|
item.join_time = date;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (ob11Config.GroupLocalTime.Record && ob11Config.GroupLocalTime.RecordList[0] === '-1' || ob11Config.GroupLocalTime.RecordList.includes(payload.group_id.toString())) {
|
} else {
|
||||||
const _sendAndJoinRember = await dbUtil.getLastSentTimeAndJoinTime(TypeConvert.toNumber(payload.group_id));
|
_groupMembers.forEach(async item => {
|
||||||
_sendAndJoinRember.forEach((element) => {
|
item.last_sent_time = parseInt((await getGroupMember(payload.group_id.toString(), item.user_id))?.lastSpeakTime || date.toString());
|
||||||
const MemberData = MemberMap.get(element.user_id);
|
item.join_time = parseInt((await getGroupMember(payload.group_id.toString(), item.user_id))?.joinTime || date.toString());
|
||||||
if (MemberData) {
|
|
||||||
MemberData.join_time = element.join_time;
|
|
||||||
MemberData.last_sent_time = element.last_sent_time;
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
// 还原索引到Array 一同返回
|
// 还原索引到Array 一同返回
|
||||||
@@ -82,7 +95,7 @@ class GetGroupMemberList extends BaseAction<Payload, OB11GroupMember[]> {
|
|||||||
// }
|
// }
|
||||||
|
|
||||||
// _groupMembers = Array.from(retData);
|
// _groupMembers = Array.from(retData);
|
||||||
|
|
||||||
_groupMembers = Array.from(MemberMap.values());
|
_groupMembers = Array.from(MemberMap.values());
|
||||||
return _groupMembers;
|
return _groupMembers;
|
||||||
}
|
}
|
||||||
|
30
src/onebot11/action/group/SetEssenceMsg.ts
Normal file
30
src/onebot11/action/group/SetEssenceMsg.ts
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
import BaseAction from '../BaseAction';
|
||||||
|
import { ActionName } from '../types';
|
||||||
|
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||||
|
import { NTQQGroupApi, NTQQMsgApi } from '@/core';
|
||||||
|
import { MessageUnique } from '@/common/utils/MessageUnique';
|
||||||
|
|
||||||
|
const SchemaData = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
message_id: { type: ['number', 'string'] }
|
||||||
|
},
|
||||||
|
required: ['message_id']
|
||||||
|
} as const satisfies JSONSchema;
|
||||||
|
|
||||||
|
type Payload = FromSchema<typeof SchemaData>;
|
||||||
|
|
||||||
|
export default class SetEssenceMsg extends BaseAction<Payload, any> {
|
||||||
|
actionName = ActionName.SetEssenceMsg;
|
||||||
|
PayloadSchema = SchemaData;
|
||||||
|
protected async _handle(payload: Payload): Promise<any> {
|
||||||
|
const msg = await MessageUnique.getMsgIdAndPeerByShortId(parseInt(payload.message_id.toString()));
|
||||||
|
if (!msg) {
|
||||||
|
throw new Error('msg not found');
|
||||||
|
}
|
||||||
|
return await NTQQGroupApi.addGroupEssence(
|
||||||
|
msg.Peer.peerUid,
|
||||||
|
msg.MsgId
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@@ -2,7 +2,6 @@ import BaseAction from '../BaseAction';
|
|||||||
import { GroupRequestOperateTypes } from '@/core/entities';
|
import { GroupRequestOperateTypes } from '@/core/entities';
|
||||||
import { ActionName } from '../types';
|
import { ActionName } from '../types';
|
||||||
import { NTQQGroupApi } from '@/core/apis/group';
|
import { NTQQGroupApi } from '@/core/apis/group';
|
||||||
import { groupNotifies } from '@/core/data';
|
|
||||||
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||||
|
|
||||||
const SchemaData = {
|
const SchemaData = {
|
||||||
@@ -10,7 +9,7 @@ const SchemaData = {
|
|||||||
properties: {
|
properties: {
|
||||||
flag: { type: 'string' },
|
flag: { type: 'string' },
|
||||||
approve: { type: ['string', 'boolean'] },
|
approve: { type: ['string', 'boolean'] },
|
||||||
reason: { type: 'string' }
|
reason: { type: 'string', nullable: true, }
|
||||||
},
|
},
|
||||||
required: ['flag'],
|
required: ['flag'],
|
||||||
} as const satisfies JSONSchema;
|
} as const satisfies JSONSchema;
|
||||||
@@ -23,11 +22,7 @@ export default class SetGroupAddRequest extends BaseAction<Payload, null> {
|
|||||||
protected async _handle(payload: Payload): Promise<null> {
|
protected async _handle(payload: Payload): Promise<null> {
|
||||||
const flag = payload.flag.toString();
|
const flag = payload.flag.toString();
|
||||||
const approve = payload.approve?.toString() !== 'false';
|
const approve = payload.approve?.toString() !== 'false';
|
||||||
const notify = groupNotifies[flag];
|
await NTQQGroupApi.handleGroupRequest(flag,
|
||||||
if (!notify) {
|
|
||||||
throw `${flag}对应的加群通知不存在`;
|
|
||||||
}
|
|
||||||
await NTQQGroupApi.handleGroupRequest(notify,
|
|
||||||
approve ? GroupRequestOperateTypes.approve : GroupRequestOperateTypes.reject,
|
approve ? GroupRequestOperateTypes.approve : GroupRequestOperateTypes.reject,
|
||||||
payload.reason
|
payload.reason
|
||||||
);
|
);
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user