mirror of
https://github.com/NapNeko/NapCatQQ.git
synced 2025-07-19 12:03:37 +00:00
Compare commits
917 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
9dad2a8ac6 | ||
![]() |
75af104f07 | ||
![]() |
76ecba245b | ||
![]() |
3697c2ced8 | ||
![]() |
b9d1d84716 | ||
![]() |
64b2d547ce | ||
![]() |
d8d2ff7e4e | ||
![]() |
8aa5dc6482 | ||
![]() |
474ba20e61 | ||
![]() |
bdea2d02a9 | ||
![]() |
c4307481f1 | ||
![]() |
b8ac1b28bd | ||
![]() |
24038cda95 | ||
![]() |
86c82e9608 | ||
![]() |
daab5d150b | ||
![]() |
9ff82bdb90 | ||
![]() |
c6d70ef1cf | ||
![]() |
15d4bb3c76 | ||
![]() |
3e698981fd | ||
![]() |
9d45c934a5 | ||
![]() |
c2bf9cf93e | ||
![]() |
b3c6fd7f26 | ||
![]() |
ccd155de71 | ||
![]() |
1f90d2e46b | ||
![]() |
4c5d974c22 | ||
![]() |
392eda1cbc | ||
![]() |
a9da3279e8 | ||
![]() |
1ce8351180 | ||
![]() |
96c334478a | ||
![]() |
f1b0875b05 | ||
![]() |
cea9e11c83 | ||
![]() |
f098b39200 | ||
![]() |
012d948b59 | ||
![]() |
3334cd0a71 | ||
![]() |
d63d53fd88 | ||
![]() |
a7fa39b2fd | ||
![]() |
40bb42e193 | ||
![]() |
9c382c639b | ||
![]() |
a43cde38f1 | ||
![]() |
c35d2e08cd | ||
![]() |
3377c383c1 | ||
![]() |
c00e6d95cd | ||
![]() |
725fccf4ed | ||
![]() |
13129bd219 | ||
![]() |
4561977bcf | ||
![]() |
40be8a91f5 | ||
![]() |
2a04d5830b | ||
![]() |
82a38574f3 | ||
![]() |
fea3a33c2b | ||
![]() |
9a502cdf6f | ||
![]() |
4b616299cf | ||
![]() |
102243e064 | ||
![]() |
4b21ac5ebe | ||
![]() |
4dd7363dd3 | ||
![]() |
3d5e5ab78f | ||
![]() |
73045a1b21 | ||
![]() |
871173a7cf | ||
![]() |
0002313093 | ||
![]() |
948cf5cca6 | ||
![]() |
d40230879c | ||
![]() |
ab22b775f1 | ||
![]() |
42c85224ba | ||
![]() |
e57444a353 | ||
![]() |
3c6503d495 | ||
![]() |
149b518f48 | ||
![]() |
74621447ff | ||
![]() |
3280952931 | ||
![]() |
9e670e2736 | ||
![]() |
9fc6347a2f | ||
![]() |
ec7a15a192 | ||
![]() |
7f99982810 | ||
![]() |
935d83aaf8 | ||
![]() |
0ff6edd546 | ||
![]() |
94f629585a | ||
![]() |
89c04be02f | ||
![]() |
3151965ea8 | ||
![]() |
bdf5159be1 | ||
![]() |
0499ebbea3 | ||
![]() |
d5843b7236 | ||
![]() |
1c9c574a90 | ||
![]() |
39acf20e48 | ||
![]() |
52eb6ed5ab | ||
![]() |
ee78d2d59d | ||
![]() |
60dc5c4a38 | ||
![]() |
50a0dc0355 | ||
![]() |
3f681ec914 | ||
![]() |
0bf499f191 | ||
![]() |
389695a0d6 | ||
![]() |
07f1afb312 | ||
![]() |
ae91e61304 | ||
![]() |
6248991b01 | ||
![]() |
7f2d57ef62 | ||
![]() |
31f8f884f1 | ||
![]() |
4f4af5985a | ||
![]() |
a716fdf6d4 | ||
![]() |
9717f64abd | ||
![]() |
adf239183a | ||
![]() |
6cf209c79c | ||
![]() |
decc5fb3c0 | ||
![]() |
1e0820d613 | ||
![]() |
70124d5177 | ||
![]() |
269de65201 | ||
![]() |
1d11abbfb6 | ||
![]() |
700f308d6e | ||
![]() |
21b6928ca6 | ||
![]() |
998c67a649 | ||
![]() |
fb99e878b0 | ||
![]() |
1619adfc27 | ||
![]() |
5510fb473f | ||
![]() |
be1878cb2b | ||
![]() |
15ab121cbd | ||
![]() |
aa79b0e861 | ||
![]() |
b80e550bcd | ||
![]() |
dbc40b5814 | ||
![]() |
0d5696a644 | ||
![]() |
ceffa05802 | ||
![]() |
d5668920b6 | ||
![]() |
516f2da144 | ||
![]() |
33c94e1888 | ||
![]() |
51ab58cd91 | ||
![]() |
aa7798d1d1 | ||
![]() |
9067a1fc92 | ||
![]() |
4024b6c564 | ||
![]() |
d39730928b | ||
![]() |
e1f049229c | ||
![]() |
8f2676ec19 | ||
![]() |
32d26248dc | ||
![]() |
16f926401b | ||
![]() |
66d60d3599 | ||
![]() |
5a35ab6c34 | ||
![]() |
ba1542bd31 | ||
![]() |
453060945a | ||
![]() |
c8351be461 | ||
![]() |
9954da22a6 | ||
![]() |
907b5611eb | ||
![]() |
5f075de212 | ||
![]() |
8fcf3c5079 | ||
![]() |
07cee90c7a | ||
![]() |
75ad495b98 | ||
![]() |
0bb7288ad2 | ||
![]() |
ad72415532 | ||
![]() |
0ad0353fc0 | ||
![]() |
9fa0dcd7aa | ||
![]() |
1f2e80cd39 | ||
![]() |
6cb6034d43 | ||
![]() |
25134c6ac6 | ||
![]() |
92bf42878a | ||
![]() |
9f4582d158 | ||
![]() |
68af73970e | ||
![]() |
b6ed8d4975 | ||
![]() |
d07d3645ce | ||
![]() |
123759ab17 | ||
![]() |
f2f1f893d8 | ||
![]() |
db93a8eed2 | ||
![]() |
12ab6d4a7d | ||
![]() |
add759e889 | ||
![]() |
f315f7977d | ||
![]() |
f2f6701ebd | ||
![]() |
1a92794d33 | ||
![]() |
7640deb798 | ||
![]() |
f1e8ef1cf6 | ||
![]() |
5e5ac0162e | ||
![]() |
0c013820f0 | ||
![]() |
4b3a9e5847 | ||
![]() |
e4982256a4 | ||
![]() |
babc4927a8 | ||
![]() |
6dd84cf469 | ||
![]() |
a8800e3899 | ||
![]() |
5f03496046 | ||
![]() |
41500c17a2 | ||
![]() |
2dcfde8b9a | ||
![]() |
5c3305d8fa | ||
![]() |
0d1fe99f53 | ||
![]() |
4c03ffeec7 | ||
![]() |
8101d17482 | ||
![]() |
bc7b4dcc2a | ||
![]() |
3db8b9078d | ||
![]() |
943dbbefd3 | ||
![]() |
480abcb853 | ||
![]() |
60aaaff58e | ||
![]() |
e3b889bbe8 | ||
![]() |
ac5506a43b | ||
![]() |
b29f533a3b | ||
![]() |
a8ee86b09e | ||
![]() |
0238c53302 | ||
![]() |
665e3c806f | ||
![]() |
8c96838441 | ||
![]() |
4a722daec6 | ||
![]() |
4e0cdbcb91 | ||
![]() |
08976624cd | ||
![]() |
fdeba94653 | ||
![]() |
d3b100b7e5 | ||
![]() |
1de3e18b08 | ||
![]() |
d5c3c95682 | ||
![]() |
dabe1e29ed | ||
![]() |
203d1c0cfc | ||
![]() |
7edd8601be | ||
![]() |
a4423247f4 | ||
![]() |
4834b203a0 | ||
![]() |
bbabb32d13 | ||
![]() |
95112d6bdf | ||
![]() |
36cdca5a3e | ||
![]() |
6980a9f3fc | ||
![]() |
7b09479cd2 | ||
![]() |
5825fd6f36 | ||
![]() |
2d5b45dd82 | ||
![]() |
52dda1d1fe | ||
![]() |
420624bee4 | ||
![]() |
8abde7b7d0 | ||
![]() |
9e5b1ba28e | ||
![]() |
b9c7d3c18e | ||
![]() |
10aeccbbe5 | ||
![]() |
15d351ebc2 | ||
![]() |
7194f31cb6 | ||
![]() |
84b7e82446 | ||
![]() |
8264423b1a | ||
![]() |
37f897f3bf | ||
![]() |
fe3efac145 | ||
![]() |
9773aebefc | ||
![]() |
06f2b8c371 | ||
![]() |
e8f0bb8350 | ||
![]() |
9bfa6b827b | ||
![]() |
b21bc17a58 | ||
![]() |
f4d5d417d0 | ||
![]() |
91fc83621e | ||
![]() |
461feca0ca | ||
![]() |
5e9afab3f7 | ||
![]() |
2599ca6450 | ||
![]() |
fc99ad3a39 | ||
![]() |
10e1c3e72c | ||
![]() |
af5dedd4d4 | ||
![]() |
3b986c1076 | ||
![]() |
72f77e8b7c | ||
![]() |
e893bf676f | ||
![]() |
80eb34f611 | ||
![]() |
5d01947552 | ||
![]() |
d3a025ef7b | ||
![]() |
c466df841e | ||
![]() |
b3c6e2a0f3 | ||
![]() |
076c9cfed7 | ||
![]() |
c3f3d12f83 | ||
![]() |
44974034ec | ||
![]() |
d6175acd38 | ||
![]() |
62eee5f05c | ||
![]() |
d4e5201913 | ||
![]() |
f4d584765a | ||
![]() |
26e224f852 | ||
![]() |
252358ed66 | ||
![]() |
475afeb7c8 | ||
![]() |
7cbbb846eb | ||
![]() |
25f947968c | ||
![]() |
cad824dcbc | ||
![]() |
e506f50b00 | ||
![]() |
96ec149a98 | ||
![]() |
8c913512f6 | ||
![]() |
4cc307299d | ||
![]() |
407c6b4c5f | ||
![]() |
8f87070434 | ||
![]() |
4a63996ee2 | ||
![]() |
0358fe7620 | ||
![]() |
55e64395ed | ||
![]() |
ff5fb18e14 | ||
![]() |
52dd960857 | ||
![]() |
430221c2de | ||
![]() |
217bdf8f92 | ||
![]() |
38c6c869bf | ||
![]() |
84d46da67e | ||
![]() |
eb9d6240d7 | ||
![]() |
2d44a871b0 | ||
![]() |
3f89f350ff | ||
![]() |
1a8407a782 | ||
![]() |
cf288a3f73 | ||
![]() |
f1f37fb180 | ||
![]() |
fb0dd079fd | ||
![]() |
a6c584c85c | ||
![]() |
77adf35a30 | ||
![]() |
dc6951c2a9 | ||
![]() |
d14ba3f0f7 | ||
![]() |
78ddf36e35 | ||
![]() |
d42734624d | ||
![]() |
b5dbd9d59b | ||
![]() |
bed3e1289b | ||
![]() |
b11ca4e60e | ||
![]() |
4fcf3aa2bd | ||
![]() |
dc39da8ca5 | ||
![]() |
c10c87d28e | ||
![]() |
c6fe6f1cc5 | ||
![]() |
1c2bbeb26d | ||
![]() |
17ed3692d0 | ||
![]() |
966a00f41e | ||
![]() |
fd8d8f89aa | ||
![]() |
305bb74072 | ||
![]() |
7f4dcdd134 | ||
![]() |
aac37dcce1 | ||
![]() |
f539c662a5 | ||
![]() |
c82f346dd0 | ||
![]() |
21b4a87837 | ||
![]() |
ae73bcf24b | ||
![]() |
2a3b56bde1 | ||
![]() |
b8ebededd8 | ||
![]() |
227c4c422c | ||
![]() |
652bfb93cc | ||
![]() |
c2278e3536 | ||
![]() |
caa2fca4e8 | ||
![]() |
745cb0175c | ||
![]() |
e5165a780f | ||
![]() |
b4b91af02b | ||
![]() |
5649ff9c2e | ||
![]() |
5b4bf6c62a | ||
![]() |
93cb662282 | ||
![]() |
00a8715e58 | ||
![]() |
7ecd479b3e | ||
![]() |
8fe7d3aaec | ||
![]() |
f32a693393 | ||
![]() |
17ebc01597 | ||
![]() |
827fb698e1 | ||
![]() |
32bdf10fd2 | ||
![]() |
b795e6c3d2 | ||
![]() |
42ba524e4e | ||
![]() |
317c6d96e3 | ||
![]() |
3692d1499f | ||
![]() |
b21fbad8a3 | ||
![]() |
743334a68a | ||
![]() |
951413eb38 | ||
![]() |
32dcdef853 | ||
![]() |
34c9254d4a | ||
![]() |
14012a4668 | ||
![]() |
575debca63 | ||
![]() |
763cac8532 | ||
![]() |
43faacd7a7 | ||
![]() |
1d4e307e96 | ||
![]() |
7f8933b0de | ||
![]() |
81608ff025 | ||
![]() |
db63675b8e | ||
![]() |
f74a83bc46 | ||
![]() |
bc1deba3e4 | ||
![]() |
d6113a8f0a | ||
![]() |
2062cd48ea | ||
![]() |
1c965ef515 | ||
![]() |
58291b7156 | ||
![]() |
afd1648d80 | ||
![]() |
21814ffa9a | ||
![]() |
9d3522da54 | ||
![]() |
e07a76755e | ||
![]() |
ba46bcdeae | ||
![]() |
8d7e44314c | ||
![]() |
35a67498c7 | ||
![]() |
90dd934f95 | ||
![]() |
4087045542 | ||
![]() |
d11cef5907 | ||
![]() |
76c91d226c | ||
![]() |
c2b4dd2afd | ||
![]() |
25b39cb39a | ||
![]() |
35dcb7b88b | ||
![]() |
e5f7e7c26e | ||
![]() |
c5c11fd6a6 | ||
![]() |
8134083419 | ||
![]() |
a87e624198 | ||
![]() |
e4c62d20b4 | ||
![]() |
fa195d9e55 | ||
![]() |
5ef5773d23 | ||
![]() |
6eea52afdf | ||
![]() |
80e64af30f | ||
![]() |
563b6ddc36 | ||
![]() |
c051ab9dc4 | ||
![]() |
87737a8bdb | ||
![]() |
94273d80b0 | ||
![]() |
a08ec2a4bd | ||
![]() |
d246c556f4 | ||
![]() |
65aa365e38 | ||
![]() |
eeeae449b4 | ||
![]() |
17c10a7ba2 | ||
![]() |
69f4383678 | ||
![]() |
07852a7295 | ||
![]() |
20b7e9b6b5 | ||
![]() |
75f43ccea4 | ||
![]() |
59e5785e93 | ||
![]() |
b38f52dba9 | ||
![]() |
2a6b17a48e | ||
![]() |
a6c056a894 | ||
![]() |
5c3442a71f | ||
![]() |
390253242f | ||
![]() |
9ab80fe1ac | ||
![]() |
91fdd09e7a | ||
![]() |
db5bd5c8a4 | ||
![]() |
ef94c2fe7c | ||
![]() |
72a25ed8e1 | ||
![]() |
eb065e218f | ||
![]() |
33426736fc | ||
![]() |
896658d5ce | ||
![]() |
b14135ed72 | ||
![]() |
a1baf2e32d | ||
![]() |
f9aa2d3bce | ||
![]() |
c95d0e0696 | ||
![]() |
ad4b84d446 | ||
![]() |
3e27d5fcb0 | ||
![]() |
48a100f49a | ||
![]() |
698649f981 | ||
![]() |
780078c3aa | ||
![]() |
4c25e4ddee | ||
![]() |
c0a5ac2ac5 | ||
![]() |
0435409870 | ||
![]() |
c521269409 | ||
![]() |
1e252b7e4c | ||
![]() |
d72b1edc48 | ||
![]() |
f7307e8e01 | ||
![]() |
127905f04b | ||
![]() |
261c6dabd5 | ||
![]() |
cae84bbf02 | ||
![]() |
cdb2bc52fa | ||
![]() |
cd2972eee0 | ||
![]() |
4036aa8d0e | ||
![]() |
52c6927c44 | ||
![]() |
a16e0a21a2 | ||
![]() |
e796b21157 | ||
![]() |
1c6bc478b4 | ||
![]() |
98f39c6388 | ||
![]() |
570c83571b | ||
![]() |
c0c38d89e0 | ||
![]() |
b866cfc03c | ||
![]() |
28c2755b37 | ||
![]() |
57bfc5c73a | ||
![]() |
0f3f7d53a3 | ||
![]() |
529e50fd7f | ||
![]() |
2fa283f91d | ||
![]() |
029a9ade93 | ||
![]() |
f1ca8b15c8 | ||
![]() |
4d8edd5da9 | ||
![]() |
6c63990653 | ||
![]() |
5b521409c6 | ||
![]() |
3268fc1014 | ||
![]() |
19afb4941b | ||
![]() |
40e5111d41 | ||
![]() |
a3a40e1e74 | ||
![]() |
101caa6826 | ||
![]() |
875fed8d77 | ||
![]() |
69e28eb000 | ||
![]() |
e5d3a8360c | ||
![]() |
4545d9285b | ||
![]() |
6702024805 | ||
![]() |
78bad4842b | ||
![]() |
b9a913cfed | ||
![]() |
6f5a6f353f | ||
![]() |
790c4f589d | ||
![]() |
cd1bd3461f | ||
![]() |
0280dcd6a8 | ||
![]() |
fc337292bc | ||
![]() |
fb1daa0e21 | ||
![]() |
579b9dc0c2 | ||
![]() |
dedd0be352 | ||
![]() |
1c7d9c3513 | ||
![]() |
0c7dfe2af4 | ||
![]() |
8d1351a8a3 | ||
![]() |
e6e68a6036 | ||
![]() |
24658edc45 | ||
![]() |
09eaa3116a | ||
![]() |
e9bff466b5 | ||
![]() |
5d77f50160 | ||
![]() |
2ab91e363f | ||
![]() |
34d881426f | ||
![]() |
13ecaa0ad4 | ||
![]() |
ce6185b1f7 | ||
![]() |
2cfde6b75a | ||
![]() |
37d0354751 | ||
![]() |
0a0edcf203 | ||
![]() |
d6aad2ea28 | ||
![]() |
63084506ee | ||
![]() |
c5d313574f | ||
![]() |
caab998212 | ||
![]() |
aa037cc3d9 | ||
![]() |
642bffe374 | ||
![]() |
d682b154fc | ||
![]() |
d4a06d98cf | ||
![]() |
856b5e16b1 | ||
![]() |
a0aa208860 | ||
![]() |
037a11e04f | ||
![]() |
bd8a1d715f | ||
![]() |
54ab1dc091 | ||
![]() |
9471e63857 | ||
![]() |
fa4a403f38 | ||
![]() |
d608d65bf4 | ||
![]() |
c0f2df172a | ||
![]() |
788ef5d81c | ||
![]() |
1c6b5cffe1 | ||
![]() |
c04382b623 | ||
![]() |
0bbe51f8fd | ||
![]() |
ff7d7d15a0 | ||
![]() |
4b3d083d3a | ||
![]() |
a566dd390b | ||
![]() |
7d1442da04 | ||
![]() |
17fc982f55 | ||
![]() |
ba417e2274 | ||
![]() |
d345094b75 | ||
![]() |
6da477480d | ||
![]() |
e274088c06 | ||
![]() |
1bcaa73c5c | ||
![]() |
ca94e8f621 | ||
![]() |
1c4e198f59 | ||
![]() |
fdd13f9c66 | ||
![]() |
4333ab624e | ||
![]() |
9fe1eb3a42 | ||
![]() |
ad251a7682 | ||
![]() |
1fa740de2d | ||
![]() |
466b89064a | ||
![]() |
2748cb0ba3 | ||
![]() |
aef0d5bdde | ||
![]() |
c71e8f024a | ||
![]() |
9411f07321 | ||
![]() |
9b2a5c9bbf | ||
![]() |
2b275523a0 | ||
![]() |
31fe2f6da4 | ||
![]() |
f95db623a5 | ||
![]() |
a46313e483 | ||
![]() |
31c330826e | ||
![]() |
c4cf800142 | ||
![]() |
b64a2b0006 | ||
![]() |
a3702f2270 | ||
![]() |
d221b1d470 | ||
![]() |
0b22a6bc1d | ||
![]() |
07e8acd003 | ||
![]() |
9fce617c57 | ||
![]() |
8d5c736975 | ||
![]() |
4ccec05186 | ||
![]() |
a4f456f002 | ||
![]() |
fbdb941c27 | ||
![]() |
a41cd42e8d | ||
![]() |
77521e4627 | ||
![]() |
b6a1242bac | ||
![]() |
2f325cfe26 | ||
![]() |
193b0ad0f0 | ||
![]() |
ed476b7793 | ||
![]() |
720fd94b7f | ||
![]() |
ff87da105c | ||
![]() |
a875e65536 | ||
![]() |
0b2c6bb662 | ||
![]() |
e44e2fbbb7 | ||
![]() |
b3c93644fd | ||
![]() |
a56b7ff636 | ||
![]() |
c724236930 | ||
![]() |
4853320b2b | ||
![]() |
ba1acb6ac1 | ||
![]() |
f32a6320fc | ||
![]() |
9f914ce36a | ||
![]() |
b037644e5a | ||
![]() |
afd8c59f83 | ||
![]() |
8aa4af3e91 | ||
![]() |
630a8a2b97 | ||
![]() |
dc34c4d00c | ||
![]() |
fb42729dec | ||
![]() |
b06989216a | ||
![]() |
e5144f08cd | ||
![]() |
c4a60190e8 | ||
![]() |
efe9e4fa4c | ||
![]() |
45800b1559 | ||
![]() |
b0b2b8104f | ||
![]() |
8dbc012825 | ||
![]() |
a434176063 | ||
![]() |
a013f750c7 | ||
![]() |
aa1f49d02f | ||
![]() |
7125a26309 | ||
![]() |
329a35ebf0 | ||
![]() |
d30043f595 | ||
![]() |
745dfa1911 | ||
![]() |
76203f49a7 | ||
![]() |
870a915377 | ||
![]() |
c174fce227 | ||
![]() |
2b6e42e919 | ||
![]() |
df73e1e5a3 | ||
![]() |
3e902311d4 | ||
![]() |
64a0037265 | ||
![]() |
bcd4e38093 | ||
![]() |
181a77d627 | ||
![]() |
b353595ba9 | ||
![]() |
75e3bb4f17 | ||
![]() |
d2fa9192d4 | ||
![]() |
4bcadc2de4 | ||
![]() |
8ddff74260 | ||
![]() |
95940fdb64 | ||
![]() |
9cd5708948 | ||
![]() |
d361683d79 | ||
![]() |
9ad17a01f7 | ||
![]() |
22ca1d443c | ||
![]() |
2662e875ca | ||
![]() |
8ae0d07ec1 | ||
![]() |
76a9edb7f5 | ||
![]() |
0ccb464e5b | ||
![]() |
bef600efa2 | ||
![]() |
58a182cd33 | ||
![]() |
aa43334f41 | ||
![]() |
a2a4c97f6c | ||
![]() |
4217ba99fd | ||
![]() |
589725f5cc | ||
![]() |
3fea4602f8 | ||
![]() |
8ea6aae875 | ||
![]() |
2c70b2af68 | ||
![]() |
54a2cbcb42 | ||
![]() |
fdef821c60 | ||
![]() |
dfa798a35d | ||
![]() |
39b8eb6ff1 | ||
![]() |
6cf71f67a9 | ||
![]() |
f2e919725e | ||
![]() |
869599126e | ||
![]() |
3b1b200f6f | ||
![]() |
93c646e3e4 | ||
![]() |
3552f80a21 | ||
![]() |
66d3a63998 | ||
![]() |
6447825978 | ||
![]() |
18b7df9fca | ||
![]() |
c3781cab96 | ||
![]() |
776098dba6 | ||
![]() |
8d1b4f61e7 | ||
![]() |
c13e2bdb96 | ||
![]() |
4682254157 | ||
![]() |
d7ca6b9213 | ||
![]() |
4a76afbde8 | ||
![]() |
a68349c23a | ||
![]() |
920e005366 | ||
![]() |
659f339020 | ||
![]() |
3ee2d463af | ||
![]() |
686ddb5460 | ||
![]() |
e5d62488b7 | ||
![]() |
eb93dd5005 | ||
![]() |
6999d02d2d | ||
![]() |
790e2b1427 | ||
![]() |
a29c7cdfe4 | ||
![]() |
6b7cd692a6 | ||
![]() |
4d3925872a | ||
![]() |
2bd0f6934a | ||
![]() |
51783f17ed | ||
![]() |
ce3aef3526 | ||
![]() |
ee70afdfbb | ||
![]() |
d96c4a56a2 | ||
![]() |
9a39513dea | ||
![]() |
8f22d63315 | ||
![]() |
7f2a5bb95e | ||
![]() |
0118dbd5fb | ||
![]() |
09405de26c | ||
![]() |
efa5ee0e57 | ||
![]() |
80d558f37a | ||
![]() |
901adc3fc7 | ||
![]() |
01417be954 | ||
![]() |
43b780cbe6 | ||
![]() |
e83f36a12f | ||
![]() |
77e3fc4ab0 | ||
![]() |
eafd1adaba | ||
![]() |
6b53abb7c9 | ||
![]() |
f994c5d284 | ||
![]() |
6fda220107 | ||
![]() |
da290ed1c3 | ||
![]() |
7e9cd80a1c | ||
![]() |
379b7413d8 | ||
![]() |
9181a4df16 | ||
![]() |
df982afd51 | ||
![]() |
5c2c3b4317 | ||
![]() |
92d1309103 | ||
![]() |
c43ee3c1d6 | ||
![]() |
e0726e5283 | ||
![]() |
5f3775584b | ||
![]() |
77873d63c5 | ||
![]() |
9e6b09765e | ||
![]() |
1ad6ea4049 | ||
![]() |
7c41da1cb9 | ||
![]() |
adcf4bfc53 | ||
![]() |
7a6321a9c1 | ||
![]() |
d56b27a7b0 | ||
![]() |
ed7657ab5f | ||
![]() |
a414838416 | ||
![]() |
93646577dc | ||
![]() |
46db66038e | ||
![]() |
efc4e9ce56 | ||
![]() |
8d5eac7f80 | ||
![]() |
7b94e49b81 | ||
![]() |
c35fd4bdc8 | ||
![]() |
98590e2d90 | ||
![]() |
e6da0e5dd5 | ||
![]() |
cb2baf747d | ||
![]() |
a2f2eb03ce | ||
![]() |
5c6acbb780 | ||
![]() |
1be7031199 | ||
![]() |
ed6399bde9 | ||
![]() |
6709893781 | ||
![]() |
686a426cda | ||
![]() |
4f90bc7813 | ||
![]() |
e307b289ae | ||
![]() |
3baeff61a7 | ||
![]() |
93ab9d12ee | ||
![]() |
36e1317792 | ||
![]() |
fa3e90a021 | ||
![]() |
782a69cf13 | ||
![]() |
d495f351c0 | ||
![]() |
30bd3d2d52 | ||
![]() |
ff5a21cca5 | ||
![]() |
f8abb73c92 | ||
![]() |
e97f323d9a | ||
![]() |
3d27a4c05d | ||
![]() |
9dbc13dbe4 | ||
![]() |
c46a4c75b1 | ||
![]() |
0bded73f16 | ||
![]() |
1333733684 | ||
![]() |
003be934de | ||
![]() |
93ef20d358 | ||
![]() |
94e1a6f0ba | ||
![]() |
8661d09d57 | ||
![]() |
0e5e21dc4e | ||
![]() |
3b25c4987c | ||
![]() |
2212eb17aa | ||
![]() |
768bac1db8 | ||
![]() |
3aef75085f | ||
![]() |
ce8bef638a | ||
![]() |
f0a0c90304 | ||
![]() |
cd6c32b21d | ||
![]() |
b31876d2d1 | ||
![]() |
ebab8a190e | ||
![]() |
1b7ce8e7a5 | ||
![]() |
646bb6bd79 | ||
![]() |
5a84b97ca9 | ||
![]() |
6d41b5a4a1 | ||
![]() |
a8bce36f3b | ||
![]() |
ac2132f8ba | ||
![]() |
cab4b57abe | ||
![]() |
938fb30359 | ||
![]() |
62346d7d9d | ||
![]() |
cf1e5ca64b | ||
![]() |
7d2d683d96 | ||
![]() |
fe5042f1c3 | ||
![]() |
a1dd76aee0 | ||
![]() |
d1c91be167 | ||
![]() |
9748d99f34 | ||
![]() |
c90ffbeb62 | ||
![]() |
eb7fafeabf | ||
![]() |
3e50629462 | ||
![]() |
65281a4554 | ||
![]() |
454ec09d6a | ||
![]() |
60e3c6858d | ||
![]() |
f911f5b4fc | ||
![]() |
ad1694d291 | ||
![]() |
1130965f26 | ||
![]() |
fe1f28998b | ||
![]() |
45727fce05 | ||
![]() |
d5c23e5add | ||
![]() |
e3a8285f6c | ||
![]() |
a791221cf6 | ||
![]() |
b954d9b403 | ||
![]() |
5e7e24a271 | ||
![]() |
ffb1e598f6 | ||
![]() |
bc2da8a645 | ||
![]() |
6f2be3ed30 | ||
![]() |
033a7bffb3 | ||
![]() |
f2b2ea61a1 | ||
![]() |
6f0783acc4 | ||
![]() |
ce60aa3823 | ||
![]() |
8075e70606 | ||
![]() |
4402fc2d0a | ||
![]() |
3e3ecda551 | ||
![]() |
50beb8f346 | ||
![]() |
8e033e3e06 | ||
![]() |
dc029a318b | ||
![]() |
8e91bc2c8e | ||
![]() |
0ff5b4e90b | ||
![]() |
20dec19bfe | ||
![]() |
d261fbff26 | ||
![]() |
6594b33bcc | ||
![]() |
a1bb6cc1b1 | ||
![]() |
7ce195b68e | ||
![]() |
16d8d04aaa | ||
![]() |
59565f7d90 | ||
![]() |
43784a2495 | ||
![]() |
3811d7469e | ||
![]() |
c72b40a1e1 | ||
![]() |
f00933969d | ||
![]() |
759adc45e3 | ||
![]() |
27ecf78372 | ||
![]() |
c91b83a7ba | ||
![]() |
39373ee63a | ||
![]() |
2db64c69ae | ||
![]() |
a699b71c02 | ||
![]() |
6c07d22cda | ||
![]() |
a2ee900ed5 | ||
![]() |
e709f31b99 | ||
![]() |
35afb12756 | ||
![]() |
9bed9fe162 | ||
![]() |
4ff5553804 | ||
![]() |
32213be7a7 | ||
![]() |
84894a73e1 | ||
![]() |
b6ea185ce7 | ||
![]() |
814ac0f731 | ||
![]() |
a40bb29da3 | ||
![]() |
e9b90079c0 | ||
![]() |
dba383c27e | ||
![]() |
42059b5817 | ||
![]() |
f92a17c01b | ||
![]() |
d6552ce333 | ||
![]() |
0db89bde5a | ||
![]() |
56a12185d4 | ||
![]() |
c40170db5d | ||
![]() |
1df3e9c414 | ||
![]() |
b1570df8b9 | ||
![]() |
023fd1ce36 | ||
![]() |
a7fe74bc0c | ||
![]() |
26c9abd9da | ||
![]() |
a5e34645c5 | ||
![]() |
b2831c0a19 | ||
![]() |
648c1ea0f9 | ||
![]() |
9cd927e06a | ||
![]() |
4272413f55 | ||
![]() |
e1711b7af6 | ||
![]() |
f7d3f27d45 | ||
![]() |
3a7a47f82d | ||
![]() |
cc211706d5 | ||
![]() |
22f74be4cd | ||
![]() |
5a00d14f94 | ||
![]() |
ecb4e7bf9f | ||
![]() |
56e5b546e1 | ||
![]() |
272f5a2f4f | ||
![]() |
ddcbe78a01 | ||
![]() |
00b6c964e2 | ||
![]() |
d7d2b06ecc | ||
![]() |
fafc59360d | ||
![]() |
19e105785e | ||
![]() |
b87ac09e43 | ||
![]() |
af9092d7c7 | ||
![]() |
24a1ffd652 | ||
![]() |
662813cc58 | ||
![]() |
d890b78290 | ||
![]() |
58747d7d4a | ||
![]() |
0773a4f39c | ||
![]() |
66cc7f8a1f | ||
![]() |
01ab40bf4a | ||
![]() |
4c09147fd1 | ||
![]() |
f9f426d788 | ||
![]() |
ff8fa1bf31 | ||
![]() |
59f99e4f6a | ||
![]() |
7449ce9c3b | ||
![]() |
f6bc8f0a1f | ||
![]() |
4d10b8cdee | ||
![]() |
5a61c5de09 | ||
![]() |
f84d0db811 | ||
![]() |
36ce3b08fe | ||
![]() |
da8ea5b545 | ||
![]() |
fad3dbf4cd | ||
![]() |
034d12c347 | ||
![]() |
c94dbf1d9a | ||
![]() |
e516687a9e | ||
![]() |
4a2f77b0a6 | ||
![]() |
7b29ecba71 | ||
![]() |
11241b8e07 | ||
![]() |
52bbd1f20b | ||
![]() |
4044750515 | ||
![]() |
b670c546b9 | ||
![]() |
f37bbf93cb | ||
![]() |
87311ab41a | ||
![]() |
ecb4d1845c | ||
![]() |
35c232ab25 | ||
![]() |
df0be2e251 | ||
![]() |
871b3a102b | ||
![]() |
02299e3892 | ||
![]() |
6af4d6f5b8 | ||
![]() |
4fb5700367 | ||
![]() |
8579276381 | ||
![]() |
7ba60b22c5 | ||
![]() |
031932f41c | ||
![]() |
079d0a89b1 | ||
![]() |
c4fdce6d64 | ||
![]() |
5604c2b29f | ||
![]() |
74b5ab2b47 | ||
![]() |
c29cbfe123 | ||
![]() |
6fe5cb1ffd | ||
![]() |
7edd5a7a8e | ||
![]() |
c1edc1b99b | ||
![]() |
4d1d890f72 | ||
![]() |
fe0f82fa2b | ||
![]() |
84083a65a8 | ||
![]() |
fc91c6bc08 | ||
![]() |
09120171ba | ||
![]() |
a362f920dc | ||
![]() |
9d7729f548 | ||
![]() |
ed56e177cf | ||
![]() |
9db28bd502 | ||
![]() |
aded70eb2e | ||
![]() |
dfbad85465 | ||
![]() |
52076fe182 | ||
![]() |
5575c3cb13 | ||
![]() |
637d32efff | ||
![]() |
fd54658e53 | ||
![]() |
2f39a8d76e | ||
![]() |
6a3e793500 | ||
![]() |
3b3ffeda6b | ||
![]() |
f7d92a3b11 | ||
![]() |
d9d9ba8bf1 | ||
![]() |
f5d9090183 | ||
![]() |
705ecd1ef1 | ||
![]() |
08b5266a86 | ||
![]() |
ecc4846ba8 | ||
![]() |
4aab705d11 | ||
![]() |
4615a68bcc | ||
![]() |
bf6934e8ac | ||
![]() |
af8c304bd4 | ||
![]() |
51dac5a5a8 | ||
![]() |
56463d9e36 | ||
![]() |
a6a339dc59 | ||
![]() |
8423304ab5 | ||
![]() |
bb7408dbe9 | ||
![]() |
7eff4dcf02 | ||
![]() |
d7ee3fec3d | ||
![]() |
5e026a3e8d | ||
![]() |
d5e117b89f | ||
![]() |
c87a5501df | ||
![]() |
7584ebba0b | ||
![]() |
66075e3960 | ||
![]() |
193ba781a0 | ||
![]() |
3e5dd64acc | ||
![]() |
d66ab7d389 | ||
![]() |
d2e6b27ecd | ||
![]() |
0588541357 | ||
![]() |
096ea84af6 | ||
![]() |
04d0cfd510 | ||
![]() |
7653f969ec |
@@ -5,7 +5,7 @@ root = true
|
|||||||
|
|
||||||
# Unix-style newlines with a newline ending every file
|
# Unix-style newlines with a newline ending every file
|
||||||
[*]
|
[*]
|
||||||
end_of_line = lf
|
end_of_line = lf|crlf
|
||||||
insert_final_newline = true
|
insert_final_newline = true
|
||||||
|
|
||||||
# Matches multiple files with brace expansion notation
|
# Matches multiple files with brace expansion notation
|
||||||
@@ -18,4 +18,4 @@ indent_style = space
|
|||||||
indent_size = 2
|
indent_size = 2
|
||||||
|
|
||||||
# Unfortunately, EditorConfig doesn't support space configuration inside import braces directly.
|
# Unfortunately, EditorConfig doesn't support space configuration inside import braces directly.
|
||||||
# You'll need to rely on your linter/formatter like ESLint or Prettier for that.
|
# You'll need to rely on your linter/formatter like ESLint or Prettier for that.
|
@@ -1 +1 @@
|
|||||||
VITE_BUILD_TYPE = Production
|
VITE_BUILD_TYPE = Production
|
||||||
|
@@ -1,9 +1,10 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
'env': {
|
'env': {
|
||||||
|
'browser': true,
|
||||||
'es2021': true,
|
'es2021': true,
|
||||||
'node': true
|
'node': true
|
||||||
},
|
},
|
||||||
'ignorePatterns': ['src/core/'],
|
'ignorePatterns': ['src/core/', 'src/core.lib/','src/proto/'],
|
||||||
'extends': [
|
'extends': [
|
||||||
'eslint:recommended',
|
'eslint:recommended',
|
||||||
'plugin:@typescript-eslint/recommended'
|
'plugin:@typescript-eslint/recommended'
|
||||||
|
81
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
Normal file
81
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
name: Bug 反馈
|
||||||
|
description: 报告可能的 NapCat 异常行为
|
||||||
|
title: '[BUG] '
|
||||||
|
labels: bug
|
||||||
|
body:
|
||||||
|
- type: markdown
|
||||||
|
attributes:
|
||||||
|
value: |
|
||||||
|
欢迎来到 NapCat 的 Issue Tracker!请填写以下表格来提交 Bug。
|
||||||
|
在提交新的 Bug 反馈前,请确保您:
|
||||||
|
* 已经搜索了现有的 issues,并且没有找到可以解决您问题的方法
|
||||||
|
* 不与现有的某一 issue 重复
|
||||||
|
- type: input
|
||||||
|
id: system-version
|
||||||
|
attributes:
|
||||||
|
label: 系统版本
|
||||||
|
description: 运行 QQNT 的系统版本
|
||||||
|
placeholder: Windows 10 Pro Workstation 22H2
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: input
|
||||||
|
id: qqnt-version
|
||||||
|
attributes:
|
||||||
|
label: QQNT 版本
|
||||||
|
description: 可在 QQNT 的「关于」的设置页中找到
|
||||||
|
placeholder: 9.9.7-21804
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: input
|
||||||
|
id: napcat-version
|
||||||
|
attributes:
|
||||||
|
label: NapCat 版本
|
||||||
|
description: 可在 LiteLoaderQQNT 的设置页或是 QQNT 的设置页侧栏中找到
|
||||||
|
placeholder: 1.0.0
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: input
|
||||||
|
id: onebot-client-version
|
||||||
|
attributes:
|
||||||
|
label: OneBot 客户端
|
||||||
|
description: 连接至 NapCat 的客户端版本信息
|
||||||
|
placeholder: Overflow 2.16.0-2cf7991-SNAPSHOT
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: what-happened
|
||||||
|
attributes:
|
||||||
|
label: 发生了什么?
|
||||||
|
description: 填写你认为的 NapCat 的不正常行为
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: how-reproduce
|
||||||
|
attributes:
|
||||||
|
label: 如何复现
|
||||||
|
description: 填写应当如何操作才能触发这个不正常行为
|
||||||
|
placeholder: |
|
||||||
|
1. xxx
|
||||||
|
2. xxx
|
||||||
|
3. xxx
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: what-expected
|
||||||
|
attributes:
|
||||||
|
label: 期望的结果?
|
||||||
|
description: 填写你认为 NapCat 应当执行的正常行为
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: napcat-log
|
||||||
|
attributes:
|
||||||
|
label: NapCat 运行日志
|
||||||
|
description: 粘贴相关日志内容到此处
|
||||||
|
render: shell
|
||||||
|
- type: textarea
|
||||||
|
id: onebot-client-log
|
||||||
|
attributes:
|
||||||
|
label: OneBot 客户端运行日志
|
||||||
|
description: 粘贴 OneBot 客户端的相关日志内容到此处
|
||||||
|
render: shell
|
74
.github/workflows/build.yml
vendored
Normal file
74
.github/workflows/build.yml
vendored
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
name: "Build"
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
|
||||||
|
permissions: write-all
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build-linux:
|
||||||
|
if: ${{ startsWith(github.event.head_commit.message, 'build:') }}
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
target_platform: [linux]
|
||||||
|
target_arch: [x64, arm64]
|
||||||
|
steps:
|
||||||
|
- name: Clone Main Repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
repository: 'NapNeko/NapCatQQ'
|
||||||
|
submodules: true
|
||||||
|
ref: main
|
||||||
|
token: ${{ secrets.NAPCAT_BUILD }}
|
||||||
|
- name: Use Node.js 20.X
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: 20.x
|
||||||
|
- name: Build NuCat Linux
|
||||||
|
run: |
|
||||||
|
npm i --arch=${{ matrix.target_arch }} --platform=${{ matrix.target_platform }}
|
||||||
|
npm run build:prod
|
||||||
|
cd dist
|
||||||
|
npm i --omit=dev --arch=${{ matrix.target_arch }} --platform=${{ matrix.target_platform }}
|
||||||
|
cd ..
|
||||||
|
- name: Upload Artifact
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: NapCat.${{ matrix.target_platform }}.${{ matrix.target_arch }}
|
||||||
|
path: dist
|
||||||
|
build-win32:
|
||||||
|
if: ${{ startsWith(github.event.head_commit.message, 'build:') }}
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
target_platform: [win32]
|
||||||
|
target_arch: [x64]
|
||||||
|
steps:
|
||||||
|
- name: Clone Main Repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
repository: 'NapNeko/NapCatQQ'
|
||||||
|
submodules: true
|
||||||
|
ref: main
|
||||||
|
token: ${{ secrets.NAPCAT_BUILD }}
|
||||||
|
- name: Use Node.js 20.X
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: 20.x
|
||||||
|
- name: Build NuCat Linux
|
||||||
|
run: |
|
||||||
|
npm i --arch=${{ matrix.target_arch }} --platform=${{ matrix.target_platform }}
|
||||||
|
npm run build:prod
|
||||||
|
cd dist
|
||||||
|
npm i --omit=dev --arch=${{ matrix.target_arch }} --platform=${{ matrix.target_platform }}
|
||||||
|
cd ..
|
||||||
|
- name: Upload Artifact
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: NapCat.${{ matrix.target_platform }}.${{ matrix.target_arch }}
|
||||||
|
path: dist
|
79
.github/workflows/release.yml
vendored
79
.github/workflows/release.yml
vendored
@@ -1,30 +1,60 @@
|
|||||||
name: "release"
|
name: "release"
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
tags:
|
tags:
|
||||||
- "v*"
|
- "v*"
|
||||||
|
|
||||||
|
permissions: write-all
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
check-version:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Clone Repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
ref: main
|
||||||
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Extract version from tag
|
||||||
|
run: echo "VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Use Node.js 20.X
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: 20.x
|
||||||
|
|
||||||
|
- name: Check Version
|
||||||
|
run: |
|
||||||
|
ls
|
||||||
|
node ./script/checkVersion.cjs
|
||||||
|
sh ./checkVersion.sh
|
||||||
build-linux:
|
build-linux:
|
||||||
|
needs: [check-version]
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
target_platform: [linux,darwin]
|
target_platform: [linux]
|
||||||
target_arch: [x64, arm64]
|
target_arch: [x64, arm64]
|
||||||
steps:
|
steps:
|
||||||
- name: Clone Main Repository
|
- name: Clone Main Repository
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
repository: 'NapNeko/NapCat'
|
repository: 'NapNeko/NapCatQQ'
|
||||||
submodules: true
|
submodules: true
|
||||||
|
ref: main
|
||||||
token: ${{ secrets.NAPCAT_BUILD }}
|
token: ${{ secrets.NAPCAT_BUILD }}
|
||||||
- name: Use Node.js 20.X
|
- name: Use Node.js 20.X
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: 20.x
|
node-version: 20.x
|
||||||
|
|
||||||
- name: Build NuCat Linux
|
- name: Build NuCat Linux
|
||||||
run: |
|
run: |
|
||||||
|
export NAPCAT_BUILDSYS=${{ matrix.target_platform }}
|
||||||
|
export NAPCAT_BUILDARCH=${{ matrix.target_arch }}
|
||||||
npm i --arch=${{ matrix.target_arch }} --platform=${{ matrix.target_platform }}
|
npm i --arch=${{ matrix.target_arch }} --platform=${{ matrix.target_platform }}
|
||||||
npm run build:prod
|
npm run build:prod
|
||||||
cd dist
|
cd dist
|
||||||
@@ -37,6 +67,7 @@ jobs:
|
|||||||
path: dist
|
path: dist
|
||||||
build-win32:
|
build-win32:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
needs: [check-version]
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
@@ -46,22 +77,62 @@ jobs:
|
|||||||
- name: Clone Main Repository
|
- name: Clone Main Repository
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
repository: 'NapNeko/NapCat'
|
repository: 'NapNeko/NapCatQQ'
|
||||||
submodules: true
|
submodules: true
|
||||||
|
ref: main
|
||||||
token: ${{ secrets.NAPCAT_BUILD }}
|
token: ${{ secrets.NAPCAT_BUILD }}
|
||||||
|
|
||||||
- name: Use Node.js 20.X
|
- name: Use Node.js 20.X
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: 20.x
|
node-version: 20.x
|
||||||
|
|
||||||
- name: Build NuCat Linux
|
- name: Build NuCat Linux
|
||||||
run: |
|
run: |
|
||||||
|
export NAPCAT_BUILDSYS=${{ matrix.target_platform }}
|
||||||
|
export NAPCAT_BUILDARCH=${{ matrix.target_arch }}
|
||||||
npm i --arch=${{ matrix.target_arch }} --platform=${{ matrix.target_platform }}
|
npm i --arch=${{ matrix.target_arch }} --platform=${{ matrix.target_platform }}
|
||||||
npm run build:prod
|
npm run build:prod
|
||||||
cd dist
|
cd dist
|
||||||
npm i --omit=dev --arch=${{ matrix.target_arch }} --platform=${{ matrix.target_platform }}
|
npm i --omit=dev --arch=${{ matrix.target_arch }} --platform=${{ matrix.target_platform }}
|
||||||
cd ..
|
cd ..
|
||||||
|
|
||||||
- name: Upload Artifact
|
- name: Upload Artifact
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: NapCat.${{ matrix.target_platform }}.${{ matrix.target_arch }}
|
name: NapCat.${{ matrix.target_platform }}.${{ matrix.target_arch }}
|
||||||
path: dist
|
path: dist
|
||||||
|
|
||||||
|
release-napcat:
|
||||||
|
needs: [build-win32,build-linux]
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Download All Artifact
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
|
||||||
|
- name: Compress subdirectories
|
||||||
|
run: |
|
||||||
|
for dir in */; do
|
||||||
|
base=$(basename "$dir")
|
||||||
|
zip -r "${base}.zip" "$dir"
|
||||||
|
done
|
||||||
|
|
||||||
|
- name: Extract version from tag
|
||||||
|
run: echo "VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Clone Changes Log
|
||||||
|
run: curl -o CHANGELOG.md https://fastly.jsdelivr.net/gh/NapNeko/NapCatQQ@main/docs/changelogs/CHANGELOG.v${{ env.VERSION }}.md
|
||||||
|
|
||||||
|
- name: Create Release Draft and Upload Artifacts
|
||||||
|
uses: softprops/action-gh-release@v1
|
||||||
|
with:
|
||||||
|
name: NapCat V${{ env.VERSION }}
|
||||||
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
body_path: CHANGELOG.md
|
||||||
|
files: |
|
||||||
|
NapCat.win32.x64.zip
|
||||||
|
NapCat.linux.x64.zip
|
||||||
|
NapCat.linux.arm64.zip
|
||||||
|
# NapCat.darwin.x64.zip
|
||||||
|
# NapCat.darwin.arm64.zip
|
||||||
|
draft: true
|
||||||
|
7
.gitignore
vendored
7
.gitignore
vendored
@@ -1,11 +1,11 @@
|
|||||||
# Logs
|
|
||||||
|
|
||||||
# Develop
|
# Develop
|
||||||
node_modules/
|
node_modules/
|
||||||
package-lock.json
|
package-lock.json
|
||||||
|
pnpm-lock.yaml
|
||||||
out/
|
out/
|
||||||
dist/
|
dist/
|
||||||
src/core.lib/common/
|
/src/core.lib/common/
|
||||||
|
/localdebug/
|
||||||
|
|
||||||
# Editor
|
# Editor
|
||||||
.vscode/*
|
.vscode/*
|
||||||
@@ -14,3 +14,4 @@ src/core.lib/common/
|
|||||||
|
|
||||||
# Build
|
# Build
|
||||||
*.db
|
*.db
|
||||||
|
checkVersion.sh
|
21
LICENSE
Normal file
21
LICENSE
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2024 NapCatQQ
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
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
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
153
README.md
153
README.md
@@ -4,159 +4,30 @@
|
|||||||
|
|
||||||
## 项目介绍
|
## 项目介绍
|
||||||
|
|
||||||
NapCatQQ(瞌睡猫QQ,不准叫我NCQQ!),像睡着了一样在后台低占用运行的无头(没有界面)的NTQQ
|
NapCatQQ 是基于 PC NTQQ 本体实现一套无头 Bot 框架。
|
||||||
|
|
||||||
目前测试在 Windows 上表现优秀,最低可达只占用内存 **20M**左右
|
名字寓意 瞌睡猫QQ,像睡着了一样在后台低占用运行的无需GUI界面的NTQQ。
|
||||||
|
|
||||||
由于 Linux 上的 QQ 图形依赖较多,会导致内存占用小高,大约 **100+M**,目前正在研究如何优化
|
## 如何使用
|
||||||
|
|
||||||
## 下载
|
可前往 [Release](https://github.com/NapNeko/NapCatQQ/releases/) 页面下载最新版本
|
||||||
|
|
||||||
前往 Release 页面下载最新版本
|
**首次使用** 请务必前往 [官方文档](https://napneko.github.io/) 查看使用文档与教程
|
||||||
|
|
||||||
## 启动
|
|
||||||
|
|
||||||
NapCat 是基于 官方NTQQ 实现的Bot框架,因此先需要安装官方QQ
|
|
||||||
|
|
||||||
*如果没有安装 QQ 请往后翻查看安装方法*
|
|
||||||
|
|
||||||
修改 `config/onebot11.json`内容,并重名为 `onebot11_<你的QQ号>.json`,如`onebot11_1234567.json`
|
|
||||||
|
|
||||||
json 配置内容参数解释:
|
|
||||||
|
|
||||||
```json5
|
|
||||||
{
|
|
||||||
// 是否启用http服务,如果启用,可以通过http接口发送消息
|
|
||||||
"enableHttp": false,
|
|
||||||
// http服务端口
|
|
||||||
"httpPort": 3000,
|
|
||||||
// 是否启用正向websocket服务
|
|
||||||
"enableWs": false,
|
|
||||||
// 正向websocket服务端口
|
|
||||||
"wsPort": 3001,
|
|
||||||
// 是否启用反向websocket服务
|
|
||||||
"enableWsReverse": false,
|
|
||||||
// 反向websocket对接的地址, 如["ws://127.0.0.1:8080/onebot/v11/ws"]
|
|
||||||
"wsReverseUrls": [],
|
|
||||||
// 是否启用http上报服务
|
|
||||||
"enableHttpPost": false,
|
|
||||||
// http上报地址, 如["http://127.0.0.1:8080/onebot/v11/http"]
|
|
||||||
"httpPostUrls": [],
|
|
||||||
// http上报密钥,可为空
|
|
||||||
"httpSecret": "",
|
|
||||||
// 消息上报格式,array为消息组,string为cq码字符串
|
|
||||||
"messagePostFormat": "array",
|
|
||||||
// 是否上报自己发送的消息
|
|
||||||
"reportSelfMessage": false,
|
|
||||||
// 是否开启调试模式,开启后上报消息会携带一个raw字段,为原始消息内容
|
|
||||||
"debug": false,
|
|
||||||
// 调用get_file接口时如果获取不到url则使用base64字段返回文件内容
|
|
||||||
"enableLocalFile2Url": true,
|
|
||||||
// ws心跳间隔,单位毫秒
|
|
||||||
"heartInterval": 30000,
|
|
||||||
// access_token,可以为空
|
|
||||||
"token": ""
|
|
||||||
}
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
### Windows 启动
|
|
||||||
|
|
||||||
运行`powershell ./napcat.ps1`, 或者 `napcat.bat`,如果出现乱码,可以尝试运行`napcat_utf8.ps1`
|
|
||||||
|
|
||||||
### Linux 启动
|
|
||||||
|
|
||||||
运行`napcat.sh`
|
|
||||||
|
|
||||||
## 使用无需扫码快速登录
|
|
||||||
|
|
||||||
前提是你已经成功登录过QQ,可以加参数` -q <你的QQ>` 进行登录,如`napcat.sh -q 1234567`
|
|
||||||
|
|
||||||
## 安装
|
|
||||||
|
|
||||||
### Linux安装
|
|
||||||
|
|
||||||
#### 安装 Linux QQ(22741),已经安装了的可以跳过
|
|
||||||
|
|
||||||
目前还在研究怎么精简安装,暂时只能安装官方QQ整体依赖
|
|
||||||
|
|
||||||
```bash
|
|
||||||
下载QQ的deb包
|
|
||||||
|
|
||||||
[deb x86版本](https://dldir1.qq.com/qqfile/qq/QQNT/Linux/QQ_3.2.7_240403_amd64_01.deb)
|
|
||||||
[deb arm版本](https://dldir1.qq.com/qqfile/qq/QQNT/Linux/QQ_3.2.7_240403_arm64_01.deb)
|
|
||||||
|
|
||||||
[rpm x86版本](https://dldir1.qq.com/qqfile/qq/QQNT/Linux/QQ_3.2.7_240403_x86_64_01.rpm)
|
|
||||||
[rpm arm版本](https://dldir1.qq.com/qqfile/qq/QQNT/Linux/QQ_3.2.7_240403_aarch64_01.rpm)
|
|
||||||
```
|
|
||||||
|
|
||||||
```bash
|
|
||||||
|
|
||||||
```bash
|
|
||||||
sudo apt install ./qq.deb
|
|
||||||
```
|
|
||||||
|
|
||||||
```bash
|
|
||||||
安装QQ的依赖
|
|
||||||
sudo apt install libgbm1 libasound2
|
|
||||||
```
|
|
||||||
|
|
||||||
### Windows 安装
|
|
||||||
|
|
||||||
#### 安装Windows QQ(22741),已经安装了的可以跳过
|
|
||||||
|
|
||||||
[Windows版本QQ下载](https://dldir1.qq.com/qqfile/qq/QQNT/Windows/QQ_9.9.9_240403_x64_01.exe)
|
|
||||||
|
|
||||||
### 编译安装 NapCat
|
|
||||||
|
|
||||||
**如果你是直接下载编译好的版本,可以跳过这一步**
|
|
||||||
|
|
||||||
准备环境 [node18.18](https://nodejs.org/download/release/v18.18.2/)
|
|
||||||
|
|
||||||
```
|
|
||||||
export NODE_ENV=production
|
|
||||||
npm install
|
|
||||||
```
|
|
||||||
|
|
||||||
## 常见问题
|
|
||||||
|
|
||||||
### 二维码无法扫描
|
|
||||||
|
|
||||||
NapCat 会自动保存二维码到目录,可以手动打开图片扫描
|
|
||||||
|
|
||||||
如果没有条件访问本地目录,可以将二维码解析的 url 复制到二维码生成网站上生成二维码,然后手机QQ扫描
|
|
||||||
|
|
||||||
### 语音、视频发送失败
|
|
||||||
|
|
||||||
需要配置 ffmpeg,将 ffmpeg 目录加入环境变量,如果仍未生效,可以修改 napcat 启动脚本加入 FFMPEG_PATH 变量指定到 ffmpeg
|
|
||||||
程序的完整路径
|
|
||||||
|
|
||||||
如 Windows 上修改 napcat.ps1,在第一行加入
|
|
||||||
|
|
||||||
```powershell
|
|
||||||
$env:FFMPEG_PATH="d:\ffmpeg\bin\ffmpeg.exe"
|
|
||||||
```
|
|
||||||
|
|
||||||
### 出现 error code v2:-1 之类的提示
|
|
||||||
|
|
||||||
不用管,这是正常现象,是因为 QQ 本身的问题,不影响使用
|
|
||||||
|
|
||||||
|
|
||||||
<!--
|
## 项目声明
|
||||||
QQ群:545402644
|
|
||||||
-->
|
|
||||||
|
|
||||||
## 声明
|
|
||||||
|
|
||||||
* 请不要在无关地方宣传NapCatQQ,本项目只是用于学习 node 相关知识,切勿用于违法用途
|
* 请不要在无关地方宣传NapCatQQ,本项目只是用于学习 node 相关知识,切勿用于违法用途
|
||||||
|
|
||||||
* NapCat 不会收集用户隐私信息,但是未来可能会为了更好的利于 NapCat 的优化会收集一些设备信息,如 cpu 架构,系统版本等
|
* NapCat 不会收集用户隐私信息,但是未来可能会为了更好的利于 NapCat 的优化会收集一些设备信息,如 cpu 架构,系统版本等
|
||||||
|
|
||||||
## 相关链接
|
## 相关链接
|
||||||
|
[Telegram Link](https://t.me/+nLZEnpne-pQ1OWFl)
|
||||||
[TG群](https://t.me/+nLZEnpne-pQ1OWFl)
|
|
||||||
|
|
||||||
## 鸣谢名单
|
## 鸣谢名单
|
||||||
[OpenShamrock]()
|
|
||||||
|
|
||||||
[Lagrange]()
|
[Lagrange](https://github.com/LagrangeDev/Lagrange.Core)
|
||||||
|
|
||||||
|
<!--
|
||||||
|
QQ群:545402644
|
||||||
|
-->
|
||||||
|
11
docs/changelogs/CHANGELOG.v1.5.5.md
Normal file
11
docs/changelogs/CHANGELOG.v1.5.5.md
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
# v1.5.5
|
||||||
|
|
||||||
|
QQ Version: Windows 9.9.11-24568 / Linux 3.2.9-23568
|
||||||
|
|
||||||
|
## 修复与优化
|
||||||
|
* 紧急修复一些问题
|
||||||
|
|
||||||
|
## 新增与调整
|
||||||
|
|
||||||
|
|
||||||
|
新增的 API 详细见[API文档](https://napneko.github.io/zh-CN/develop/extends_api)
|
17
docs/changelogs/old/CHANGELOG.v1.3.3.md
Normal file
17
docs/changelogs/old/CHANGELOG.v1.3.3.md
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
# v1.3.3
|
||||||
|
|
||||||
|
QQ Version: Windows 9.9.9-23424 / Linux 3.2.7-23361
|
||||||
|
|
||||||
|
## 修复与优化
|
||||||
|
* 尝试修复多开崩溃问题
|
||||||
|
* 修复群列表更新问题
|
||||||
|
* 修复兼容性问题支持Win7
|
||||||
|
* 修复下载 http 资源缺少UA
|
||||||
|
* 优化少量消息合并转发速度
|
||||||
|
* 修复加载群通知时出现 getUserDetailInfo timeout 导致程序崩溃
|
||||||
|
|
||||||
|
## 新增与调整
|
||||||
|
* 新增设置群公告 Api: /_send_group_notice
|
||||||
|
* 新增重启实现 包括重启快速登录/普通重启 副作用: 原进程 无法清理
|
||||||
|
|
||||||
|
新增的 API 详细见[API文档](https://napneko.github.io/zh-CN/develop/extends_api)
|
18
docs/changelogs/old/CHANGELOG.v1.3.5.md
Normal file
18
docs/changelogs/old/CHANGELOG.v1.3.5.md
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
# v1.3.5
|
||||||
|
|
||||||
|
QQ Version: Windows 9.9.9-23424 / Linux 3.2.7-23361
|
||||||
|
|
||||||
|
## 修复与优化
|
||||||
|
* 优化启动脚本
|
||||||
|
* 修复非管理时群成员减少事件上报 **无法获取操作者与操作类型**
|
||||||
|
* 修复快速重启进程清理问题
|
||||||
|
* 优化配置文件格式 支持自动更新配置 但仍然建议 **备份配置**
|
||||||
|
* 修复正向反向ws多个客户端周期多次心跳问题
|
||||||
|
|
||||||
|
## 新增与调整
|
||||||
|
* 支持WebUi热重载
|
||||||
|
* 新增启动输出WEBUI秘钥
|
||||||
|
* 新增群荣誉信息 /get_group_honor_info
|
||||||
|
* 支持获取群系统消息 /get_group_system_msg
|
||||||
|
|
||||||
|
新增的 API 详细见[API文档](https://napneko.github.io/zh-CN/develop/extends_api)
|
11
docs/changelogs/old/CHANGELOG.v1.3.6.md
Normal file
11
docs/changelogs/old/CHANGELOG.v1.3.6.md
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
# v1.3.6
|
||||||
|
|
||||||
|
QQ Version: Windows 9.9.9-23424 / Linux 3.2.7-23361
|
||||||
|
|
||||||
|
## 修复与优化
|
||||||
|
* 修复戳一戳多次上报问题
|
||||||
|
|
||||||
|
## 新增与调整
|
||||||
|
|
||||||
|
|
||||||
|
新增的 API 详细见[API文档](https://napneko.github.io/zh-CN/develop/extends_api)
|
15
docs/changelogs/old/CHANGELOG.v1.3.8.md
Normal file
15
docs/changelogs/old/CHANGELOG.v1.3.8.md
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
# v1.3.8
|
||||||
|
|
||||||
|
QQ Version: Windows 9.9.9-23873 / Linux 3.2.7-23361
|
||||||
|
|
||||||
|
## 修复与优化
|
||||||
|
* 优化打包后体积问题
|
||||||
|
* 修复QQ等级获取
|
||||||
|
* 兼容 9.7.x 版本换行符 统一为 \n
|
||||||
|
* 修复处理加群请求 字段异常情况
|
||||||
|
* 修复退群通知问题
|
||||||
|
|
||||||
|
## 新增与调整
|
||||||
|
|
||||||
|
|
||||||
|
新增的 API 详细见[API文档](https://napneko.github.io/zh-CN/develop/extends_api)
|
11
docs/changelogs/old/CHANGELOG.v1.3.9.md
Normal file
11
docs/changelogs/old/CHANGELOG.v1.3.9.md
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
# v1.3.9
|
||||||
|
|
||||||
|
QQ Version: Windows 9.9.10-23873 / Linux 3.2.7-23361
|
||||||
|
|
||||||
|
## 修复与优化
|
||||||
|
* 修复QQ等级获取与兼容性问题
|
||||||
|
|
||||||
|
## 新增与调整
|
||||||
|
|
||||||
|
|
||||||
|
新增的 API 详细见[API文档](https://napneko.github.io/zh-CN/develop/extends_api)
|
12
docs/changelogs/old/CHANGELOG.v1.4.0.md
Normal file
12
docs/changelogs/old/CHANGELOG.v1.4.0.md
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
# v1.4.0
|
||||||
|
|
||||||
|
QQ Version: Windows 9.9.10-24108 / Linux 3.2.7-23361
|
||||||
|
|
||||||
|
## 修复与优化
|
||||||
|
|
||||||
|
## 新增与调整
|
||||||
|
* 支持空间Cookies获取
|
||||||
|
* 支持获取在线设备 API /get_online_clients
|
||||||
|
* 支持图片OCR API: /.ocr_image /ocr_image
|
||||||
|
|
||||||
|
新增的 API 详细见[API文档](https://napneko.github.io/zh-CN/develop/extends_api)
|
14
docs/changelogs/old/CHANGELOG.v1.4.1.md
Normal file
14
docs/changelogs/old/CHANGELOG.v1.4.1.md
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
# v1.4.1
|
||||||
|
|
||||||
|
QQ Version: Windows 9.9.10-24108 / Linux 3.2.7-23361
|
||||||
|
|
||||||
|
## 修复与优化
|
||||||
|
* 提高部分Api兼容性
|
||||||
|
* 优化日志膨胀问题
|
||||||
|
* 在线状态刷新问题修复
|
||||||
|
## 新增与调整
|
||||||
|
* 支持非管理群 本地记录时间数据 (建议 **备份配置 清空配置 重新配置**)
|
||||||
|
* 新增英译中接口 Api: /translate_en2zh
|
||||||
|
* 新增群文件管理相关扩展接口 Api: /get_group_file_count /get_group_file_list /set_group_file_folder /del_group_file /del_group_file_folder
|
||||||
|
|
||||||
|
新增的 API 详细见[API文档](https://napneko.github.io/zh-CN/develop/extends_api)
|
12
docs/changelogs/old/CHANGELOG.v1.4.2.md
Normal file
12
docs/changelogs/old/CHANGELOG.v1.4.2.md
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
# v1.4.2
|
||||||
|
|
||||||
|
QQ Version: Windows 9.9.10-24108 / Linux 3.2.7-23361
|
||||||
|
|
||||||
|
## 修复与优化
|
||||||
|
* 修复获取群文件列表Api
|
||||||
|
* 修复退群通知问题
|
||||||
|
|
||||||
|
## 新增与调整
|
||||||
|
|
||||||
|
|
||||||
|
新增的 API 详细见[API文档](https://napneko.github.io/zh-CN/develop/extends_api)
|
11
docs/changelogs/old/CHANGELOG.v1.4.3.md
Normal file
11
docs/changelogs/old/CHANGELOG.v1.4.3.md
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
# v1.4.3
|
||||||
|
|
||||||
|
QQ Version: Windows 9.9.10-24108 / Linux 3.2.7-23361
|
||||||
|
|
||||||
|
## 修复与优化
|
||||||
|
* 修复名片通知
|
||||||
|
|
||||||
|
## 新增与调整
|
||||||
|
|
||||||
|
|
||||||
|
新增的 API 详细见[API文档](https://napneko.github.io/zh-CN/develop/extends_api)
|
10
docs/changelogs/old/CHANGELOG.v1.4.4.md
Normal file
10
docs/changelogs/old/CHANGELOG.v1.4.4.md
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
# v1.4.4
|
||||||
|
|
||||||
|
QQ Version: Windows 9.9.10-24108 / Linux 3.2.7-23361
|
||||||
|
|
||||||
|
## 更新
|
||||||
|
* **重大更新:**更新了版本号
|
||||||
|
|
||||||
|
|
||||||
|
新增的 API 详细见[API文档](https://napneko.github.io/zh-CN/develop/extends_api)
|
||||||
|
|
12
docs/changelogs/old/CHANGELOG.v1.4.5.md
Normal file
12
docs/changelogs/old/CHANGELOG.v1.4.5.md
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
# v1.4.5
|
||||||
|
|
||||||
|
QQ Version: Windows 9.9.10-24108 / Linux 3.2.7-23361
|
||||||
|
|
||||||
|
## 修复与优化
|
||||||
|
* 紧急修复二维扫码问题
|
||||||
|
|
||||||
|
## 新增与调整
|
||||||
|
|
||||||
|
|
||||||
|
新增的 API 详细见[API文档](https://napneko.github.io/zh-CN/develop/extends_api)
|
||||||
|
|
12
docs/changelogs/old/CHANGELOG.v1.4.6.md
Normal file
12
docs/changelogs/old/CHANGELOG.v1.4.6.md
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
# v1.4.6
|
||||||
|
|
||||||
|
QQ Version: Windows 9.9.10-24108 / Linux 3.2.7-23361
|
||||||
|
|
||||||
|
## 修复与优化
|
||||||
|
* 优化整体稳定性
|
||||||
|
|
||||||
|
## 新增与调整
|
||||||
|
|
||||||
|
|
||||||
|
新增的 API 详细见[API文档](https://napneko.github.io/zh-CN/develop/extends_api)
|
||||||
|
|
11
docs/changelogs/old/CHANGELOG.v1.4.7.md
Normal file
11
docs/changelogs/old/CHANGELOG.v1.4.7.md
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
# v1.4.7
|
||||||
|
|
||||||
|
QQ Version: Windows 9.9.10-24108 / Linux 3.2.7-23361
|
||||||
|
|
||||||
|
## 修复与优化
|
||||||
|
* 临时扩展 Api: GoCQHTTPUploadGroupFile folder_id字段 用于选择文件夹
|
||||||
|
|
||||||
|
## 新增与调整
|
||||||
|
|
||||||
|
|
||||||
|
新增的 API 详细见[API文档](https://napneko.github.io/zh-CN/develop/extends_api)
|
12
docs/changelogs/old/CHANGELOG.v1.4.8.md
Normal file
12
docs/changelogs/old/CHANGELOG.v1.4.8.md
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
# v1.4.8
|
||||||
|
|
||||||
|
QQ Version: Windows 9.9.10-24108 / Linux 3.2.7-23361
|
||||||
|
|
||||||
|
## 修复与优化
|
||||||
|
* 优化Guid的生成方式
|
||||||
|
* 支持临时消息获取群来源
|
||||||
|
|
||||||
|
## 新增与调整
|
||||||
|
|
||||||
|
|
||||||
|
新增的 API 详细见[API文档](https://napneko.github.io/zh-CN/develop/extends_api)
|
11
docs/changelogs/old/CHANGELOG.v1.4.9.md
Normal file
11
docs/changelogs/old/CHANGELOG.v1.4.9.md
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
# v1.4.9
|
||||||
|
|
||||||
|
QQ Version: Windows 9.9.10-24108 / Linux 3.2.7-23361
|
||||||
|
|
||||||
|
## 修复与优化
|
||||||
|
* 修复接口调用问题 接口标准化 API:set_group_add_request
|
||||||
|
|
||||||
|
## 新增与调整
|
||||||
|
|
||||||
|
|
||||||
|
新增的 API 详细见[API文档](https://napneko.github.io/zh-CN/develop/extends_api)
|
11
docs/changelogs/old/CHANGELOG.v1.5.0.md
Normal file
11
docs/changelogs/old/CHANGELOG.v1.5.0.md
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
# v1.5.0
|
||||||
|
|
||||||
|
QQ Version: Windows 9.9.10-24108 / Linux 3.2.7-23361
|
||||||
|
|
||||||
|
## 修复与优化
|
||||||
|
* 修正各Api默认值
|
||||||
|
|
||||||
|
## 新增与调整
|
||||||
|
|
||||||
|
|
||||||
|
新增的 API 详细见[API文档](https://napneko.github.io/zh-CN/develop/extends_api)
|
12
docs/changelogs/old/CHANGELOG.v1.5.1.md
Normal file
12
docs/changelogs/old/CHANGELOG.v1.5.1.md
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
# v1.5.1
|
||||||
|
|
||||||
|
QQ Version: Windows 9.9.10-24108 / Linux 3.2.7-23361
|
||||||
|
|
||||||
|
## 修复与优化
|
||||||
|
* 支持 新Api: set_self_profile 可设置个性签名
|
||||||
|
* 修复 Api: get_group_system_msg
|
||||||
|
* 整理日志、添加颜色、使用统一的日志函数以提高日志可读性
|
||||||
|
## 新增与调整
|
||||||
|
|
||||||
|
|
||||||
|
新增的 API 详细见[API文档](https://napneko.github.io/zh-CN/develop/extends_api)
|
13
docs/changelogs/old/CHANGELOG.v1.5.2.md
Normal file
13
docs/changelogs/old/CHANGELOG.v1.5.2.md
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
# v1.5.2
|
||||||
|
|
||||||
|
QQ Version: Windows 9.9.10-24108 / Linux 3.2.7-23361
|
||||||
|
|
||||||
|
## 修复与优化
|
||||||
|
* 替换Uid/Uin为内部实现
|
||||||
|
* 增加HttpApi调用稳定性
|
||||||
|
* 修复 GetMsg 兼容性
|
||||||
|
|
||||||
|
## 新增与调整
|
||||||
|
* 支持真正意义上的陌生人信息获取 Api: GoCQHTTP_GetStrangerInfo
|
||||||
|
|
||||||
|
新增的 API 详细见[API文档](https://napneko.github.io/zh-CN/develop/extends_api)
|
15
docs/changelogs/old/CHANGELOG.v1.5.3.md
Normal file
15
docs/changelogs/old/CHANGELOG.v1.5.3.md
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
# v1.5.3
|
||||||
|
|
||||||
|
QQ Version: Windows 9.9.11-24568 / Linux 3.2.9-23568
|
||||||
|
|
||||||
|
## 修复与优化
|
||||||
|
* 修复引用消息id问题
|
||||||
|
* 修复添加好友的通知
|
||||||
|
|
||||||
|
## 新增与调整
|
||||||
|
* 扩展群分享Json生成
|
||||||
|
* 扩展关于收藏的一系列接口
|
||||||
|
* 支持专属群头衔获取
|
||||||
|
* 支持视频获取直链
|
||||||
|
|
||||||
|
新增的 API 详细见[API文档](https://napneko.github.io/zh-CN/develop/extends_api)
|
11
docs/changelogs/old/CHANGELOG.v1.5.4.md
Normal file
11
docs/changelogs/old/CHANGELOG.v1.5.4.md
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
# v1.5.4
|
||||||
|
|
||||||
|
QQ Version: Windows 9.9.11-24568 / Linux 3.2.9-23568
|
||||||
|
|
||||||
|
## 修复与优化
|
||||||
|
* 紧急修复视频与文件问题
|
||||||
|
|
||||||
|
## 新增与调整
|
||||||
|
|
||||||
|
|
||||||
|
新增的 API 详细见[API文档](https://napneko.github.io/zh-CN/develop/extends_api)
|
2
docs/develop/Android.md
Normal file
2
docs/develop/Android.md
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
# 开始
|
||||||
|
jadx 跳转于 `com.tencent.qqnt.kernel.*`
|
42
docs/develop/GetMemberExt.md
Normal file
42
docs/develop/GetMemberExt.md
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
# 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
|
||||||
|
```
|
48
docs/develop/Image.NTAndroid.md
Normal file
48
docs/develop/Image.NTAndroid.md
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
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_QUIC_DOMAIN = "c2cpicdw.quic.qpic.cn";
|
||||||
|
public static final int FLAG_NOT_UPLOAD = 3;
|
||||||
|
public static final int FLAG_UPLOADINFO_ERROR = 4;
|
||||||
|
public static final int GROUP_PIC_DOWNLOAD = 1000;
|
||||||
|
public static final String GROUP_PIC_DOWNLOAD_DOMAIN = "gchat.qpic.cn";
|
||||||
|
public static final String GROUP_PIC_DOWNLOAD_QUIC_DOMAIN = "gchat.quic.qpic.cn";
|
||||||
|
public static final String GUILD_PIC_DOWNLOAD_DOMAIN = "gchat.qpic.cn/qmeetpic";
|
||||||
|
public static final boolean NEW_STORE_FLAG = true;
|
||||||
|
public static final String PTT_VIDEO_DOWNLOAD_DOMAIN = "grouptalk.c2c.qq.com";
|
||||||
|
|
||||||
|
protected static final int AVIF_DECODE_EXCEPTION = 4;
|
||||||
|
protected static final int AVIF_DECODE_FAIL = 1;
|
||||||
|
protected static final int AVIF_DECODE_FAIL_SO_FAIL = 2;
|
||||||
|
protected static final int AVIF_DECODE_FAIL_UNKNOWN = 6;
|
||||||
|
protected static final int AVIF_DECODE_FILETYPE_ERROR = 5;
|
||||||
|
protected static final int AVIF_DECODE_OOM = 3;
|
||||||
|
protected static final int AVIF_DECODE_RENAME_FAIL = 7;
|
||||||
|
protected static final int AVIF_DECODE_SUC = 0;
|
||||||
|
public static final String AVIF_FILE_SUFFIX = ".avif";
|
||||||
|
public static final int AVIF_REQ_APPRUNTIME_NULL = 12;
|
||||||
|
public static final int AVIF_REQ_CODEC_UNSURPPORT = 5;
|
||||||
|
protected static final int AVIF_REQ_DENSITY_UNSURPPORT = 10;
|
||||||
|
protected static final int AVIF_REQ_FLASH_PHOTO = 9;
|
||||||
|
protected static final int AVIF_REQ_HAS_TMP_AVIF = 7;
|
||||||
|
protected static final int AVIF_REQ_INVALID_MSG_RECORD = 2;
|
||||||
|
protected static final int AVIF_REQ_IS_RAW_PHOTO = 3;
|
||||||
|
protected static final int AVIF_REQ_OUTPUTSTREAM_UNSURPPORT = 11;
|
||||||
|
protected static final int AVIF_REQ_OVERSIZE = 6;
|
||||||
|
protected static final int AVIF_REQ_RETRY = 1;
|
||||||
|
public static final int AVIF_REQ_SO_DOWNLOAD_FAILED = 8;
|
||||||
|
protected static final int AVIF_REQ_SUC = 0;
|
||||||
|
public static final int AVIF_REQ_SWITCH_CLOSE = 4;
|
||||||
|
public static final String C2C_PIC_DOWNLOAD_ERROR_CODE = "C2CPicDownloadErrorCode";
|
||||||
|
static final int DOWNLOAD_ST_COMPLETE = 1;
|
||||||
|
static final int DOWNLOAD_ST_HEAD = 2;
|
||||||
|
static final int DOWNLOAD_ST_LEFT = 4;
|
||||||
|
static final int DOWNLOAD_ST_PART = 3;
|
||||||
|
private static final int ENCRYPT_APPID = 1600000226;
|
||||||
|
public static final String GROUP_PIC_DOWNLOAD_ERROR_CODE = "GroupPicDownloadErrorCode";
|
||||||
|
public static final String KEY_PIC_DOWNLOAD_ERROR_CODE = "param_detail_code";
|
||||||
|
protected static final int QUIC_FAIL_IP_LIST_EMPTY = 1;
|
||||||
|
protected static final int QUIC_FAIL_REQUEST_HTTPS = 3;
|
||||||
|
protected static final int QUIC_FAIL_REQUEST_QUIC = 2;
|
||||||
|
protected static final int QUIC_FAIL_SO_LOAD = 4;
|
||||||
|
public static final String REPORT_TAG_DIRECT_DOWNLOAD_FAIL = "report_direct_download_fail";
|
||||||
|
public static final String REQ_PARAM_AVIF = "tp=avif";
|
444
docs/develop/Msg常量NTAndroid.md
Normal file
444
docs/develop/Msg常量NTAndroid.md
Normal file
@@ -0,0 +1,444 @@
|
|||||||
|
```java
|
||||||
|
MsgConstant
|
||||||
|
int ARKSTRUCTELEMENTSUBTYPETENCENTDOCFROMMINIAPP = 1;
|
||||||
|
int ARKSTRUCTELEMENTSUBTYPETENCENTDOCFROMPLUSPANEL = 2;
|
||||||
|
int ARKSTRUCTELEMENTSUBTYPEUNKNOWN = 0;
|
||||||
|
int ATTYPEALL = 1;
|
||||||
|
int ATTYPECATEGORY = 512;
|
||||||
|
int ATTYPECHANNEL = 16;
|
||||||
|
int ATTYPEME = 4;
|
||||||
|
int ATTYPEONE = 2;
|
||||||
|
int ATTYPEONLINE = 64;
|
||||||
|
int ATTYPEROLE = 8;
|
||||||
|
int ATTYPESUMMON = 32;
|
||||||
|
int ATTYPESUMMONONLINE = 128;
|
||||||
|
int ATTYPESUMMONROLE = 256;
|
||||||
|
int ATTYPEUNKNOWN = 0;
|
||||||
|
int CALENDARELEMSUBTYPECOMMON = 3;
|
||||||
|
int CALENDARELEMSUBTYPESTRONG = 1;
|
||||||
|
int CALENDARELEMSUBTYPEUNKNOWN = 0;
|
||||||
|
int CALENDARELEMSUBTYPEWEAK = 2;
|
||||||
|
int FACEBUBBLEELEMSUBTYPENORMAL = 1;
|
||||||
|
int FACEBUBBLEELEMSUBTYPEUNKNOWN = 0;
|
||||||
|
int FETCHLONGMSGERRCODEMSGEXPIRED = 196;
|
||||||
|
int FILEELEMENTSUBTYPEAI = 16;
|
||||||
|
int FILEELEMENTSUBTYPEAPP = 11;
|
||||||
|
int FILEELEMENTSUBTYPEAUDIO = 3;
|
||||||
|
int FILEELEMENTSUBTYPEDOC = 4;
|
||||||
|
int FILEELEMENTSUBTYPEEMOTICON = 15;
|
||||||
|
int FILEELEMENTSUBTYPEEXCEL = 6;
|
||||||
|
int FILEELEMENTSUBTYPEFOLDER = 13;
|
||||||
|
int FILEELEMENTSUBTYPEHTML = 10;
|
||||||
|
int FILEELEMENTSUBTYPEIPA = 14;
|
||||||
|
int FILEELEMENTSUBTYPENORMAL = 0;
|
||||||
|
int FILEELEMENTSUBTYPEPDF = 7;
|
||||||
|
int FILEELEMENTSUBTYPEPIC = 1;
|
||||||
|
int FILEELEMENTSUBTYPEPPT = 5;
|
||||||
|
int FILEELEMENTSUBTYPEPSD = 12;
|
||||||
|
int FILEELEMENTSUBTYPETXT = 8;
|
||||||
|
int FILEELEMENTSUBTYPEVIDEO = 2;
|
||||||
|
int FILEELEMENTSUBTYPEZIP = 9;
|
||||||
|
int GRAYTIPELEMENTSUBTYPEAIOOP = 15;
|
||||||
|
int GRAYTIPELEMENTSUBTYPEBLOCK = 14;
|
||||||
|
int GRAYTIPELEMENTSUBTYPEBUDDY = 5;
|
||||||
|
int GRAYTIPELEMENTSUBTYPEBUDDYNOTIFY = 9;
|
||||||
|
int GRAYTIPELEMENTSUBTYPEEMOJIREPLY = 3;
|
||||||
|
int GRAYTIPELEMENTSUBTYPEESSENCE = 7;
|
||||||
|
int GRAYTIPELEMENTSUBTYPEFEED = 6;
|
||||||
|
int GRAYTIPELEMENTSUBTYPEFEEDCHANNELMSG = 11;
|
||||||
|
int GRAYTIPELEMENTSUBTYPEFILE = 10;
|
||||||
|
int GRAYTIPELEMENTSUBTYPEGROUP = 4;
|
||||||
|
int GRAYTIPELEMENTSUBTYPEGROUPNOTIFY = 8;
|
||||||
|
int GRAYTIPELEMENTSUBTYPEJSON = 17;
|
||||||
|
int GRAYTIPELEMENTSUBTYPELOCALMSG = 13;
|
||||||
|
int GRAYTIPELEMENTSUBTYPEPROCLAMATION = 2;
|
||||||
|
int GRAYTIPELEMENTSUBTYPEREVOKE = 1;
|
||||||
|
int GRAYTIPELEMENTSUBTYPEUNKNOWN = 0;
|
||||||
|
int GRAYTIPELEMENTSUBTYPEWALLET = 16;
|
||||||
|
int GRAYTIPELEMENTSUBTYPEXMLMSG = 12;
|
||||||
|
int INLINEKEYBOARDBUTTONRENDERSTYLEBLUEBLACKGROUND = 4;
|
||||||
|
int INLINEKEYBOARDBUTTONRENDERSTYLEBLUEBORDER = 1;
|
||||||
|
int INLINEKEYBOARDBUTTONRENDERSTYLEGRAYBORDER = 0;
|
||||||
|
int INLINEKEYBOARDBUTTONRENDERSTYLENOBORDER = 2;
|
||||||
|
int INLINEKEYBOARDBUTTONRENDERSTYLEREDCHARACTER = 3;
|
||||||
|
int INPUTSTATUSTYPECANCEL = 2;
|
||||||
|
int INPUTSTATUSTYPESPEAK = 3;
|
||||||
|
int INPUTSTATUSTYPETEXT = 1;
|
||||||
|
int KACTIVITYMSG = 22;
|
||||||
|
int KADDLOCALMSGEXTINFOTYPEPROLOGUEMSG = 1;
|
||||||
|
int KANONYMOUSATMEMSGTYPEINMSGBOX = 1001;
|
||||||
|
int KANONYMOUSFLAGFROMOTHERPEOPLE = 1;
|
||||||
|
int KANONYMOUSFLAGFROMOWN = 2;
|
||||||
|
int KANONYMOUSFLAGINVALID = 0;
|
||||||
|
int KAPPCHANNELMSG = 16;
|
||||||
|
int KATALLMSGTYPEINMSGBOX = 2000;
|
||||||
|
int KATMEMSGTYPEINMSGBOX = 1000;
|
||||||
|
int KATTRIBUTETYPEADELIEMSG = 16;
|
||||||
|
int KATTRIBUTETYPEEXTENDBUSINESS = 13;
|
||||||
|
int KATTRIBUTETYPEFEEDBACKSTATE = 17;
|
||||||
|
int KATTRIBUTETYPEGROUPHONOR = 2;
|
||||||
|
int KATTRIBUTETYPEKINGHONOR = 3;
|
||||||
|
int KATTRIBUTETYPELONGMSG = 8;
|
||||||
|
int KATTRIBUTETYPEMEMORYSTATEMSGINFO = 18;
|
||||||
|
int KATTRIBUTETYPEMSG = 0;
|
||||||
|
int KATTRIBUTETYPEMSGBOXEVENTTYPE = 14;
|
||||||
|
int KATTRIBUTETYPEPERSONAL = 1;
|
||||||
|
int KATTRIBUTETYPEPUBLICACCOUNT = 4;
|
||||||
|
int KATTRIBUTETYPEQQCONNECT = 12;
|
||||||
|
int KATTRIBUTETYPESENDMSGRSPTRANSSVRINFO = 15;
|
||||||
|
int KATTRIBUTETYPESHAREDMSGINFO = 5;
|
||||||
|
int KATTRIBUTETYPETEMPCHATGAMESESSION = 6;
|
||||||
|
int KATTRIBUTETYPETOROBOTMSG = 9;
|
||||||
|
int KATTRIBUTETYPEUININFO = 7;
|
||||||
|
int KATTRIBUTETYPEZPLAN = 11;
|
||||||
|
int KAUTOREPLYTEXTNONEINDEX = -1;
|
||||||
|
int KAVRECORDMSG = 19;
|
||||||
|
int KBUSINESSTYPGUILD = 1;
|
||||||
|
int KBUSINESSTYPNT = 0;
|
||||||
|
int KCHATTYPEADELIE = 42;
|
||||||
|
int KCHATTYPEBUDDYNOTIFY = 5;
|
||||||
|
int KCHATTYPEC2C = 1;
|
||||||
|
int KCHATTYPECIRCLE = 113;
|
||||||
|
int KCHATTYPEDATALINE = 8;
|
||||||
|
int KCHATTYPEDATALINEMQQ = 134;
|
||||||
|
int KCHATTYPEDISC = 3;
|
||||||
|
int KCHATTYPEFAV = 41;
|
||||||
|
int KCHATTYPEGAMEMESSAGE = 105;
|
||||||
|
int KCHATTYPEGAMEMESSAGEFOLDER = 116;
|
||||||
|
int KCHATTYPEGROUP = 2;
|
||||||
|
int KCHATTYPEGROUPBLESS = 133;
|
||||||
|
int KCHATTYPEGROUPGUILD = 9;
|
||||||
|
int KCHATTYPEGROUPHELPER = 7;
|
||||||
|
int KCHATTYPEGROUPNOTIFY = 6;
|
||||||
|
int KCHATTYPEGUILD = 4;
|
||||||
|
int KCHATTYPEGUILDMETA = 16;
|
||||||
|
int KCHATTYPEMATCHFRIEND = 104;
|
||||||
|
int KCHATTYPEMATCHFRIENDFOLDER = 109;
|
||||||
|
int KCHATTYPENEARBY = 106;
|
||||||
|
int KCHATTYPENEARBYASSISTANT = 107;
|
||||||
|
int KCHATTYPENEARBYFOLDER = 110;
|
||||||
|
int KCHATTYPENEARBYHELLOFOLDER = 112;
|
||||||
|
int KCHATTYPENEARBYINTERACT = 108;
|
||||||
|
int KCHATTYPEQQNOTIFY = 132;
|
||||||
|
int KCHATTYPERELATEACCOUNT = 131;
|
||||||
|
int KCHATTYPESERVICEASSISTANT = 118;
|
||||||
|
int KCHATTYPESERVICEASSISTANTSUB = 201;
|
||||||
|
int KCHATTYPESQUAREPUBLIC = 115;
|
||||||
|
int KCHATTYPESUBSCRIBEFOLDER = 30;
|
||||||
|
int KCHATTYPETEMPADDRESSBOOK = 111;
|
||||||
|
int KCHATTYPETEMPBUSSINESSCRM = 102;
|
||||||
|
int KCHATTYPETEMPC2CFROMGROUP = 100;
|
||||||
|
int KCHATTYPETEMPC2CFROMUNKNOWN = 99;
|
||||||
|
int KCHATTYPETEMPFRIENDVERIFY = 101;
|
||||||
|
int KCHATTYPETEMPNEARBYPRO = 119;
|
||||||
|
int KCHATTYPETEMPPUBLICACCOUNT = 103;
|
||||||
|
int KCHATTYPETEMPWPA = 117;
|
||||||
|
int KCHATTYPEUNKNOWN = 0;
|
||||||
|
int KCHATTYPEWEIYUN = 40;
|
||||||
|
int KCOMMONREDENVELOPEMSGTYPEINMSGBOX = 1007;
|
||||||
|
int KDOWNSOURCETYPEAIOINNER = 1;
|
||||||
|
int KDOWNSOURCETYPEBIGSCREEN = 2;
|
||||||
|
int KDOWNSOURCETYPEHISTORY = 3;
|
||||||
|
int KDOWNSOURCETYPEUNKNOWN = 0;
|
||||||
|
int KELEMTYPEACTIVITY = 25;
|
||||||
|
int KELEMTYPEACTIVITYSTATE = 41;
|
||||||
|
int KELEMTYPEACTIVITYSUBTYPECREATEMOBATEAM = 12;
|
||||||
|
int KELEMTYPEACTIVITYSUBTYPEDISBANDMOBATEAM = 11;
|
||||||
|
int KELEMTYPEACTIVITYSUBTYPEFEEDSQUARE = 10001;
|
||||||
|
int KELEMTYPEACTIVITYSUBTYPEFINISHGAME = 16;
|
||||||
|
int KELEMTYPEACTIVITYSUBTYPEFINISHMATCHTEAM = 14;
|
||||||
|
int KELEMTYPEACTIVITYSUBTYPEHOTCHAT = 10000;
|
||||||
|
int KELEMTYPEACTIVITYSUBTYPEMINIGAME = 18;
|
||||||
|
int KELEMTYPEACTIVITYSUBTYPEMUSICPLAY = 17;
|
||||||
|
int KELEMTYPEACTIVITYSUBTYPENEWSMOBA = 9;
|
||||||
|
int KELEMTYPEACTIVITYSUBTYPENOLIVE = 2;
|
||||||
|
int KELEMTYPEACTIVITYSUBTYPENOSCREENSHARE = 7;
|
||||||
|
int KELEMTYPEACTIVITYSUBTYPENOVOICE = 3;
|
||||||
|
int KELEMTYPEACTIVITYSUBTYPEONLIVE = 1;
|
||||||
|
int KELEMTYPEACTIVITYSUBTYPEONSCREENSHARE = 6;
|
||||||
|
int KELEMTYPEACTIVITYSUBTYPEONVOICE = 4;
|
||||||
|
int KELEMTYPEACTIVITYSUBTYPESTARTMATCHTEAM = 13;
|
||||||
|
int KELEMTYPEACTIVITYSUBTYPETARTGAME = 15;
|
||||||
|
int KELEMTYPEACTIVITYSUBTYPEUNKNOWN = 0;
|
||||||
|
int KELEMTYPEADELIEACTIONBAR = 44;
|
||||||
|
int KELEMTYPEADELIERECOMMENDEDMSG = 43;
|
||||||
|
int KELEMTYPEARKSTRUCT = 10;
|
||||||
|
int KELEMTYPEAVRECORD = 21;
|
||||||
|
int KELEMTYPECALENDAR = 19;
|
||||||
|
int KELEMTYPEFACE = 6;
|
||||||
|
int KELEMTYPEFACEBUBBLE = 27;
|
||||||
|
int KELEMTYPEFEED = 22;
|
||||||
|
int KELEMTYPEFILE = 3;
|
||||||
|
int KELEMTYPEGIPHY = 15;
|
||||||
|
int KELEMTYPEGRAYTIP = 8;
|
||||||
|
int KELEMTYPEINLINEKEYBOARD = 17;
|
||||||
|
int KELEMTYPEINTEXTGIFT = 18;
|
||||||
|
int KELEMTYPELIVEGIFT = 12;
|
||||||
|
int KELEMTYPEMARKDOWN = 14;
|
||||||
|
int KELEMTYPEMARKETFACE = 11;
|
||||||
|
int KELEMTYPEMULTIFORWARD = 16;
|
||||||
|
int KELEMTYPEONLINEFILE = 23;
|
||||||
|
int KELEMTYPEPIC = 2;
|
||||||
|
int KELEMTYPEPROLOGUE = 46;
|
||||||
|
int KELEMTYPEPTT = 4;
|
||||||
|
int KELEMTYPEREPLY = 7;
|
||||||
|
int KELEMTYPESHARELOCATION = 28;
|
||||||
|
int KELEMTYPESTRUCTLONGMSG = 13;
|
||||||
|
int KELEMTYPETASKTOPMSG = 29;
|
||||||
|
int KELEMTYPETEXT = 1;
|
||||||
|
int KELEMTYPETOFU = 26;
|
||||||
|
int KELEMTYPEUNKNOWN = 0;
|
||||||
|
int KELEMTYPEVIDEO = 5;
|
||||||
|
int KELEMTYPEWALLET = 9;
|
||||||
|
int KELEMTYPEYOLOGAMERESULT = 20;
|
||||||
|
int KENTERAIO = 1;
|
||||||
|
int KEXITAIO = 2;
|
||||||
|
int KFEEDBACKBUTTONTYPEDISLIKE = 2;
|
||||||
|
int KFEEDBACKBUTTONTYPELIKE = 1;
|
||||||
|
int KFEEDBACKBUTTONTYPEPROMPTCLICK = 5;
|
||||||
|
int KFEEDBACKBUTTONTYPEREGENERATE = 4;
|
||||||
|
int KFEEDBACKBUTTONTYPEUNKNOWN = 0;
|
||||||
|
int KFEEDBACKOPTLIKE = 1;
|
||||||
|
int KFEEDBACKOPTUNKNOWN = 0;
|
||||||
|
int KFEEDBACKOPTUNLIKE = 2;
|
||||||
|
int KFRIENDNEWADDEDMSGTYPEINMSGBOX = 1008;
|
||||||
|
int KGAMEBOXNEWMSGTYPEINMSGBOX = 3000;
|
||||||
|
int KGIFTATMEMSGTYPEINMSGBOX = 1005;
|
||||||
|
int KGROUPFILEATALLMSGTYPEINMSGBOX = 2001;
|
||||||
|
int KGROUPHOMEWORK = 20000;
|
||||||
|
int KGROUPHOMEWORKTASK = 20001;
|
||||||
|
int KGROUPKEYWORDMSGTYPEINMSGBOX = 2006;
|
||||||
|
int KGROUPMANNOUNCEATALLMSGTYPEINMSGBOX = 2004;
|
||||||
|
int KGROUPTASKATALLMSGTYPEINMSGBOX = 2003;
|
||||||
|
int KGROUPUNREADTYPEINMSGBOX = 2007;
|
||||||
|
int KGUILDCHANNELLIST = 10;
|
||||||
|
int KHIGHLIGHTWORDINTEMPCHATTYPEINMSGBOX = 1009;
|
||||||
|
int KHOMEWORKREMINDER = 10000;
|
||||||
|
int KLIKEORDISLIKESTATEDISLIKE = 2;
|
||||||
|
int KLIKEORDISLIKESTATELIKE = 1;
|
||||||
|
int KLIKEORDISLIKESTATENONESELECTED = 0;
|
||||||
|
int KMARKETFACE = 17;
|
||||||
|
int KMEMORYSTATEMSGTYPEADELIEWELCOME = 1;
|
||||||
|
int KMEMORYSTATEMSGTYPEUNKNOWN = 0;
|
||||||
|
int KMINIPROGRAMNOTICE = 114;
|
||||||
|
int KMSGSUBTYPEARKGROUPANNOUNCE = 3;
|
||||||
|
int KMSGSUBTYPEARKGROUPANNOUNCECONFIRMREQUIRED = 4;
|
||||||
|
int KMSGSUBTYPEARKGROUPGIFTATME = 5;
|
||||||
|
int KMSGSUBTYPEARKGROUPTASKATALL = 6;
|
||||||
|
int KMSGSUBTYPEARKMULTIMSG = 7;
|
||||||
|
int KMSGSUBTYPEARKNORMAL = 0;
|
||||||
|
int KMSGSUBTYPEARKTENCENTDOCFROMMINIAPP = 1;
|
||||||
|
int KMSGSUBTYPEARKTENCENTDOCFROMPLUSPANEL = 2;
|
||||||
|
int KMSGSUBTYPEEMOTICON = 15;
|
||||||
|
int KMSGSUBTYPEFILEAPP = 11;
|
||||||
|
int KMSGSUBTYPEFILEAUDIO = 3;
|
||||||
|
int KMSGSUBTYPEFILEDOC = 4;
|
||||||
|
int KMSGSUBTYPEFILEEXCEL = 6;
|
||||||
|
int KMSGSUBTYPEFILEFOLDER = 13;
|
||||||
|
int KMSGSUBTYPEFILEHTML = 10;
|
||||||
|
int KMSGSUBTYPEFILEIPA = 14;
|
||||||
|
int KMSGSUBTYPEFILENORMAL = 0;
|
||||||
|
int KMSGSUBTYPEFILEPDF = 7;
|
||||||
|
int KMSGSUBTYPEFILEPIC = 1;
|
||||||
|
int KMSGSUBTYPEFILEPPT = 5;
|
||||||
|
int KMSGSUBTYPEFILEPSD = 12;
|
||||||
|
int KMSGSUBTYPEFILETXT = 8;
|
||||||
|
int KMSGSUBTYPEFILEVIDEO = 2;
|
||||||
|
int KMSGSUBTYPEFILEZIP = 9;
|
||||||
|
int KMSGSUBTYPELINK = 5;
|
||||||
|
int KMSGSUBTYPEMARKETFACE = 1;
|
||||||
|
int KMSGSUBTYPEMIXEMOTICON = 7;
|
||||||
|
int KMSGSUBTYPEMIXFACE = 3;
|
||||||
|
int KMSGSUBTYPEMIXMARKETFACE = 2;
|
||||||
|
int KMSGSUBTYPEMIXPIC = 1;
|
||||||
|
int KMSGSUBTYPEMIXREPLY = 4;
|
||||||
|
int KMSGSUBTYPEMIXTEXT = 0;
|
||||||
|
int KMSGSUBTYPETENCENTDOC = 6;
|
||||||
|
int KMSGTYPEARKSTRUCT = 11;
|
||||||
|
int KMSGTYPEFACEBUBBLE = 24;
|
||||||
|
int KMSGTYPEFILE = 3;
|
||||||
|
int KMSGTYPEGIFT = 14;
|
||||||
|
int KMSGTYPEGIPHY = 13;
|
||||||
|
int KMSGTYPEGRAYTIPS = 5;
|
||||||
|
int KMSGTYPEMIX = 2;
|
||||||
|
int KMSGTYPEMULTIMSGFORWARD = 8;
|
||||||
|
int KMSGTYPENULL = 1;
|
||||||
|
int KMSGTYPEONLINEFILE = 21;
|
||||||
|
int KMSGTYPEONLINEFOLDER = 27;
|
||||||
|
int KMSGTYPEPROLOGUE = 29;
|
||||||
|
int KMSGTYPEPTT = 6;
|
||||||
|
int KMSGTYPEREPLY = 9;
|
||||||
|
int KMSGTYPESHARELOCATION = 25;
|
||||||
|
int KMSGTYPESTRUCT = 4;
|
||||||
|
int KMSGTYPESTRUCTLONGMSG = 12;
|
||||||
|
int KMSGTYPETEXTGIFT = 15;
|
||||||
|
int KMSGTYPEUNKNOWN = 0;
|
||||||
|
int KMSGTYPEVIDEO = 7;
|
||||||
|
int KMSGTYPEWALLET = 10;
|
||||||
|
int KNEEDCONFIRMGROUPMANNOUNCEATALLMSGTYPEINMSGBOX = 2005;
|
||||||
|
int KNOTPASSTHROUGHEVENTTYPEUPPERBOUNDARY = 9999;
|
||||||
|
int KPTTFORMATTYPEAMR = 0;
|
||||||
|
int KPTTFORMATTYPESILK = 1;
|
||||||
|
int KPTTTRANSLATESTATUSFAIL = 3;
|
||||||
|
int KPTTTRANSLATESTATUSSUC = 2;
|
||||||
|
int KPTTTRANSLATESTATUSTRANSLATING = 1;
|
||||||
|
int KPTTTRANSLATESTATUSUNKNOWN = 0;
|
||||||
|
int KPTTVIPLEVELTYPENONE = 0;
|
||||||
|
int KPTTVIPLEVELTYPEQQVIP = 0;
|
||||||
|
int KPTTVIPLEVELTYPESVIP = 0;
|
||||||
|
int KPTTVOICECHANGETYPEBEASTMACHINE = 7;
|
||||||
|
int KPTTVOICECHANGETYPEBOY = 2;
|
||||||
|
int KPTTVOICECHANGETYPECATCHCOLD = 13;
|
||||||
|
int KPTTVOICECHANGETYPEECHO = 5;
|
||||||
|
int KPTTVOICECHANGETYPEFATGUY = 16;
|
||||||
|
int KPTTVOICECHANGETYPEFLASHING = 9;
|
||||||
|
int KPTTVOICECHANGETYPEGIRL = 1;
|
||||||
|
int KPTTVOICECHANGETYPEHORRIBLE = 3;
|
||||||
|
int KPTTVOICECHANGETYPEKINDERGARTEN = 6;
|
||||||
|
int KPTTVOICECHANGETYPEMEDAROT = 15;
|
||||||
|
int KPTTVOICECHANGETYPENONE = 0;
|
||||||
|
int KPTTVOICECHANGETYPEOPTIMUSPRIME = 8;
|
||||||
|
int KPTTVOICECHANGETYPEOUTOFDATE = 14;
|
||||||
|
int KPTTVOICECHANGETYPEPAPI = 11;
|
||||||
|
int KPTTVOICECHANGETYPEQUICK = 4;
|
||||||
|
int KPTTVOICECHANGETYPESTUTTER = 10;
|
||||||
|
int KPTTVOICECHANGETYPETRAPPEDBEAST = 12;
|
||||||
|
int KPTTVOICETYPEINTERCOM = 1;
|
||||||
|
int KPTTVOICETYPESOUNDRECORD = 2;
|
||||||
|
int KPTTVOICETYPEUNKNOW = 0;
|
||||||
|
int KPTTVOICETYPEVOICECHANGE = 3;
|
||||||
|
int KPUBLICACCOUNTTIANSHUHIGHLIGHTWORDTYPEINMSGBOX = 1010;
|
||||||
|
int KREPLYABSELEMTYPEFACE = 2;
|
||||||
|
int KREPLYABSELEMTYPEPIC = 3;
|
||||||
|
int KREPLYABSELEMTYPETEXT = 1;
|
||||||
|
int KREPLYABSELEMTYPEUNKNOWN = 0;
|
||||||
|
int KREPLYATMEMSGTYPEINMSGBOX = 1002;
|
||||||
|
int KRMDOWNTYPEORIG = 1;
|
||||||
|
int KRMDOWNTYPETHUMB = 2;
|
||||||
|
int KRMDOWNTYPEUNKNOWN = 0;
|
||||||
|
int KRMFILETHUMBSIZE128 = 128;
|
||||||
|
int KRMFILETHUMBSIZE320 = 320;
|
||||||
|
int KRMFILETHUMBSIZE384 = 384;
|
||||||
|
int KRMFILETHUMBSIZE750 = 750;
|
||||||
|
int KRMPICAIOTHUMBSIZE = 0;
|
||||||
|
int KRMPICTHUMBSIZE198 = 198;
|
||||||
|
int KRMPICTHUMBSIZE720 = 720;
|
||||||
|
int KRMPICTYPEBMP = 3;
|
||||||
|
int KRMPICTYPECHECKOTHER = 900;
|
||||||
|
int KRMPICTYPEGIF = 2;
|
||||||
|
int KRMPICTYPEJPG = 0;
|
||||||
|
int KRMPICTYPENEWPICAPNG = 2001;
|
||||||
|
int KRMPICTYPENEWPICBMP = 1005;
|
||||||
|
int KRMPICTYPENEWPICGIF = 2000;
|
||||||
|
int KRMPICTYPENEWPICJPEG = 1000;
|
||||||
|
int KRMPICTYPENEWPICPNG = 1001;
|
||||||
|
int KRMPICTYPENEWPICPROGERSSIVJPEG = 1003;
|
||||||
|
int KRMPICTYPENEWPICSHARPP = 1004;
|
||||||
|
int KRMPICTYPENEWPICWEBP = 1002;
|
||||||
|
int KRMPICTYPEPNG = 1;
|
||||||
|
int KRMPICTYPEUNKOWN = 0;
|
||||||
|
int KRMTHUMBSIZEZERO = 0;
|
||||||
|
int KRMTRNASFERSTATUSDOWNLOADING = 3;
|
||||||
|
int KRMTRNASFERSTATUSFAIL = 5;
|
||||||
|
int KRMTRNASFERSTATUSINIT = 1;
|
||||||
|
int KRMTRNASFERSTATUSSUC = 4;
|
||||||
|
int KRMTRNASFERSTATUSUNKOW = 0;
|
||||||
|
int KRMTRNASFERSTATUSUPLOADING = 2;
|
||||||
|
int KRMTRNASFERSTATUSUSERCANCEL = 6;
|
||||||
|
int KSEEKINGPARTNERFLAGSEEKING = 1;
|
||||||
|
int KSEEKINGPARTNERFLAGUNKNOWN = 0;
|
||||||
|
int KSENDSTATUSFAILED = 0;
|
||||||
|
int KSENDSTATUSSENDING = 1;
|
||||||
|
int KSENDSTATUSSUCCESS = 2;
|
||||||
|
int KSENDSTATUSSUCCESSNOSEQ = 3;
|
||||||
|
int KSENDTYPEDROPPED = 6;
|
||||||
|
int KSENDTYPELOCAL = 3;
|
||||||
|
int KSENDTYPEOTHERDEVICE = 2;
|
||||||
|
int KSENDTYPERECV = 0;
|
||||||
|
int KSENDTYPESELF = 1;
|
||||||
|
int KSENDTYPESELFFORWARD = 4;
|
||||||
|
int KSENDTYPESELFMULTIFORWARD = 5;
|
||||||
|
int KSESSIONTYPEADDRESSBOOK = 5;
|
||||||
|
int KSESSIONTYPEC2C = 1;
|
||||||
|
int KSESSIONTYPEDISC = 3;
|
||||||
|
int KSESSIONTYPEFAV = 41;
|
||||||
|
int KSESSIONTYPEGROUP = 2;
|
||||||
|
int KSESSIONTYPEGROUPBLESS = 52;
|
||||||
|
int KSESSIONTYPEGUILD = 4;
|
||||||
|
int KSESSIONTYPEGUILDMETA = 16;
|
||||||
|
int KSESSIONTYPENEARBYPRO = 54;
|
||||||
|
int KSESSIONTYPEQQNOTIFY = 51;
|
||||||
|
int KSESSIONTYPERELATEACCOUNT = 50;
|
||||||
|
int KSESSIONTYPESERVICEASSISTANT = 19;
|
||||||
|
int KSESSIONTYPESUBSCRIBEFOLDER = 30;
|
||||||
|
int KSESSIONTYPETYPEBUDDYNOTIFY = 7;
|
||||||
|
int KSESSIONTYPETYPEGROUPHELPER = 9;
|
||||||
|
int KSESSIONTYPETYPEGROUPNOTIFY = 8;
|
||||||
|
int KSESSIONTYPEUNKNOWN = 0;
|
||||||
|
int KSESSIONTYPEWEIYUN = 40;
|
||||||
|
int KSPECIALCAREMSGTYPEINMSGBOX = 1006;
|
||||||
|
int KSPECIFIEDREDENVELOPEATMEMSGTYPEINMSGBOX = 1004;
|
||||||
|
int KSPECIFIEDREDENVELOPEATONEMSGTYPEINMSGBOX = 1003;
|
||||||
|
int KTENCENTDOCTYPEADDON = 110;
|
||||||
|
int KTENCENTDOCTYPEDOC = 0;
|
||||||
|
int KTENCENTDOCTYPEDRAWING = 89;
|
||||||
|
int KTENCENTDOCTYPEDRIVE = 101;
|
||||||
|
int KTENCENTDOCTYPEFILE = 100;
|
||||||
|
int KTENCENTDOCTYPEFLOWCHART = 91;
|
||||||
|
int KTENCENTDOCTYPEFOLDER = 3;
|
||||||
|
int KTENCENTDOCTYPEFORM = 2;
|
||||||
|
int KTENCENTDOCTYPEMIND = 90;
|
||||||
|
int KTENCENTDOCTYPENOTES = 5;
|
||||||
|
int KTENCENTDOCTYPEPDF = 6;
|
||||||
|
int KTENCENTDOCTYPEPROGRAM = 7;
|
||||||
|
int KTENCENTDOCTYPESHEET = 1;
|
||||||
|
int KTENCENTDOCTYPESLIDE = 4;
|
||||||
|
int KTENCENTDOCTYPESMARTCANVAS = 8;
|
||||||
|
int KTENCENTDOCTYPESMARTSHEET = 9;
|
||||||
|
int KTENCENTDOCTYPESPEECH = 102;
|
||||||
|
int KTENCENTDOCTYPEUNKNOWN = 10;
|
||||||
|
int KTOFURECORDMSG = 23;
|
||||||
|
int KTOPMSGTYPETASK = 1;
|
||||||
|
int KTOPMSGTYPEUNKNOWN = 0;
|
||||||
|
int KTRIGGERTYPEAUTO = 1;
|
||||||
|
int KTRIGGERTYPEMANUAL = 0;
|
||||||
|
int KUNKNOWN = 0;
|
||||||
|
int KUNKNOWNTYPEINMSGBOX = 0;
|
||||||
|
int KUNREADCNTUPTYPEALLDIRECTSESSION = 4;
|
||||||
|
int KUNREADCNTUPTYPEALLFEEDSINGUILD = 6;
|
||||||
|
int KUNREADCNTUPTYPEALLGUILD = 3;
|
||||||
|
int KUNREADCNTUPTYPECATEGORY = 5;
|
||||||
|
int KUNREADCNTUPTYPECHANNEL = 1;
|
||||||
|
int KUNREADCNTUPTYPECONTACT = 0;
|
||||||
|
int KUNREADCNTUPTYPEGUILD = 2;
|
||||||
|
int KUNREADCNTUPTYPEGUILDGROUP = 7;
|
||||||
|
int KUNREADSHOWTTYPEGRAYPOINT = 2;
|
||||||
|
int KUNREADSHOWTYPEREDPOINT = 1;
|
||||||
|
int KUNREADSHOWTYPESMALLGRAYPOINT = 4;
|
||||||
|
int KUNREADSHOWTYPESMALLREDPOINT = 3;
|
||||||
|
int KUNREADSHOWTYPEUNKNOWN = 0;
|
||||||
|
int KVASGIFTCOINTYPECOIN = 0;
|
||||||
|
int KVASGIFTCOINTYPEMARKETCOIN = 1;
|
||||||
|
int KYOLOGAMERESULTMSG = 18;
|
||||||
|
int PIC_800_RECOMMENDED = 7;
|
||||||
|
int PIC_AIGC_EMOJI = 14;
|
||||||
|
int PIC_ALBUM_GIF = 11;
|
||||||
|
int PIC_COMMERCIAL_ADVERTISING = 9;
|
||||||
|
int PIC_FIND = 10;
|
||||||
|
int PIC_HOT = 2;
|
||||||
|
int PIC_HOT_EMOJI = 13;
|
||||||
|
int PIC_NORMAL = 0;
|
||||||
|
int PIC_PK = 3;
|
||||||
|
int PIC_QQZONE = 5;
|
||||||
|
int PIC_SELFIE_GIF = 8;
|
||||||
|
int PIC_SEND_FROM_TAB_SEARCH_BOX = 12;
|
||||||
|
int PIC_USER = 1;
|
||||||
|
int PIC_WISDOM_FIGURE = 4;
|
||||||
|
int REPLYORIGINALMSGSTATEHASRECALL = 1;
|
||||||
|
int REPLYORIGINALMSGSTATEUNKNOWN = 0;
|
||||||
|
int SHARELOCATIONELEMSUBTYPENORMAL = 1;
|
||||||
|
int SHARELOCATIONELEMSUBTYPEUNKNOWN = 0;
|
||||||
|
int TEXTELEMENTSUBTYPELINK = 1;
|
||||||
|
int TEXTELEMENTSUBTYPETENCENTDOC = 2;
|
||||||
|
int TEXTELEMENTSUBTYPEUNKNOWN = 0;
|
||||||
|
```
|
24
docs/develop/参与开发.md
Normal file
24
docs/develop/参与开发.md
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
# 前排提示
|
||||||
|
由于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调用流程
|
130
package.json
130
package.json
@@ -1,63 +1,67 @@
|
|||||||
{
|
{
|
||||||
"name": "napcat",
|
"name": "napcat",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"version": "1.0.0",
|
"version": "1.5.5",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"watch:dev": "vite --mode development",
|
"watch:dev": "vite --mode development",
|
||||||
"watch:prod": "vite --mode production",
|
"watch:prod": "vite --mode production",
|
||||||
"build:dev": "vite build --mode development",
|
"build:dev": "vite build --mode development",
|
||||||
"build:prod": "vite build --mode production",
|
"build:prod": "vite build --mode production",
|
||||||
"build": "npm run build:dev",
|
"build": "npm run build:dev",
|
||||||
"build:core": "cd ./src/core && vite build --mode production",
|
"build:core": "cd ./src/core && npm run build && cd ../.. && node ./script/copy-core.cjs",
|
||||||
"watch": "npm run watch:dev",
|
"build:webui": "cd ./src/webui && vite build",
|
||||||
"debug-win": "powershell dist/napcat.ps1",
|
"watch": "npm run watch:dev",
|
||||||
"lint": "eslint --fix src/**/*.{js,ts}",
|
"debug-win": "powershell dist/napcat.ps1",
|
||||||
"release": "npm run build:prod",
|
"lint": "eslint --fix src/**/*.{js,ts}",
|
||||||
"depend": "cd dist && npm install --omit=dev"
|
"release": "npm run build:prod",
|
||||||
},
|
"depend": "cd dist && npm install --omit=dev"
|
||||||
"devDependencies": {
|
},
|
||||||
"@log4js-node/log4js-api": "^1.0.2",
|
"devDependencies": {
|
||||||
"@rollup/plugin-commonjs": "^25.0.7",
|
"@log4js-node/log4js-api": "^1.0.2",
|
||||||
"@rollup/plugin-node-resolve": "^15.2.3",
|
"@protobuf-ts/plugin": "^2.9.4",
|
||||||
"@rollup/plugin-typescript": "^11.1.6",
|
"@rollup/plugin-node-resolve": "^15.2.3",
|
||||||
"@types/express": "^4.17.21",
|
"@rollup/plugin-typescript": "^11.1.6",
|
||||||
"@types/figlet": "^1.5.8",
|
"@types/cors": "^2.8.17",
|
||||||
"@types/fluent-ffmpeg": "^2.1.24",
|
"@types/express": "^4.17.21",
|
||||||
"@types/node": "^20.11.30",
|
"@types/figlet": "^1.5.8",
|
||||||
"@types/qrcode-terminal": "^0.12.2",
|
"@types/fluent-ffmpeg": "^2.1.24",
|
||||||
"@types/uuid": "^9.0.8",
|
"@types/node": "^20.11.30",
|
||||||
"@types/ws": "^8.5.10",
|
"@types/qrcode-terminal": "^0.12.2",
|
||||||
"@typescript-eslint/eslint-plugin": "^7.4.0",
|
"@types/uuid": "^9.0.8",
|
||||||
"@typescript-eslint/parser": "^7.4.0",
|
"@types/ws": "^8.5.10",
|
||||||
"eslint": "^8.57.0",
|
"@typescript-eslint/eslint-plugin": "^7.4.0",
|
||||||
"eslint-import-resolver-typescript": "^3.6.1",
|
"@typescript-eslint/parser": "^7.4.0",
|
||||||
"eslint-plugin-import": "^2.29.1",
|
"eslint": "^8.57.0",
|
||||||
"i": "^0.3.7",
|
"eslint-import-resolver-typescript": "^3.6.1",
|
||||||
"javascript-obfuscator": "^4.1.0",
|
"eslint-plugin-import": "^2.29.1",
|
||||||
"protobufjs-cli": "^1.1.2",
|
"i": "^0.3.7",
|
||||||
"rollup": "^4.13.2",
|
"javascript-obfuscator": "^4.1.0",
|
||||||
"rollup-plugin-dts": "^6.1.0",
|
"rollup": "^4.13.2",
|
||||||
"rollup-plugin-obfuscator": "^1.1.0",
|
"rollup-plugin-dts": "^6.1.0",
|
||||||
"typescript": "^5.3.3",
|
"rollup-plugin-obfuscator": "^1.1.0",
|
||||||
"vite": "^5.2.6",
|
"typescript": "^5.3.3",
|
||||||
"vite-plugin-cp": "^4.0.8",
|
"vite": "^5.2.6",
|
||||||
"vite-plugin-dts": "^3.8.2",
|
"vite-plugin-cp": "^4.0.8",
|
||||||
"vite-tsconfig-paths": "^4.3.2"
|
"vite-plugin-dts": "^3.8.2",
|
||||||
},
|
"vite-tsconfig-paths": "^4.3.2"
|
||||||
"dependencies": {
|
},
|
||||||
"commander": "^12.0.0",
|
"dependencies": {
|
||||||
"express": "^5.0.0-beta.2",
|
"ajv": "^8.13.0",
|
||||||
"file-type": "^19.0.0",
|
"chalk": "^5.3.0",
|
||||||
"fluent-ffmpeg": "^2.1.2",
|
"commander": "^12.0.0",
|
||||||
"image-size": "^1.1.1",
|
"cors": "^2.8.5",
|
||||||
"log4js": "^6.9.1",
|
"express": "^5.0.0-beta.2",
|
||||||
"protobufjs": "^7.2.6",
|
"fast-xml-parser": "^4.3.6",
|
||||||
"qrcode-terminal": "^0.12.0",
|
"file-type": "^19.0.0",
|
||||||
"silk-wasm": "^3.3.4",
|
"fluent-ffmpeg": "^2.1.2",
|
||||||
"sqlite3": "^5.1.7",
|
"image-size": "^1.1.1",
|
||||||
"uuid": "^9.0.1",
|
"json-schema-to-ts": "^3.1.0",
|
||||||
"ws": "^8.16.0",
|
"log4js": "^6.9.1",
|
||||||
"yaml": "^2.4.1"
|
"qrcode-terminal": "^0.12.0",
|
||||||
}
|
"silk-wasm": "^3.3.4",
|
||||||
}
|
"sqlite3": "^5.1.7",
|
||||||
|
"uuid": "^10.0.0",
|
||||||
|
"ws": "^8.16.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
42
script/checkVersion.cjs
Normal file
42
script/checkVersion.cjs
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
const fs = require("fs");
|
||||||
|
const process = require("process");
|
||||||
|
|
||||||
|
console.log("[NapCat] [CheckVersion] 开始检测当前仓库版本...");
|
||||||
|
try {
|
||||||
|
const packageJson = require("../package.json");
|
||||||
|
const currentVersion = packageJson.version;
|
||||||
|
const targetVersion = process.env.VERSION;
|
||||||
|
|
||||||
|
console.log("[NapCat] [CheckVersion] currentVersion:", currentVersion, "targetVersion:", targetVersion);
|
||||||
|
|
||||||
|
// 验证 targetVersion 格式
|
||||||
|
if (!targetVersion || typeof targetVersion !== 'string') {
|
||||||
|
console.error("[NapCat] [CheckVersion] 目标版本格式不正确或未设置!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 写入脚本文件的统一函数
|
||||||
|
const writeScriptToFile = (content) => {
|
||||||
|
fs.writeFileSync("./checkVersion.sh", content, { flag: 'w' });
|
||||||
|
console.log("[NapCat] [CheckVersion] checkVersion.sh 文件已更新。");
|
||||||
|
};
|
||||||
|
|
||||||
|
if (currentVersion === targetVersion) {
|
||||||
|
// 不需要更新版本,写入一个简单的脚本
|
||||||
|
const simpleScript = "#!/bin/bash\necho \"CheckVersion Is Done\"";
|
||||||
|
writeScriptToFile(simpleScript);
|
||||||
|
} else {
|
||||||
|
// 更新版本,构建安全的sed命令
|
||||||
|
const safeScriptContent = `
|
||||||
|
#!/bin/bash
|
||||||
|
git config --global user.email "bot@test.wumiao.wang"
|
||||||
|
git config --global user.name "Version"
|
||||||
|
sed -i "s/\\\"version\\\": \\\"${currentVersion}\\\"/\\\"version\\\": \\\"${targetVersion}\\\"/g" package.json
|
||||||
|
git add .
|
||||||
|
git commit -m "chore:version change"
|
||||||
|
git push -u origin main`;
|
||||||
|
writeScriptToFile(safeScriptContent);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("[NapCat] [CheckVersion] 检测过程中发生错误:", error);
|
||||||
|
}
|
32
script/copy-core.cjs
Normal file
32
script/copy-core.cjs
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
let fs = require('fs');
|
||||||
|
let path = require('path');
|
||||||
|
|
||||||
|
const coreDistDir = path.join(path.resolve(__dirname, '../'), 'src/core/dist/core/src');
|
||||||
|
const coreLibDir = path.join(path.resolve(__dirname, '../'), 'src/core.lib/src');
|
||||||
|
|
||||||
|
function copyDir(currentPath, outputDir) {
|
||||||
|
fs.readdir(currentPath, { withFileTypes: true }, (err, entries) => {
|
||||||
|
if (err?.errno === -4058) return;
|
||||||
|
|
||||||
|
entries.forEach(entry => {
|
||||||
|
const localBasePath = path.join(currentPath, entry.name);
|
||||||
|
const outputLocalBasePath = path.join(outputDir, entry.name);
|
||||||
|
|
||||||
|
if (entry.isDirectory()) {
|
||||||
|
// 如果是目录,递归调用
|
||||||
|
if (!fs.existsSync(outputLocalBasePath)) {
|
||||||
|
fs.mkdirSync(outputLocalBasePath, { recursive: true });
|
||||||
|
}
|
||||||
|
copyDir(localBasePath, outputLocalBasePath);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
// 如果是文件,直接复制
|
||||||
|
fs.copyFile(localBasePath, outputLocalBasePath, (err) => {
|
||||||
|
if (err) throw err;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
copyDir(coreDistDir, coreLibDir);
|
21
script/gen-version.ts
Normal file
21
script/gen-version.ts
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
import fs from 'fs'
|
||||||
|
import path from 'path'
|
||||||
|
import { version } from '../src/onebot11/version'
|
||||||
|
|
||||||
|
const manifestPath = path.join(__dirname, '../package.json')
|
||||||
|
|
||||||
|
function readManifest (): any {
|
||||||
|
if (fs.existsSync(manifestPath)) {
|
||||||
|
return JSON.parse(fs.readFileSync(manifestPath, 'utf-8'))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function writeManifest (manifest: any) {
|
||||||
|
fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2))
|
||||||
|
}
|
||||||
|
|
||||||
|
const manifest = readManifest()
|
||||||
|
if (version !== manifest.version) {
|
||||||
|
manifest.version = version
|
||||||
|
writeManifest(manifest)
|
||||||
|
}
|
3
script/napcat-custom.bat
Normal file
3
script/napcat-custom.bat
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
chcp 65001
|
||||||
|
set ELECTRON_RUN_AS_NODE=1
|
||||||
|
"H:\Program Files\QQNT最新版\QQ.exe" %~dp0/napcat.cjs %*
|
@@ -2,21 +2,17 @@
|
|||||||
setlocal enabledelayedexpansion
|
setlocal enabledelayedexpansion
|
||||||
chcp 65001
|
chcp 65001
|
||||||
:loop_read
|
:loop_read
|
||||||
for /f "tokens=3" %%a in ('reg query "HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\QQ" /v "UninstallString"') do (
|
for /f "tokens=2*" %%a in ('reg query "HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\QQ" /v "UninstallString"') do (
|
||||||
set "RetString=%%a"
|
set "RetString=%%b"
|
||||||
goto :napcat_boot
|
goto :napcat_boot
|
||||||
)
|
)
|
||||||
|
|
||||||
goto :loop_read
|
|
||||||
|
|
||||||
:napcat_boot
|
:napcat_boot
|
||||||
for %%a in ("!RetString!") do (
|
for %%a in ("!RetString!") do (
|
||||||
set "pathWithoutUninstall=%%~dpa"
|
set "pathWithoutUninstall=%%~dpa"
|
||||||
set "fileName=%%~na"
|
|
||||||
set "extension=%%~xa"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
set "QQPath=!pathWithoutUninstall!QQ.exe"
|
set "QQPath=!pathWithoutUninstall!QQ.exe"
|
||||||
set ELECTRON_RUN_AS_NODE=1
|
set ELECTRON_RUN_AS_NODE=1
|
||||||
echo !QQPath!
|
echo !QQPath!
|
||||||
!QQPath! ./napcat.cjs
|
"!QQPath!" ./napcat.mjs %*
|
||||||
|
@@ -5,11 +5,39 @@ function Get-QQpath {
|
|||||||
return [System.IO.Path]::GetDirectoryName($uninstallString) + "\QQ.exe"
|
return [System.IO.Path]::GetDirectoryName($uninstallString) + "\QQ.exe"
|
||||||
}
|
}
|
||||||
catch {
|
catch {
|
||||||
return "D:\QQ.exe"
|
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 " "
|
$params = $args -join " "
|
||||||
$QQpath = Get-QQpath
|
Try {
|
||||||
$Bootfile = Join-Path $PSScriptRoot "napcat.cjs"
|
$QQpath = Get-QQpath
|
||||||
|
}
|
||||||
|
Catch {
|
||||||
|
$QQpath = Select-QQPath
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(Test-Path $QQpath)) {
|
||||||
|
throw "provided QQ path is invalid: $QQpath"
|
||||||
|
}
|
||||||
|
|
||||||
|
$Bootfile = Join-Path $PSScriptRoot "napcat.mjs"
|
||||||
$env:ELECTRON_RUN_AS_NODE = 1
|
$env:ELECTRON_RUN_AS_NODE = 1
|
||||||
Start-Process powershell -ArgumentList "-noexit", "-noprofile", "-command &{& chcp 65001;& '$QQpath' $Bootfile $params}"
|
$commandInfo = Get-Command $QQpath -ErrorAction Stop
|
||||||
|
Start-Process powershell -ArgumentList "-noexit", "-noprofile", "-command &{& chcp 65001;& '$($commandInfo.Path)' $Bootfile $params}"
|
@@ -1,22 +1,17 @@
|
|||||||
@echo off
|
@echo off
|
||||||
setlocal enabledelayedexpansion
|
setlocal enabledelayedexpansion
|
||||||
|
|
||||||
:loop_read
|
:loop_read
|
||||||
for /f "tokens=3" %%a in ('reg query "HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\QQ" /v "UninstallString"') do (
|
for /f "tokens=2*" %%a in ('reg query "HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\QQ" /v "UninstallString"') do (
|
||||||
set "RetString=%%a"
|
set "RetString=%%b"
|
||||||
goto :napcat_boot
|
goto :napcat_boot
|
||||||
)
|
)
|
||||||
|
|
||||||
goto :loop_read
|
|
||||||
|
|
||||||
:napcat_boot
|
:napcat_boot
|
||||||
for %%a in ("!RetString!") do (
|
for %%a in ("!RetString!") do (
|
||||||
set "pathWithoutUninstall=%%~dpa"
|
set "pathWithoutUninstall=%%~dpa"
|
||||||
set "fileName=%%~na"
|
|
||||||
set "extension=%%~xa"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
set "QQPath=!pathWithoutUninstall!QQ.exe"
|
set "QQPath=!pathWithoutUninstall!QQ.exe"
|
||||||
set ELECTRON_RUN_AS_NODE=1
|
set ELECTRON_RUN_AS_NODE=1
|
||||||
echo !QQPath!
|
echo !QQPath!
|
||||||
!QQPath! ./napcat.cjs
|
"!QQPath!" ./napcat.mjs %*
|
||||||
|
@@ -5,11 +5,39 @@ function Get-QQpath {
|
|||||||
return [System.IO.Path]::GetDirectoryName($uninstallString) + "\QQ.exe"
|
return [System.IO.Path]::GetDirectoryName($uninstallString) + "\QQ.exe"
|
||||||
}
|
}
|
||||||
catch {
|
catch {
|
||||||
return "D:\QQ.exe"
|
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 " "
|
$params = $args -join " "
|
||||||
$QQpath = Get-QQpath
|
Try {
|
||||||
$Bootfile = Join-Path $PSScriptRoot "napcat.cjs"
|
$QQpath = Get-QQpath
|
||||||
|
}
|
||||||
|
Catch {
|
||||||
|
$QQpath = Select-QQPath
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(Test-Path $QQpath)) {
|
||||||
|
throw "provided QQ path is invalid: $QQpath"
|
||||||
|
}
|
||||||
|
|
||||||
|
$Bootfile = Join-Path $PSScriptRoot "napcat.mjs"
|
||||||
$env:ELECTRON_RUN_AS_NODE = 1
|
$env:ELECTRON_RUN_AS_NODE = 1
|
||||||
Start-Process powershell -ArgumentList "-noexit", "-noprofile", "-command &{& '$QQpath' $Bootfile $params}"
|
$commandInfo = Get-Command $QQpath -ErrorAction Stop
|
||||||
|
Start-Process powershell -ArgumentList "-noexit", "-noprofile", "-command &{& '$($commandInfo.Path)' $Bootfile $params}"
|
@@ -1,4 +1,21 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
SCRIPT_DIR=$(realpath $(dirname "${BASH_SOURCE[0]}"))
|
|
||||||
|
get_script_dir() {
|
||||||
|
local script_path="${1:-$0}"
|
||||||
|
local script_dir
|
||||||
|
script_path=$(readlink -f "$script_path")
|
||||||
|
script_dir=$(dirname "$script_path")
|
||||||
|
|
||||||
|
echo "$script_dir"
|
||||||
|
}
|
||||||
|
|
||||||
|
SCRIPT_DIR=$(get_script_dir)
|
||||||
|
|
||||||
export ELECTRON_RUN_AS_NODE=1
|
export ELECTRON_RUN_AS_NODE=1
|
||||||
/opt/QQ/qq ${SCRIPT_DIR}/napcat.cjs $@
|
|
||||||
|
if ! [ -x /opt/QQ/qq ]; then
|
||||||
|
echo "Error: /opt/QQ/qq is not executable or does not exist." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
/opt/QQ/qq "${SCRIPT_DIR}/napcat.mjs" "$@"
|
||||||
|
@@ -1,85 +0,0 @@
|
|||||||
import {
|
|
||||||
type Friend,
|
|
||||||
type FriendRequest,
|
|
||||||
type Group,
|
|
||||||
type GroupMember, GroupNotify,
|
|
||||||
type SelfInfo
|
|
||||||
} from '@/core/qqnt/entities';
|
|
||||||
import { isNumeric } from './utils/helper';
|
|
||||||
import { log } from '@/common/utils/log';
|
|
||||||
|
|
||||||
export const selfInfo: SelfInfo = {
|
|
||||||
uid: '',
|
|
||||||
uin: '',
|
|
||||||
nick: '',
|
|
||||||
online: true
|
|
||||||
};
|
|
||||||
|
|
||||||
// groupCode -> Group
|
|
||||||
export const groups: Map<string, Group> = new Map<string, Group>();
|
|
||||||
|
|
||||||
// 群号 -> 群成员map(uid=>GroupMember)
|
|
||||||
export const groupMembers: Map<string, Map<string, GroupMember>> = new Map<string, Map<string, GroupMember>>();
|
|
||||||
|
|
||||||
// uid -> Friend
|
|
||||||
export const friends: Map<string, Friend> = new Map<string, Friend>();
|
|
||||||
|
|
||||||
export const friendRequests: Record<string, FriendRequest> = {}; // flag->FriendRequest
|
|
||||||
export const groupNotifies: Record<string, GroupNotify> = {}; // flag->GroupNotify
|
|
||||||
|
|
||||||
export const napCatError = {
|
|
||||||
ffmpegError: '',
|
|
||||||
httpServerError: '',
|
|
||||||
wsServerError: '',
|
|
||||||
otherError: 'NapCat未能正常启动,请检查日志查看错误'
|
|
||||||
};
|
|
||||||
|
|
||||||
export async function getFriend(uinOrUid: string): Promise<Friend | undefined> {
|
|
||||||
uinOrUid = uinOrUid.toString();
|
|
||||||
if (isNumeric(uinOrUid)) {
|
|
||||||
const friendList = Array.from(friends.values());
|
|
||||||
return friendList.find(friend => friend.uin === uinOrUid);
|
|
||||||
} else {
|
|
||||||
return friends.get(uinOrUid);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function getGroup(qq: string): Promise<Group | undefined> {
|
|
||||||
const group = groups.get(qq.toString());
|
|
||||||
return group;
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function getGroupMember(groupQQ: string | number, memberUinOrUid: string | number) {
|
|
||||||
groupQQ = groupQQ.toString();
|
|
||||||
memberUinOrUid = memberUinOrUid.toString();
|
|
||||||
const members = groupMembers.get(groupQQ);
|
|
||||||
if (!members) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
// log('getGroupMember', members);
|
|
||||||
if (isNumeric(memberUinOrUid)) {
|
|
||||||
return Array.from(members.values()).find(member => member.uin === memberUinOrUid);
|
|
||||||
} else {
|
|
||||||
return members.get(memberUinOrUid);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function refreshGroupMembers(groupQQ: string) {
|
|
||||||
// const group = groups.find(group => group.groupCode === groupQQ)
|
|
||||||
// if (group) {
|
|
||||||
// group.members = await NTQQGroupApi.getGroupMembers(groupQQ)
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
export const uid2UinMap: Record<string, string> = {}; // 一串加密的字符串(uid) -> qq号
|
|
||||||
|
|
||||||
export function getUidByUin(uin: string) {
|
|
||||||
for (const uid in uid2UinMap) {
|
|
||||||
if (uid2UinMap[uid] === uin) {
|
|
||||||
return uid;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const tempGroupCodeMap: Record<string, string> = {}; // peerUid => 群号
|
|
||||||
|
|
@@ -1,17 +1,19 @@
|
|||||||
import express, { Express, Request, Response } from 'express';
|
import express, { Express, Request, Response } from 'express';
|
||||||
|
import cors from 'cors';
|
||||||
import http from 'http';
|
import http from 'http';
|
||||||
import { log } from '../utils/log';
|
import { log, logDebug, logError } from '../utils/log';
|
||||||
import { ob11Config } from '@/onebot11/config';
|
import { ob11Config } from '@/onebot11/config';
|
||||||
|
|
||||||
type RegisterHandler = (res: Response, payload: any) => Promise<any>
|
type RegisterHandler = (res: Response, payload: any) => Promise<any>
|
||||||
|
|
||||||
export abstract class HttpServerBase {
|
export abstract class HttpServerBase {
|
||||||
name: string = 'LLOneBot';
|
name: string = 'NapCatQQ';
|
||||||
private readonly expressAPP: Express;
|
private readonly expressAPP: Express;
|
||||||
private server: http.Server | null = null;
|
private server: http.Server | null = null;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.expressAPP = express();
|
this.expressAPP = express();
|
||||||
|
this.expressAPP.use(cors());
|
||||||
this.expressAPP.use(express.urlencoded({ extended: true, limit: '5000mb' }));
|
this.expressAPP.use(express.urlencoded({ extended: true, limit: '5000mb' }));
|
||||||
this.expressAPP.use((req, res, next) => {
|
this.expressAPP.use((req, res, next) => {
|
||||||
// 兼容处理没有带content-type的请求
|
// 兼容处理没有带content-type的请求
|
||||||
@@ -21,7 +23,7 @@ export abstract class HttpServerBase {
|
|||||||
// 调用原始的express.json()处理器
|
// 调用原始的express.json()处理器
|
||||||
originalJson(req, res, (err) => {
|
originalJson(req, res, (err) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
log('Error parsing JSON:', err);
|
logError('Error parsing JSON:', err);
|
||||||
return res.status(400).send('Invalid JSON');
|
return res.status(400).send('Invalid JSON');
|
||||||
}
|
}
|
||||||
next();
|
next();
|
||||||
@@ -35,14 +37,14 @@ export abstract class HttpServerBase {
|
|||||||
const authHeader = req.get('authorization');
|
const authHeader = req.get('authorization');
|
||||||
if (authHeader) {
|
if (authHeader) {
|
||||||
clientToken = authHeader.split('Bearer ').pop() || '';
|
clientToken = authHeader.split('Bearer ').pop() || '';
|
||||||
log('receive http header token', clientToken);
|
//logDebug('receive http header token', clientToken);
|
||||||
} else if (req.query.access_token) {
|
} else if (req.query.access_token) {
|
||||||
if (Array.isArray(req.query.access_token)) {
|
if (Array.isArray(req.query.access_token)) {
|
||||||
clientToken = req.query.access_token[0].toString();
|
clientToken = req.query.access_token[0].toString();
|
||||||
} else {
|
} else {
|
||||||
clientToken = req.query.access_token.toString();
|
clientToken = req.query.access_token.toString();
|
||||||
}
|
}
|
||||||
log('receive http url token', clientToken);
|
//logDebug('receive http url token', clientToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (serverToken && clientToken != serverToken) {
|
if (serverToken && clientToken != serverToken) {
|
||||||
@@ -51,29 +53,29 @@ export abstract class HttpServerBase {
|
|||||||
next();
|
next();
|
||||||
}
|
}
|
||||||
|
|
||||||
start(port: number) {
|
start(port: number, host: string) {
|
||||||
try {
|
try {
|
||||||
this.expressAPP.get('/', (req: Request, res: Response) => {
|
this.expressAPP.get('/', (req: Request, res: Response) => {
|
||||||
res.send(`${this.name}已启动`);
|
res.send(`${this.name}已启动`);
|
||||||
});
|
});
|
||||||
this.listen(port);
|
this.listen(port, host);
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
log('HTTP服务启动失败', e.toString());
|
logError('HTTP服务启动失败', e.toString());
|
||||||
// llonebotError.httpServerError = "HTTP服务启动失败, " + e.toString()
|
// httpServerError = "HTTP服务启动失败, " + e.toString()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
stop() {
|
stop() {
|
||||||
// llonebotError.httpServerError = ""
|
// httpServerError = ""
|
||||||
if (this.server) {
|
if (this.server) {
|
||||||
this.server.close();
|
this.server.close();
|
||||||
this.server = null;
|
this.server = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
restart(port: number) {
|
restart(port: number, host: string) {
|
||||||
this.stop();
|
this.stop();
|
||||||
this.start(port);
|
this.start(port, host);
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract handleFailed(res: Response, payload: any, err: any): void
|
abstract handleFailed(res: Response, payload: any, err: any): void
|
||||||
@@ -86,7 +88,7 @@ export abstract class HttpServerBase {
|
|||||||
// @ts-expect-error wait fix
|
// @ts-expect-error wait fix
|
||||||
if (!this.expressAPP[method]) {
|
if (!this.expressAPP[method]) {
|
||||||
const err = `${this.name} register router failed,${method} not exist`;
|
const err = `${this.name} register router failed,${method} not exist`;
|
||||||
log(err);
|
logError(err);
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
// @ts-expect-error wait fix
|
// @ts-expect-error wait fix
|
||||||
@@ -94,8 +96,10 @@ export abstract class HttpServerBase {
|
|||||||
let payload = req.body;
|
let payload = req.body;
|
||||||
if (method == 'get') {
|
if (method == 'get') {
|
||||||
payload = req.query;
|
payload = req.query;
|
||||||
|
} else if (req.query) {
|
||||||
|
payload = { ...req.query, ...req.body };
|
||||||
}
|
}
|
||||||
log('收到http请求', url, payload);
|
logDebug('收到http请求', url, payload);
|
||||||
try {
|
try {
|
||||||
res.send(await handler(res, payload));
|
res.send(await handler(res, payload));
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
@@ -104,10 +108,17 @@ export abstract class HttpServerBase {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
protected listen(port: number) {
|
protected listen(port: number, host: string = '0.0.0.0') {
|
||||||
this.server = this.expressAPP.listen(port, '0.0.0.0', () => {
|
host = host || '0.0.0.0';
|
||||||
const info = `${this.name} started 0.0.0.0:${port}`;
|
try {
|
||||||
log(info);
|
this.server = this.expressAPP.listen(port, host, () => {
|
||||||
});
|
const info = `${this.name} started ${host}:${port}`;
|
||||||
|
log(info);
|
||||||
|
}).on('error', (err) => {
|
||||||
|
logError('HTTP服务启动失败', err.toString());
|
||||||
|
});
|
||||||
|
} catch (e: any) {
|
||||||
|
logError('HTTP服务启动失败, 请检查监听的ip地址和端口', e.stack.toString());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -27,11 +27,17 @@ export class WebsocketServerBase {
|
|||||||
constructor() {
|
constructor() {
|
||||||
}
|
}
|
||||||
|
|
||||||
start(port: number) {
|
start(port: number, host: string = '') {
|
||||||
try {
|
try {
|
||||||
this.ws = new WebSocketServer({ port });
|
this.ws = new WebSocketServer({
|
||||||
|
port,
|
||||||
|
host: '',
|
||||||
|
maxPayload: 1024 * 1024 * 1024
|
||||||
|
}).on('error', () => {
|
||||||
|
});
|
||||||
|
log(`ws服务启动成功, ${host}:${port}`);
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
throw Error('ws服务启动失败, ' + e.toString());
|
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() || '/';
|
||||||
|
36
src/common/utils/AsyncQueue.ts
Normal file
36
src/common/utils/AsyncQueue.ts
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
73
src/common/utils/ConfigBase.ts
Normal file
73
src/common/utils/ConfigBase.ts
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
import path from 'node:path';
|
||||||
|
import fs from 'node:fs';
|
||||||
|
import { log, logDebug, logError } from '@/common/utils/log';
|
||||||
|
import { dirname } from 'node:path';
|
||||||
|
import { fileURLToPath } from 'node:url';
|
||||||
|
|
||||||
|
|
||||||
|
const __filename = fileURLToPath(import.meta.url);
|
||||||
|
const __dirname = dirname(__filename);
|
||||||
|
|
||||||
|
const configDir = path.resolve(__dirname, 'config');
|
||||||
|
fs.mkdirSync(configDir, { recursive: true });
|
||||||
|
|
||||||
|
|
||||||
|
export class ConfigBase<T>{
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
}
|
||||||
|
|
||||||
|
protected getKeys(): string[] | null {
|
||||||
|
// 决定 key 在json配置文件中的顺序
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
getConfigDir(){
|
||||||
|
const configDir = path.resolve(__dirname, 'config');
|
||||||
|
fs.mkdirSync(configDir, { recursive: true });
|
||||||
|
return configDir;
|
||||||
|
}
|
||||||
|
getConfigPath(): string {
|
||||||
|
throw new Error('Method not implemented.');
|
||||||
|
}
|
||||||
|
|
||||||
|
read() {
|
||||||
|
const configPath = this.getConfigPath();
|
||||||
|
if (!fs.existsSync(configPath)) {
|
||||||
|
try{
|
||||||
|
fs.writeFileSync(configPath, JSON.stringify(this, this.getKeys(), 2));
|
||||||
|
log(`配置文件${configPath}已创建\n如果修改此文件后需要重启 NapCat 生效`);
|
||||||
|
}
|
||||||
|
catch (e: any) {
|
||||||
|
logError(`创建配置文件 ${configPath} 时发生错误:`, e.message);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const data = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
|
||||||
|
logDebug(`配置文件${configPath}已加载`, data);
|
||||||
|
Object.assign(this, data);
|
||||||
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
|
// @ts-expect-error
|
||||||
|
this.save(this); // 保存一次,让新版本的字段写入
|
||||||
|
return this;
|
||||||
|
} catch (e: any) {
|
||||||
|
if (e instanceof SyntaxError) {
|
||||||
|
logError(`配置文件 ${configPath} 格式错误,请检查配置文件:`, e.message);
|
||||||
|
} else {
|
||||||
|
logError(`读取配置文件 ${configPath} 时发生错误:`, e.message);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
save(config: T) {
|
||||||
|
Object.assign(this, config);
|
||||||
|
const configPath = this.getConfigPath();
|
||||||
|
try {
|
||||||
|
fs.writeFileSync(configPath, JSON.stringify(this, this.getKeys(), 2));
|
||||||
|
} catch (e: any) {
|
||||||
|
logError(`保存配置文件 ${configPath} 时发生错误:`, e.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
182
src/common/utils/EventTask.ts
Normal file
182
src/common/utils/EventTask.ts
Normal file
@@ -0,0 +1,182 @@
|
|||||||
|
import { NodeIKernelMsgListener } from '@/core';
|
||||||
|
import { NodeIQQNTWrapperSession } from '@/core/wrapper';
|
||||||
|
import { randomUUID } from 'crypto';
|
||||||
|
|
||||||
|
interface Internal_MapKey {
|
||||||
|
timeout: number,
|
||||||
|
createtime: number,
|
||||||
|
func: (...arg: any[]) => any,
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ListenerClassBase {
|
||||||
|
[key: string]: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ListenerIBase {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-misused-new
|
||||||
|
new(listener: any): ListenerClassBase;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class NTEventWrapper {
|
||||||
|
|
||||||
|
private ListenerMap: { [key: string]: ListenerIBase } | undefined;//ListenerName-Unique -> Listener构造函数
|
||||||
|
private WrapperSession: NodeIQQNTWrapperSession | undefined;//WrapperSession
|
||||||
|
private ListenerManger: Map<string, ListenerClassBase> = new Map<string, ListenerClassBase>(); //ListenerName-Unique -> Listener实例
|
||||||
|
private EventTask = new Map<string, Map<string, Map<string, Internal_MapKey>>>();//tasks ListenerMainName -> ListenerSubName-> uuid -> {timeout,createtime,func}
|
||||||
|
constructor() {
|
||||||
|
|
||||||
|
}
|
||||||
|
createProxyDispatch(ListenerMainName: string) {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
||||||
|
const current = this;
|
||||||
|
return new Proxy({}, {
|
||||||
|
get(target: any, prop: any, receiver: any) {
|
||||||
|
// console.log('get', prop, typeof target[prop]);
|
||||||
|
if (typeof target[prop] === 'undefined') {
|
||||||
|
// 如果方法不存在,返回一个函数,这个函数调用existentMethod
|
||||||
|
return (...args: any[]) => {
|
||||||
|
current.DispatcherListener.apply(current, [ListenerMainName, prop, ...args]).then();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
// 如果方法存在,正常返回
|
||||||
|
return Reflect.get(target, prop, receiver);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
init({ ListenerMap, WrapperSession }: { ListenerMap: { [key: string]: typeof ListenerClassBase }, WrapperSession: NodeIQQNTWrapperSession }) {
|
||||||
|
this.ListenerMap = ListenerMap;
|
||||||
|
this.WrapperSession = WrapperSession;
|
||||||
|
}
|
||||||
|
CreatEventFunction<T extends (...args: any) => any>(eventName: string): T | undefined {
|
||||||
|
const eventNameArr = eventName.split('/');
|
||||||
|
type eventType = {
|
||||||
|
[key: string]: () => { [key: string]: (...params: Parameters<T>) => Promise<ReturnType<T>> }
|
||||||
|
}
|
||||||
|
if (eventNameArr.length > 1) {
|
||||||
|
const serviceName = 'get' + eventNameArr[0].replace('NodeIKernel', '');
|
||||||
|
const eventName = eventNameArr[1];
|
||||||
|
//getNodeIKernelGroupListener,GroupService
|
||||||
|
//console.log('2', eventName);
|
||||||
|
const services = (this.WrapperSession as unknown as eventType)[serviceName]();
|
||||||
|
let event = services[eventName];
|
||||||
|
//重新绑定this
|
||||||
|
event = event.bind(services);
|
||||||
|
if (event) {
|
||||||
|
return event as T;
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
CreatListenerFunction<T>(listenerMainName: string, uniqueCode: string = ''): T {
|
||||||
|
const ListenerType = this.ListenerMap![listenerMainName];
|
||||||
|
let Listener = this.ListenerManger.get(listenerMainName + uniqueCode);
|
||||||
|
if (!Listener && ListenerType) {
|
||||||
|
Listener = new ListenerType(this.createProxyDispatch(listenerMainName));
|
||||||
|
const ServiceSubName = listenerMainName.match(/^NodeIKernel(.*?)Listener$/)![1];
|
||||||
|
const Service = 'NodeIKernel' + ServiceSubName + 'Service/addKernel' + ServiceSubName + 'Listener';
|
||||||
|
const addfunc = this.CreatEventFunction<(listener: T) => number>(Service);
|
||||||
|
addfunc!(Listener as T);
|
||||||
|
//console.log(addfunc!(Listener as T));
|
||||||
|
this.ListenerManger.set(listenerMainName + uniqueCode, Listener);
|
||||||
|
}
|
||||||
|
return Listener as T;
|
||||||
|
}
|
||||||
|
//统一回调清理事件
|
||||||
|
async DispatcherListener(ListenerMainName: string, ListenerSubName: string, ...args: any[]) {
|
||||||
|
//console.log(ListenerMainName, this.EventTask.get(ListenerMainName), ListenerSubName, this.EventTask.get(ListenerMainName)?.get(ListenerSubName));
|
||||||
|
this.EventTask.get(ListenerMainName)?.get(ListenerSubName)?.forEach((task, uuid) => {
|
||||||
|
//console.log(task.func, uuid, task.createtime, task.timeout);
|
||||||
|
if (task.createtime + task.timeout < Date.now()) {
|
||||||
|
this.EventTask.get(ListenerMainName)?.get(ListenerSubName)?.delete(uuid);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
task.func(...args);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
async CallNoListenerEvent<EventType extends (...args: any[]) => Promise<any>,>(EventName = '', timeout: number = 3000, ...args: Parameters<EventType>) {
|
||||||
|
return new Promise<Awaited<ReturnType<EventType>>>(async (resolve, reject) => {
|
||||||
|
const EventFunc = this.CreatEventFunction<EventType>(EventName);
|
||||||
|
let complete = false;
|
||||||
|
const Timeouter = setTimeout(() => {
|
||||||
|
if (!complete) {
|
||||||
|
reject(new Error('NTEvent EventName:' + EventName + ' timeout'));
|
||||||
|
}
|
||||||
|
}, timeout);
|
||||||
|
const retData = await EventFunc!(...args);
|
||||||
|
complete = true;
|
||||||
|
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>) {
|
||||||
|
return new Promise<[EventRet: Awaited<ReturnType<EventType>>, ...Parameters<ListenerType>]>(async (resolve, reject) => {
|
||||||
|
const id = randomUUID();
|
||||||
|
let complete = 0;
|
||||||
|
let retData: Parameters<ListenerType> | undefined = undefined;
|
||||||
|
let retEvent: any = {};
|
||||||
|
const databack = () => {
|
||||||
|
if (complete < waitTimes) {
|
||||||
|
reject(new Error('NTEvent EventName:' + EventName + ' ListenerName:' + ListenerName + ' timeout'));
|
||||||
|
} else {
|
||||||
|
resolve([retEvent as Awaited<ReturnType<EventType>>, ...retData!]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const Timeouter = setTimeout(databack, timeout);
|
||||||
|
|
||||||
|
const ListenerNameList = ListenerName.split('/');
|
||||||
|
const ListenerMainName = ListenerNameList[0];
|
||||||
|
const ListenerSubName = ListenerNameList[1];
|
||||||
|
const eventCallbak = {
|
||||||
|
timeout: timeout,
|
||||||
|
createtime: Date.now(),
|
||||||
|
func: (...args: any[]) => {
|
||||||
|
complete++;
|
||||||
|
//console.log('func', ...args);
|
||||||
|
retData = args as Parameters<ListenerType>;
|
||||||
|
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);
|
||||||
|
const EventFunc = this.CreatEventFunction<EventType>(EventName);
|
||||||
|
//console.log("测试打点", args);
|
||||||
|
retEvent = await EventFunc!(...(args as any[]));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export const NTEventDispatch = new NTEventWrapper();
|
||||||
|
|
||||||
|
// 示例代码 快速创建事件
|
||||||
|
// let NTEvent = new NTEventWrapper();
|
||||||
|
// let TestEvent = NTEvent.CreatEventFunction<(force: boolean) => Promise<Number>>('NodeIKernelProfileLikeService/GetTest');
|
||||||
|
// if (TestEvent) {
|
||||||
|
// TestEvent(true);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// 示例代码 快速创建监听Listener类
|
||||||
|
// let NTEvent = new NTEventWrapper();
|
||||||
|
// NTEvent.CreatListenerFunction<NodeIKernelMsgListener>('NodeIKernelMsgListener', 'core')
|
||||||
|
|
||||||
|
|
||||||
|
// 调用接口
|
||||||
|
//let NTEvent = new NTEventWrapper();
|
||||||
|
//let ret = await NTEvent.CallNormalEvent<(force: boolean) => Promise<Number>, (data1: string, data2: number) => void>('NodeIKernelProfileLikeService/GetTest', 'NodeIKernelMsgListener/onAddSendMsg', 1, 3000, true);
|
||||||
|
|
||||||
|
// 注册监听 解除监听
|
||||||
|
// NTEventDispatch.RigisterListener('NodeIKernelMsgListener/onAddSendMsg','core',cb);
|
||||||
|
// NTEventDispatch.UnRigisterListener('NodeIKernelMsgListener/onAddSendMsg','core');
|
||||||
|
|
||||||
|
// let GetTest = NTEventDispatch.CreatEvent('NodeIKernelProfileLikeService/GetTest','NodeIKernelMsgListener/onAddSendMsg',Mode);
|
||||||
|
// GetTest('test');
|
||||||
|
|
||||||
|
// always模式
|
||||||
|
// NTEventDispatch.CreatEvent('NodeIKernelProfileLikeService/GetTest','NodeIKernelMsgListener/onAddSendMsg',Mode,(...args:any[])=>{ console.log(args) });
|
145
src/common/utils/LRUCache.ts
Normal file
145
src/common/utils/LRUCache.ts
Normal file
@@ -0,0 +1,145 @@
|
|||||||
|
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 = 2e4, 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default LRU;
|
53
src/common/utils/MessageUnique.ts
Normal file
53
src/common/utils/MessageUnique.ts
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
import crypto from 'crypto';
|
||||||
|
|
||||||
|
class LimitedHashTable<K, V> {
|
||||||
|
private keyToValue: Map<K, V> = new Map();
|
||||||
|
private valueToKey: Map<V, K> = new Map();
|
||||||
|
private maxSize: number;
|
||||||
|
private KeyQueneList: K[] = [];
|
||||||
|
private ValueQueneList: V[] = [];
|
||||||
|
constructor(maxSize: number) {
|
||||||
|
this.maxSize = maxSize;
|
||||||
|
}
|
||||||
|
set(key: K, value: V): void {
|
||||||
|
this.keyToValue.set(key, value);
|
||||||
|
this.valueToKey.set(value, key);
|
||||||
|
if (this.KeyQueneList.length >= this.maxSize || this.ValueQueneList.length >= this.maxSize) {
|
||||||
|
this.KeyQueneList.shift();
|
||||||
|
this.ValueQueneList.shift();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getValue(key: K): V | undefined {
|
||||||
|
return this.keyToValue.get(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
getKey(value: V): K | undefined {
|
||||||
|
return this.valueToKey.get(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
delete(key: K): void {
|
||||||
|
const value = this.keyToValue.get(key);
|
||||||
|
if (value !== undefined) {
|
||||||
|
this.keyToValue.delete(key);
|
||||||
|
this.valueToKey.delete(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class MessageUniqueWrapper {
|
||||||
|
private msgIdMap: LimitedHashTable<number, string> = new LimitedHashTable(1000);
|
||||||
|
createMsg(MsgId: string) {
|
||||||
|
let ShortId = parseInt(crypto.createHash('sha1').update('2345').digest('hex').slice(0, 8), 16);
|
||||||
|
this.msgIdMap.set(ShortId, MsgId);
|
||||||
|
return ShortId;
|
||||||
|
}
|
||||||
|
getMsgIdByShortId(ShortId: number) {
|
||||||
|
return this.msgIdMap.getValue(ShortId);
|
||||||
|
}
|
||||||
|
getShortIdByMsgId(MsgId: string) {
|
||||||
|
return this.msgIdMap.getKey(MsgId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const MessageUnique = new MessageUniqueWrapper();
|
@@ -2,6 +2,7 @@ import path from 'node:path';
|
|||||||
import fs from 'node:fs';
|
import fs from 'node:fs';
|
||||||
import os from 'node:os';
|
import os from 'node:os';
|
||||||
import { systemPlatform } from '@/common/utils/system';
|
import { systemPlatform } from '@/common/utils/system';
|
||||||
|
import { logError } from '@/common/utils/log';
|
||||||
|
|
||||||
export const exePath = process.execPath;
|
export const exePath = process.execPath;
|
||||||
|
|
||||||
@@ -37,24 +38,36 @@ type QQVersionConfigInfo = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let _qqVersionConfigInfo: QQVersionConfigInfo = {
|
let _qqVersionConfigInfo: QQVersionConfigInfo = {
|
||||||
'baseVersion': '9.9.9-22578',
|
'baseVersion': '9.9.9-23361',
|
||||||
'curVersion': '9.9.9-22578',
|
'curVersion': '9.9.9-23361',
|
||||||
'prevVersion': '',
|
'prevVersion': '',
|
||||||
'onErrorVersions': [],
|
'onErrorVersions': [],
|
||||||
'buildId': '22578'
|
'buildId': '23361'
|
||||||
};
|
};
|
||||||
|
|
||||||
if (fs.existsSync(configVersionInfoPath)) {
|
if (fs.existsSync(configVersionInfoPath)) {
|
||||||
_qqVersionConfigInfo = JSON.parse(fs.readFileSync(configVersionInfoPath).toString());
|
try {
|
||||||
|
const _ =JSON.parse(fs.readFileSync(configVersionInfoPath).toString());
|
||||||
|
_qqVersionConfigInfo = Object.assign(_qqVersionConfigInfo, _);
|
||||||
|
} catch (e) {
|
||||||
|
logError('Load QQ version config info failed, Use default version', e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const qqVersionConfigInfo: QQVersionConfigInfo = _qqVersionConfigInfo;
|
export const qqVersionConfigInfo: QQVersionConfigInfo = _qqVersionConfigInfo;
|
||||||
|
|
||||||
export const qqPkgInfo: QQPkgInfo = require(pkgInfoPath);
|
export const qqPkgInfo: QQPkgInfo = JSON.parse(fs.readFileSync(pkgInfoPath).toString());
|
||||||
|
// platform_type: 3,
|
||||||
|
// app_type: 4,
|
||||||
|
// app_version: '9.9.9-23159',
|
||||||
|
// qua: 'V1_WIN_NQ_9.9.9_23159_GW_B',
|
||||||
|
// appid: '537213764',
|
||||||
|
// platVer: '10.0.26100',
|
||||||
|
// clientVer: '9.9.9-23159',
|
||||||
|
|
||||||
let _appid: string = '537213335'; // 默认为 Windows 平台的 appid
|
let _appid: string = '537216417'; // 默认为 Windows 平台的 appid
|
||||||
if (systemPlatform === 'linux') {
|
if (systemPlatform === 'linux') {
|
||||||
_appid = '537213710';
|
_appid = '537213827';
|
||||||
}
|
}
|
||||||
// todo: mac 平台的 appid
|
// todo: mac 平台的 appid
|
||||||
export const appid = _appid;
|
export const appid = _appid;
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import { encode, getDuration, getWavFileInfo, isWav } from 'silk-wasm';
|
import { encode, getDuration, getWavFileInfo, isWav } from 'silk-wasm';
|
||||||
import fsPromise from 'fs/promises';
|
import fsPromise from 'fs/promises';
|
||||||
import { log } from './log';
|
import { log, logError } from './log';
|
||||||
import path from 'node:path';
|
import path from 'node:path';
|
||||||
import { v4 as uuidv4 } from 'uuid';
|
import { v4 as uuidv4 } from 'uuid';
|
||||||
import { spawn } from 'node:child_process';
|
import { spawn } from 'node:child_process';
|
||||||
@@ -24,7 +24,7 @@ export async function encodeSilk(filePath: string) {
|
|||||||
const fileHeader = buffer.toString('hex', 0, bytesToRead);
|
const fileHeader = buffer.toString('hex', 0, bytesToRead);
|
||||||
return fileHeader;
|
return fileHeader;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('读取文件错误:', err);
|
logError('读取文件错误:', err);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -129,7 +129,7 @@ export async function encodeSilk(filePath: string) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
log('convert silk failed', error.stack);
|
logError('convert silk failed', error.stack);
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
23
src/common/utils/cpmodule.ts
Normal file
23
src/common/utils/cpmodule.ts
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import * as os from 'os';
|
||||||
|
import path from 'node:path';
|
||||||
|
import fs from 'fs';
|
||||||
|
import { dirname } from 'node:path';
|
||||||
|
import { fileURLToPath } from 'node:url';
|
||||||
|
|
||||||
|
const __filename = fileURLToPath(import.meta.url);
|
||||||
|
const __dirname = dirname(__filename);
|
||||||
|
export function getModuleWithArchName(moduleName: string) {
|
||||||
|
const systemPlatform = os.platform();
|
||||||
|
const cpuArch = os.arch();
|
||||||
|
return `${moduleName}-${systemPlatform}-${cpuArch}.node`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function cpModule(moduleName: string) {
|
||||||
|
const currentDir = path.resolve(__dirname);
|
||||||
|
const fileName = `./${getModuleWithArchName(moduleName)}`;
|
||||||
|
try {
|
||||||
|
fs.copyFileSync(path.join(currentDir, fileName), path.join(currentDir, `${moduleName}.node`));
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
}
|
@@ -1,14 +1,24 @@
|
|||||||
import { ElementType, FileElement, PicElement, PttElement, RawMessage, VideoElement } from '@/core/qqnt/entities';
|
import { ElementType, FileElement, PicElement, PttElement, RawMessage, VideoElement } from '../../core/src/entities';
|
||||||
|
|
||||||
import sqlite3 from 'sqlite3';
|
import sqlite3 from 'sqlite3';
|
||||||
import { log } from '@/common/utils/log';
|
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 = {
|
type DBMsg = {
|
||||||
id: number,
|
id: number,
|
||||||
|
shortId: number,
|
||||||
longId: string,
|
longId: string,
|
||||||
seq: number,
|
seq: number,
|
||||||
peerUid: string,
|
peerUid: string,
|
||||||
msg: string
|
chatType: number,
|
||||||
}
|
}
|
||||||
|
|
||||||
type DBFile = {
|
type DBFile = {
|
||||||
@@ -27,16 +37,20 @@ type DBFile = {
|
|||||||
class DBUtilBase {
|
class DBUtilBase {
|
||||||
protected db: sqlite3.Database | undefined;
|
protected db: sqlite3.Database | undefined;
|
||||||
|
|
||||||
createConnection(dbPath: string) {
|
async init(dbPath: string) {
|
||||||
if (this.db) {
|
if (this.db) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.db = new sqlite3.Database(dbPath, sqlite3.OPEN_READWRITE | sqlite3.OPEN_CREATE, (err) => {
|
return new Promise<void>((resolve, reject) => {
|
||||||
if (err) {
|
this.db = new sqlite3.Database(dbPath, sqlite3.OPEN_READWRITE | sqlite3.OPEN_CREATE, (err) => {
|
||||||
log('Could not connect to database', err);
|
if (err) {
|
||||||
return;
|
logError('Could not connect to database', err);
|
||||||
}
|
reject(err);
|
||||||
this.createTable();
|
return;
|
||||||
|
}
|
||||||
|
this.createTable();
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -50,13 +64,38 @@ class DBUtilBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class DBUtil extends DBUtilBase {
|
class DBUtil extends DBUtilBase {
|
||||||
private msgCache: Map<string, RawMessage> = new Map<string, RawMessage>();
|
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() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
const interval = 1000 * 60 * 10; // 10分钟清理一次缓存
|
const interval = 1000 * 60 * 10; // 10分钟清理一次缓存
|
||||||
setInterval(() => {
|
setInterval(() => {
|
||||||
log('清理消息缓存');
|
logDebug('清理消息缓存');
|
||||||
this.msgCache.forEach((msg, key) => {
|
this.msgCache.forEach((msg, key) => {
|
||||||
if ((Date.now() - parseInt(msg.msgTime) * 1000) > interval) {
|
if ((Date.now() - parseInt(msg.msgTime) * 1000) > interval) {
|
||||||
this.msgCache.delete(key);
|
this.msgCache.delete(key);
|
||||||
@@ -65,20 +104,112 @@ class DBUtil extends DBUtilBase {
|
|||||||
}, interval);
|
}, 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() {
|
protected createTable() {
|
||||||
// 消息记录
|
// 消息记录
|
||||||
const createTableSQL = `
|
const createTableSQL = `
|
||||||
CREATE TABLE IF NOT EXISTS msgs (
|
CREATE TABLE IF NOT EXISTS msgs (
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
long_id TEXT NOT NULL UNIQUE,
|
shortId INTEGER NOT NULL UNIQUE,
|
||||||
|
longId TEXT NOT NULL UNIQUE,
|
||||||
seq INTEGER NOT NULL,
|
seq INTEGER NOT NULL,
|
||||||
peer_uid TEXT NOT NULL,
|
peerUid TEXT NOT NULL,
|
||||||
msg TEXT NOT NULL
|
chatType INTEGER NOT NULL
|
||||||
)`;
|
)`;
|
||||||
this.db!.run(createTableSQL, function (err) {
|
this.db!.run(createTableSQL, function (err) {
|
||||||
if (err) {
|
if (err) {
|
||||||
log('Could not create table', err);
|
logError('Could not create table msgs', err.stack);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -98,21 +229,21 @@ class DBUtil extends DBUtilBase {
|
|||||||
)`;
|
)`;
|
||||||
this.db!.run(createFileTableSQL, function (err) {
|
this.db!.run(createFileTableSQL, function (err) {
|
||||||
if (err) {
|
if (err) {
|
||||||
log('Could not create table files', err);
|
logError('Could not create table files', err);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// 接收到的临时会话消息uid
|
private async getCurrentMaxShortId() {
|
||||||
const createTempUinTableSQL = `
|
return new Promise<number>((resolve, reject) => {
|
||||||
CREATE TABLE IF NOT EXISTS temp_uins (
|
this.db!.get('SELECT MAX(shortId) as maxId FROM msgs', (err, row: { maxId: number }) => {
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
if (err) {
|
||||||
uid TEXT,
|
logDebug('Could not get max short id, Use default -2147483640', err);
|
||||||
uin TEXT
|
return resolve(-2147483640);
|
||||||
)`;
|
}
|
||||||
this.db!.run(createTempUinTableSQL, function (err) {
|
logDebug('数据库中消息最大短id', row?.maxId);
|
||||||
if (err) {
|
resolve(row?.maxId ?? -2147483640);
|
||||||
log('Could not create table temp_uins', err);
|
});
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -122,22 +253,35 @@ class DBUtil extends DBUtilBase {
|
|||||||
stmt.get(...params, (err: any, row: DBMsg) => {
|
stmt.get(...params, (err: any, row: DBMsg) => {
|
||||||
// log("getMsg", row, err);
|
// log("getMsg", row, err);
|
||||||
if (err) {
|
if (err) {
|
||||||
log('Could not get msg by short id', err);
|
logError('Could not get msg', err, query, params);
|
||||||
resolve(null);
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
const msg = JSON.parse(row.msg);
|
|
||||||
msg.id = row.id;
|
|
||||||
return resolve(msg);
|
|
||||||
} catch (e) {
|
|
||||||
return resolve(null);
|
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> {
|
async getMsgByShortId(shortId: number): Promise<RawMessage | null> {
|
||||||
const getStmt = 'SELECT * FROM msgs WHERE id = ?';
|
if (this.msgCache.has(shortId)) {
|
||||||
|
return this.msgCache.get(shortId)!;
|
||||||
|
}
|
||||||
|
const getStmt = 'SELECT * FROM msgs WHERE shortId = ?';
|
||||||
return this.getMsg(getStmt, [shortId]);
|
return this.getMsg(getStmt, [shortId]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -145,60 +289,46 @@ class DBUtil extends DBUtilBase {
|
|||||||
if (this.msgCache.has(longId)) {
|
if (this.msgCache.has(longId)) {
|
||||||
return this.msgCache.get(longId)!;
|
return this.msgCache.get(longId)!;
|
||||||
}
|
}
|
||||||
return this.getMsg('SELECT * FROM msgs WHERE long_id = ?', [longId]);
|
return this.getMsg('SELECT * FROM msgs WHERE longId = ?', [longId]);
|
||||||
}
|
}
|
||||||
|
|
||||||
async getMsgBySeq(peerUid: string, seq: string): Promise<RawMessage | null> {
|
async getMsgBySeq(peerUid: string, seq: string): Promise<RawMessage | null> {
|
||||||
const stmt = 'SELECT * FROM msgs WHERE peer_uid = ? AND seq = ?';
|
const stmt = 'SELECT * FROM msgs WHERE peerUid = ? AND seq = ?';
|
||||||
return this.getMsg(stmt, [peerUid, seq]);
|
return this.getMsg(stmt, [peerUid, seq]);
|
||||||
}
|
}
|
||||||
|
|
||||||
async addMsg(msg: RawMessage, update = true): Promise<number> {
|
async addMsg(msg: RawMessage, update = true): Promise<number> {
|
||||||
log('正在记录消息到数据库', msg.msgId);
|
|
||||||
const existMsg = await this.getMsgByLongId(msg.msgId);
|
const existMsg = await this.getMsgByLongId(msg.msgId);
|
||||||
if (existMsg) {
|
if (existMsg) {
|
||||||
// log('消息已存在,更新数据库', msg.msgId);
|
// logDebug('消息已存在,更新数据库', msg.msgId);
|
||||||
if (update) this.updateMsg(msg).then();
|
if (update) this.updateMsg(msg).then();
|
||||||
return existMsg.id!;
|
return existMsg.id!;
|
||||||
}
|
}
|
||||||
const stmt = this.db!.prepare('INSERT INTO msgs (long_id, seq, peer_uid, msg) VALUES (?, ?, ?, ?)');
|
const stmt = this.db!.prepare('INSERT INTO msgs (shortId, longId, seq, peerUid, chatType) VALUES (?, ?, ?, ?, ?)');
|
||||||
|
|
||||||
// const runAsync = promisify(stmt.run.bind(stmt));
|
// const runAsync = promisify(stmt.run.bind(stmt));
|
||||||
return new Promise((resolve, reject) => {
|
const shortId = ++this.globalMsgShortId;
|
||||||
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
msg.id = shortId;
|
||||||
const dbInstance = this;
|
//logDebug(`记录消息到数据库, 消息长id: ${msg.msgId}, 短id: ${msg.id}`);
|
||||||
stmt.run(msg.msgId, msg.msgSeq, msg.peerUid, JSON.stringify(msg), function (err: any) {
|
this.msgCache.set(shortId, msg);
|
||||||
if (err) {
|
this.msgCache.set(msg.msgId, msg);
|
||||||
if (err.errno === 19) {
|
stmt.run(this.globalMsgShortId, msg.msgId, msg.msgSeq.toString(), msg.peerUid, msg.chatType, (err: any) => {
|
||||||
// log('消息已存在,更新数据库', msg.msgId);
|
if (err) {
|
||||||
dbInstance.getMsgByLongId(msg.msgId).then((msg: RawMessage | null) => {
|
if (err.errno === 19) {
|
||||||
if (msg) {
|
this.getMsgByLongId(msg.msgId).then((msg: RawMessage | null) => {
|
||||||
dbInstance.msgCache.set(msg.msgId, msg);
|
if (msg) {
|
||||||
// log('获取消息短id成功', msg.id);
|
this.msgCache.set(shortId, msg);
|
||||||
resolve(msg.id!);
|
this.msgCache.set(msg.msgId, msg);
|
||||||
} else {
|
// logDebug('获取消息短id成功', msg.id);
|
||||||
log('db could not get msg by long id', err);
|
} else {
|
||||||
resolve(-1);
|
logError('db could not get msg by long id', err);
|
||||||
}
|
}
|
||||||
});
|
}).catch(e => logError('db getMsgByLongId error', e));
|
||||||
} else {
|
|
||||||
log('db could not add msg', err);
|
|
||||||
resolve(-1);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// log("addMsg", this);
|
logError('db could not add msg', err);
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
||||||
// @ts-expect-error
|
|
||||||
msg.id = this.lastID;
|
|
||||||
dbInstance.msgCache.set(msg.msgId, msg);
|
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
||||||
// log('获取消息短id成功', this.lastID);
|
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
||||||
// @ts-expect-error
|
|
||||||
resolve(this.lastID);
|
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
});
|
});
|
||||||
|
return shortId;
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateMsg(msg: RawMessage) {
|
async updateMsg(msg: RawMessage) {
|
||||||
@@ -206,12 +336,14 @@ class DBUtil extends DBUtilBase {
|
|||||||
if (existMsg) {
|
if (existMsg) {
|
||||||
Object.assign(existMsg, msg);
|
Object.assign(existMsg, msg);
|
||||||
}
|
}
|
||||||
const stmt = this.db!.prepare('UPDATE msgs SET msg = ?, seq = ? WHERE long_id = ?');
|
//logDebug(`更新消息, shortId:${msg.id}, seq: ${msg.msgSeq}, msgId: ${msg.msgId}`);
|
||||||
try {
|
const stmt = this.db!.prepare('UPDATE msgs SET seq=? WHERE longId=?');
|
||||||
stmt.run(JSON.stringify(msg), msg.msgSeq, msg.msgId);
|
stmt.run(msg.msgSeq, msg.msgId, (err: any) => {
|
||||||
} catch (e) {
|
if (err) {
|
||||||
log('updateMsg db error', e);
|
logError('updateMsg db error', err);
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async addFileCache(file: DBFile) {
|
async addFileCache(file: DBFile) {
|
||||||
@@ -224,7 +356,7 @@ class DBUtil extends DBUtilBase {
|
|||||||
file.msgId,
|
file.msgId,
|
||||||
function (err: any) {
|
function (err: any) {
|
||||||
if (err) {
|
if (err) {
|
||||||
log('db could not add file', err);
|
logError('db could not add file', err);
|
||||||
reject(err);
|
reject(err);
|
||||||
}
|
}
|
||||||
resolve(null);
|
resolve(null);
|
||||||
@@ -237,7 +369,7 @@ class DBUtil extends DBUtilBase {
|
|||||||
return new Promise<DBFile | null>((resolve, reject) => {
|
return new Promise<DBFile | null>((resolve, reject) => {
|
||||||
stmt.get(...params, (err: any, row: DBFile & { element: string }) => {
|
stmt.get(...params, (err: any, row: DBFile & { element: string }) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
log('db could not get file cache', err);
|
logError('db could not get file cache', err);
|
||||||
reject(err);
|
reject(err);
|
||||||
}
|
}
|
||||||
if (row) {
|
if (row) {
|
||||||
@@ -260,9 +392,9 @@ class DBUtil extends DBUtilBase {
|
|||||||
async updateFileCache(file: DBFile) {
|
async updateFileCache(file: DBFile) {
|
||||||
const stmt = this.db!.prepare('UPDATE files SET path = ?, url = ? WHERE uuid = ?');
|
const stmt = this.db!.prepare('UPDATE files SET path = ?, url = ? WHERE uuid = ?');
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
stmt.run(file.path, file.url, function (err: any) {
|
stmt.run(file.path, file.url, file.uuid, function (err: any) {
|
||||||
if (err) {
|
if (err) {
|
||||||
log('db could not update file cache', err);
|
logError('db could not update file cache', err);
|
||||||
reject(err);
|
reject(err);
|
||||||
}
|
}
|
||||||
resolve(null);
|
resolve(null);
|
||||||
@@ -270,52 +402,46 @@ class DBUtil extends DBUtilBase {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// 被动收到的临时会话消息uin->uid
|
async getLastSentTimeAndJoinTime(
|
||||||
async getReceivedTempUinMap() {
|
groupId: number
|
||||||
const stmt = 'SELECT * FROM temp_uins';
|
): Promise<IRember[]> {
|
||||||
return new Promise<Record<string, string>>((resolve, reject) => {
|
logDebug('读取发言时间', groupId);
|
||||||
this.db!.all(stmt, (err, rows: { uin: string, uid: string }[]) => {
|
return new Promise<IRember[]>((resolve, reject) => {
|
||||||
|
this.db!.all(`SELECT * FROM "${groupId}" `, (err, rows: IRember[]) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
log('db could not get temp uin map', err);
|
logError('查询发言时间失败', groupId);
|
||||||
reject(err);
|
return resolve([]);
|
||||||
}
|
}
|
||||||
const map: Record<string, string> = {};
|
logDebug('查询发言时间成功', groupId, rows);
|
||||||
rows.forEach(row => {
|
resolve(rows);
|
||||||
map[row.uin] = row.uid;
|
|
||||||
});
|
|
||||||
resolve(map);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// 通过uin获取临时会话消息uid
|
insertLastSentTime(
|
||||||
async getUidByTempUin(uid: string) {
|
groupId: number,
|
||||||
const stmt = 'SELECT * FROM temp_uins WHERE uin = ?';
|
userId: number,
|
||||||
return new Promise<string>((resolve, reject) => {
|
time: number
|
||||||
this.db!.get(stmt, [uid], (err, row: { uin: string, uid: string }) => {
|
) {
|
||||||
if (err) {
|
this.LURCache.set(groupId, userId, time);
|
||||||
log('db could not get temp uin map', err);
|
|
||||||
reject(err);
|
|
||||||
}
|
|
||||||
resolve(row?.uid);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
async addTempUin(uin: string, uid: string) {
|
|
||||||
const existUid = await this.getUidByTempUin(uin);
|
|
||||||
if (!existUid) {
|
|
||||||
const stmt = this.db!.prepare('INSERT INTO temp_uins (uin, uid) VALUES (?, ?)');
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
stmt.run(uin, uid, function (err: any) {
|
|
||||||
if (err) {
|
|
||||||
log('db could not add temp uin', err);
|
|
||||||
reject(err);
|
|
||||||
}
|
|
||||||
resolve(null);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -3,15 +3,14 @@ import fsPromise from 'fs/promises';
|
|||||||
import crypto from 'crypto';
|
import crypto from 'crypto';
|
||||||
import util from 'util';
|
import util from 'util';
|
||||||
import path from 'node:path';
|
import path from 'node:path';
|
||||||
import { log } from './log';
|
import { log, logError } from './log';
|
||||||
import { dbUtil } from './db';
|
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 { v4 as uuidv4 } from 'uuid';
|
||||||
import { napCatCore } from '@/core';
|
import { napCatCore } from '@/core';
|
||||||
import os from 'node:os';
|
|
||||||
|
|
||||||
export const getNapCatDir = () => {
|
export const getNapCatDir = () => {
|
||||||
const p = path.join(napCatCore.wrapper.dataPath, 'NapCat');
|
const p = path.join(napCatCore.dataPath, 'NapCat');
|
||||||
fs.mkdirSync(p, { recursive: true });
|
fs.mkdirSync(p, { recursive: true });
|
||||||
return p;
|
return p;
|
||||||
};
|
};
|
||||||
@@ -126,7 +125,7 @@ export async function httpDownload(options: string | HttpDownloadOptions): Promi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const fetchRes = await fetch(url, headers);
|
const fetchRes = await fetch(url, { headers });
|
||||||
if (!fetchRes.ok) throw new Error(`下载文件失败: ${fetchRes.statusText}`);
|
if (!fetchRes.ok) throw new Error(`下载文件失败: ${fetchRes.statusText}`);
|
||||||
|
|
||||||
const blob = await fetchRes.blob();
|
const blob = await fetchRes.blob();
|
||||||
@@ -194,6 +193,7 @@ export async function uri2local(uri: string, fileName: string | null = null): Pr
|
|||||||
// res.ext = pathInfo.ext
|
// res.ext = pathInfo.ext
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
fileName = fileName.replace(/[/\\:*?"<>|]/g, '_');
|
||||||
res.fileName = fileName;
|
res.fileName = fileName;
|
||||||
filePath = path.join(getTempDir(), uuidv4() + fileName);
|
filePath = path.join(getTempDir(), uuidv4() + fileName);
|
||||||
fs.writeFileSync(filePath, buffer);
|
fs.writeFileSync(filePath, buffer);
|
||||||
@@ -262,12 +262,12 @@ export async function copyFolder(sourcePath: string, destPath: string) {
|
|||||||
try {
|
try {
|
||||||
await fsPromise.copyFile(srcPath, dstPath);
|
await fsPromise.copyFile(srcPath, dstPath);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`无法复制文件 '${srcPath}' 到 '${dstPath}': ${error}`);
|
logError(`无法复制文件 '${srcPath}' 到 '${dstPath}': ${error}`);
|
||||||
// 这里可以决定是否要继续复制其他文件
|
// 这里可以决定是否要继续复制其他文件
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('复制文件夹时出错:', error);
|
logError('复制文件夹时出错:', error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,6 +1,13 @@
|
|||||||
import crypto from 'node:crypto';
|
import crypto from 'node:crypto';
|
||||||
import { resolve } from 'dns';
|
import path from 'node:path';
|
||||||
|
import fs from 'fs/promises';
|
||||||
|
import { log, logDebug } from './log';
|
||||||
|
import { dirname } from 'node:path';
|
||||||
|
import { fileURLToPath } from 'node:url';
|
||||||
|
|
||||||
|
|
||||||
|
const __filename = fileURLToPath(import.meta.url);
|
||||||
|
const __dirname = dirname(__filename);
|
||||||
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));
|
||||||
}
|
}
|
||||||
@@ -19,3 +26,159 @@ export function isNull(value: any) {
|
|||||||
export function isNumeric(str: string) {
|
export function isNumeric(str: string) {
|
||||||
return /^\d+$/.test(str);
|
return /^\d+$/.test(str);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function truncateString(obj: any, maxLength = 500) {
|
||||||
|
if (obj !== null && typeof obj === 'object') {
|
||||||
|
Object.keys(obj).forEach(key => {
|
||||||
|
if (typeof obj[key] === 'string') {
|
||||||
|
// 如果是字符串且超过指定长度,则截断
|
||||||
|
if (obj[key].length > maxLength) {
|
||||||
|
obj[key] = obj[key].substring(0, maxLength) + '...';
|
||||||
|
}
|
||||||
|
} else if (typeof obj[key] === 'object') {
|
||||||
|
// 如果是对象或数组,则递归调用
|
||||||
|
truncateString(obj[key], maxLength);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 函数缓存装饰器,根据方法名、参数、自定义key生成缓存键,在一定时间内返回缓存结果
|
||||||
|
* @param ttl 超时时间,单位毫秒
|
||||||
|
* @param customKey 自定义缓存键前缀,可为空,防止方法名参数名一致时导致缓存键冲突
|
||||||
|
* @returns 处理后缓存或调用原方法的结果
|
||||||
|
*/
|
||||||
|
export function cacheFunc(ttl: number, customKey: string = '') {
|
||||||
|
const cache = new Map<string, { expiry: number; value: any }>();
|
||||||
|
|
||||||
|
return function (target: any, propertyKey: string, descriptor: PropertyDescriptor): PropertyDescriptor {
|
||||||
|
const originalMethod = descriptor.value;
|
||||||
|
const className = target.constructor.name; // 获取类名
|
||||||
|
const methodName = propertyKey; // 获取方法名
|
||||||
|
descriptor.value = async function (...args: any[]) {
|
||||||
|
const cacheKey = `${customKey}${className}.${methodName}:${JSON.stringify(args)}`;
|
||||||
|
const cached = cache.get(cacheKey);
|
||||||
|
if (cached && cached.expiry > Date.now()) {
|
||||||
|
return cached.value;
|
||||||
|
} else {
|
||||||
|
const result = await originalMethod.apply(this, args);
|
||||||
|
cache.set(cacheKey, { value: result, expiry: Date.now() + ttl });
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return descriptor;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
export function isValidOldConfig(config: any) {
|
||||||
|
if (typeof config !== 'object') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const requiredKeys = [
|
||||||
|
'httpHost', 'httpPort', 'httpPostUrls', 'httpSecret',
|
||||||
|
'wsHost', 'wsPort', 'wsReverseUrls', 'enableHttp',
|
||||||
|
'enableHttpHeart', 'enableHttpPost', 'enableWs', 'enableWsReverse',
|
||||||
|
'messagePostFormat', 'reportSelfMessage', 'enableLocalFile2Url',
|
||||||
|
'debug', 'heartInterval', 'token', 'musicSignUrl'
|
||||||
|
];
|
||||||
|
for (const key of requiredKeys) {
|
||||||
|
if (!(key in config)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!Array.isArray(config.httpPostUrls) || !Array.isArray(config.wsReverseUrls)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (config.httpPostUrls.some((url: any) => typeof url !== 'string')) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (config.wsReverseUrls.some((url: any) => typeof url !== 'string')) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (typeof config.httpPort !== 'number' || typeof config.wsPort !== 'number' || typeof config.heartInterval !== 'number') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
typeof config.enableHttp !== 'boolean' ||
|
||||||
|
typeof config.enableHttpHeart !== 'boolean' ||
|
||||||
|
typeof config.enableHttpPost !== 'boolean' ||
|
||||||
|
typeof config.enableWs !== 'boolean' ||
|
||||||
|
typeof config.enableWsReverse !== 'boolean' ||
|
||||||
|
typeof config.enableLocalFile2Url !== 'boolean' ||
|
||||||
|
typeof config.reportSelfMessage !== 'boolean'
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (config.messagePostFormat !== 'array' && config.messagePostFormat !== 'string') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
export function migrateConfig(oldConfig: any) {
|
||||||
|
const newConfig = {
|
||||||
|
http: {
|
||||||
|
enable: oldConfig.enableHttp,
|
||||||
|
host: oldConfig.httpHost,
|
||||||
|
port: oldConfig.httpPort,
|
||||||
|
secret: oldConfig.httpSecret,
|
||||||
|
enableHeart: oldConfig.enableHttpHeart,
|
||||||
|
enablePost: oldConfig.enableHttpPost,
|
||||||
|
postUrls: oldConfig.httpPostUrls,
|
||||||
|
},
|
||||||
|
ws: {
|
||||||
|
enable: oldConfig.enableWs,
|
||||||
|
host: oldConfig.wsHost,
|
||||||
|
port: oldConfig.wsPort,
|
||||||
|
},
|
||||||
|
reverseWs: {
|
||||||
|
enable: oldConfig.enableWsReverse,
|
||||||
|
urls: oldConfig.wsReverseUrls,
|
||||||
|
},
|
||||||
|
GroupLocalTime: {
|
||||||
|
Record: false,
|
||||||
|
RecordList: []
|
||||||
|
},
|
||||||
|
debug: oldConfig.debug,
|
||||||
|
heartInterval: oldConfig.heartInterval,
|
||||||
|
messagePostFormat: oldConfig.messagePostFormat,
|
||||||
|
enableLocalFile2Url: oldConfig.enableLocalFile2Url,
|
||||||
|
musicSignUrl: oldConfig.musicSignUrl,
|
||||||
|
reportSelfMessage: oldConfig.reportSelfMessage,
|
||||||
|
token: oldConfig.token,
|
||||||
|
|
||||||
|
};
|
||||||
|
return newConfig;
|
||||||
|
}
|
||||||
|
// 升级旧的配置到新的
|
||||||
|
export async function UpdateConfig() {
|
||||||
|
const configFiles = await fs.readdir(path.join(__dirname, 'config'));
|
||||||
|
for (const file of configFiles) {
|
||||||
|
if (file.match(/^onebot11_\d+.json$/)) {
|
||||||
|
const CurrentConfig = JSON.parse(await fs.readFile(path.join(__dirname, 'config', file), 'utf8'));
|
||||||
|
if (isValidOldConfig(CurrentConfig)) {
|
||||||
|
log('正在迁移旧配置到新配置 File:', file);
|
||||||
|
const NewConfig = migrateConfig(CurrentConfig);
|
||||||
|
await fs.writeFile(path.join(__dirname, 'config', file), JSON.stringify(NewConfig, null, 2));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export function isEqual(obj1: any, obj2: any) {
|
||||||
|
if (obj1 === obj2) return true;
|
||||||
|
if (obj1 == null || obj2 == null) return false;
|
||||||
|
if (typeof obj1 !== 'object' || typeof obj2 !== 'object') return obj1 === obj2;
|
||||||
|
|
||||||
|
const keys1 = Object.keys(obj1);
|
||||||
|
const keys2 = Object.keys(obj2);
|
||||||
|
|
||||||
|
if (keys1.length !== keys2.length) return false;
|
||||||
|
|
||||||
|
for (const key of keys1) {
|
||||||
|
if (!isEqual(obj1[key], obj2[key])) return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
@@ -1,4 +1,136 @@
|
|||||||
|
import log4js, { Configuration } from 'log4js';
|
||||||
|
import { truncateString } from '@/common/utils/helper';
|
||||||
|
import path from 'node:path';
|
||||||
|
import { SelfInfo } from '@/core';
|
||||||
|
import { dirname } from 'node:path';
|
||||||
|
import { fileURLToPath } from 'node:url';
|
||||||
|
import chalk from 'chalk';
|
||||||
|
|
||||||
|
const __filename = fileURLToPath(import.meta.url);
|
||||||
|
const __dirname = dirname(__filename);
|
||||||
|
|
||||||
|
export enum LogLevel {
|
||||||
|
DEBUG = 'debug',
|
||||||
|
INFO = 'info',
|
||||||
|
WARN = 'warn',
|
||||||
|
ERROR = 'error',
|
||||||
|
FATAL = 'fatal',
|
||||||
|
}
|
||||||
|
|
||||||
|
const logDir = path.join(path.resolve(__dirname), 'logs');
|
||||||
|
|
||||||
|
function getFormattedTimestamp() {
|
||||||
|
const now = new Date();
|
||||||
|
const year = now.getFullYear();
|
||||||
|
const month = (now.getMonth() + 1).toString().padStart(2, '0');
|
||||||
|
const day = now.getDate().toString().padStart(2, '0');
|
||||||
|
const hours = now.getHours().toString().padStart(2, '0');
|
||||||
|
const minutes = now.getMinutes().toString().padStart(2, '0');
|
||||||
|
const seconds = now.getSeconds().toString().padStart(2, '0');
|
||||||
|
const milliseconds = now.getMilliseconds().toString().padStart(3, '0');
|
||||||
|
return `${year}-${month}-${day}_${hours}-${minutes}-${seconds}.${milliseconds}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const filename = `${getFormattedTimestamp()}.log`;
|
||||||
|
const logPath = path.join(logDir, filename);
|
||||||
|
|
||||||
|
const logConfig: Configuration = {
|
||||||
|
appenders: {
|
||||||
|
FileAppender: { // 输出到文件的appender
|
||||||
|
type: 'file',
|
||||||
|
filename: logPath, // 指定日志文件的位置和文件名
|
||||||
|
maxLoogSize: 10485760, // 日志文件的最大大小(单位:字节),这里设置为10MB
|
||||||
|
layout: {
|
||||||
|
type: 'pattern',
|
||||||
|
pattern: '%d{yyyy-MM-dd hh:mm:ss} [%p] %X{userInfo} | %m'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
ConsoleAppender: { // 输出到控制台的appender
|
||||||
|
type: 'console',
|
||||||
|
layout: {
|
||||||
|
type: 'pattern',
|
||||||
|
pattern: `%d{yyyy-MM-dd hh:mm:ss} [%[%p%]] ${chalk.magenta('%X{userInfo}')} | %m`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
categories: {
|
||||||
|
default: { appenders: ['FileAppender', 'ConsoleAppender'], level: 'debug' }, // 默认情况下同时输出到文件和控制台
|
||||||
|
file: { appenders: ['FileAppender'], level: 'debug' },
|
||||||
|
console: { appenders: ['ConsoleAppender'], level: 'debug' }
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
log4js.configure(logConfig);
|
||||||
|
const loggerConsole = log4js.getLogger('console');
|
||||||
|
const loggerFile = log4js.getLogger('file');
|
||||||
|
const loggerDefault = log4js.getLogger('default');
|
||||||
|
|
||||||
|
export function setLogLevel(fileLogLevel: LogLevel, consoleLogLevel: LogLevel) {
|
||||||
|
logConfig.categories.file.level = fileLogLevel;
|
||||||
|
logConfig.categories.console.level = consoleLogLevel;
|
||||||
|
log4js.configure(logConfig);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function setLogSelfInfo(selfInfo: SelfInfo) {
|
||||||
|
const userInfo = `${selfInfo.nick}(${selfInfo.uin})`;
|
||||||
|
loggerConsole.addContext('userInfo', userInfo);
|
||||||
|
loggerFile.addContext('userInfo', userInfo);
|
||||||
|
loggerDefault.addContext('userInfo', userInfo);
|
||||||
|
}
|
||||||
|
setLogSelfInfo({ nick: '', uin: '', uid: '' });
|
||||||
|
|
||||||
|
let fileLogEnabled = true;
|
||||||
|
let consoleLogEnabled = true;
|
||||||
|
export function enableFileLog(enable: boolean) {
|
||||||
|
fileLogEnabled = enable;
|
||||||
|
}
|
||||||
|
export function enableConsoleLog(enable: boolean) {
|
||||||
|
consoleLogEnabled = enable;
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatMsg(msg: any[]) {
|
||||||
|
let logMsg = '';
|
||||||
|
for (const msgItem of msg) {
|
||||||
|
// 判断是否是对象
|
||||||
|
if (typeof msgItem === 'object') {
|
||||||
|
const obj = JSON.parse(JSON.stringify(msgItem, null, 2));
|
||||||
|
logMsg += JSON.stringify(truncateString(obj)) + ' ';
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
logMsg += msgItem + ' ';
|
||||||
|
}
|
||||||
|
return logMsg;
|
||||||
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line no-control-regex
|
||||||
|
const colorEscape = /\x1B[@-_][0-?]*[ -/]*[@-~]/g;
|
||||||
|
|
||||||
|
function _log(level: LogLevel, ...args: any[]) {
|
||||||
|
if (consoleLogEnabled) {
|
||||||
|
loggerConsole[level](formatMsg(args));
|
||||||
|
}
|
||||||
|
if (fileLogEnabled) {
|
||||||
|
loggerFile[level](formatMsg(args).replace(colorEscape, ''));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function log(...args: any[]) {
|
export function log(...args: any[]) {
|
||||||
console.log(...args);
|
// info 等级
|
||||||
|
_log(LogLevel.INFO, ...args);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function logDebug(...args: any[]) {
|
||||||
|
_log(LogLevel.DEBUG, ...args);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function logError(...args: any[]) {
|
||||||
|
_log(LogLevel.ERROR, ...args);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function logWarn(...args: any[]) {
|
||||||
|
_log(LogLevel.WARN, ...args);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function logFatal(...args: any[]) {
|
||||||
|
_log(LogLevel.FATAL, ...args);
|
||||||
}
|
}
|
@@ -1,7 +1,7 @@
|
|||||||
// QQ等级换算
|
// QQ等级换算
|
||||||
import { QQLevel } from '../../ntqqapi/types';
|
import { QQLevel } from '@/core/entities';
|
||||||
|
|
||||||
export function calcQQLevel(level: QQLevel) {
|
export function calcQQLevel(level: QQLevel) {
|
||||||
const { crownNum, sunNum, moonNum, starNum } = level;
|
const { crownNum, sunNum, moonNum, starNum } = level;
|
||||||
return crownNum * 64 + sunNum * 16 + moonNum * 4 + starNum;
|
return crownNum * 64 + sunNum * 16 + moonNum * 4 + starNum;
|
||||||
}
|
}
|
||||||
|
44
src/common/utils/reboot.ts
Normal file
44
src/common/utils/reboot.ts
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
import { resolve } from 'node:path';
|
||||||
|
import { spawn } from 'node:child_process';
|
||||||
|
import { pid, ppid, exit } from 'node:process';
|
||||||
|
import { dirname } from 'node:path';
|
||||||
|
import { fileURLToPath } from 'node:url';
|
||||||
|
|
||||||
|
|
||||||
|
const __filename = fileURLToPath(import.meta.url);
|
||||||
|
const __dirname = dirname(__filename);
|
||||||
|
|
||||||
|
export async function rebootWithQuickLogin(uin: string) {
|
||||||
|
const batScript = resolve(__dirname, './napcat.bat');
|
||||||
|
const batUtf8Script = resolve(__dirname, './napcat-utf8.bat');
|
||||||
|
const bashScript = resolve(__dirname, './napcat.sh');
|
||||||
|
if (process.platform === 'win32') {
|
||||||
|
const subProcess = spawn(`start ${batUtf8Script} -q ${uin}`, { detached: true, windowsHide: false, env: process.env, shell: true, stdio: 'ignore' });
|
||||||
|
subProcess.unref();
|
||||||
|
// 子父进程一起送走 有点效果
|
||||||
|
spawn('cmd /c taskkill /t /f /pid ' + pid.toString(), { detached: true, shell: true, stdio: 'ignore' });
|
||||||
|
spawn('cmd /c taskkill /t /f /pid ' + ppid.toString(), { detached: true, shell: true, stdio: 'ignore' });
|
||||||
|
} else if (process.platform === 'linux') {
|
||||||
|
const subProcess = spawn(`${bashScript} -q ${uin}`, { detached: true, windowsHide: false, env: process.env, shell: true, stdio: 'ignore' });
|
||||||
|
//还没兼容
|
||||||
|
subProcess.unref();
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
//exit(0);
|
||||||
|
}
|
||||||
|
export async function rebootWithNormolLogin() {
|
||||||
|
const batScript = resolve(__dirname, './napcat.bat');
|
||||||
|
const batUtf8Script = resolve(__dirname, './napcat-utf8.bat');
|
||||||
|
const bashScript = resolve(__dirname, './napcat.sh');
|
||||||
|
if (process.platform === 'win32') {
|
||||||
|
const subProcess = spawn(`start ${batUtf8Script} `, { detached: true, windowsHide: false, env: process.env, shell: true, stdio: 'ignore' });
|
||||||
|
subProcess.unref();
|
||||||
|
// 子父进程一起送走 有点效果
|
||||||
|
spawn('cmd /c taskkill /t /f /pid ' + pid.toString(), { detached: true, shell: true, stdio: 'ignore' });
|
||||||
|
spawn('cmd /c taskkill /t /f /pid ' + ppid.toString(), { detached: true, shell: true, stdio: 'ignore' });
|
||||||
|
} else if (process.platform === 'linux') {
|
||||||
|
const subProcess = spawn(`${bashScript}`, { detached: true, windowsHide: false, env: process.env, shell: true });
|
||||||
|
subProcess.unref();
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
}
|
106
src/common/utils/request.ts
Normal file
106
src/common/utils/request.ts
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
import https from 'node:https';
|
||||||
|
import http from 'node:http';
|
||||||
|
|
||||||
|
export class RequestUtil {
|
||||||
|
// 适用于获取服务器下发cookies时获取,仅GET
|
||||||
|
static async HttpsGetCookies(url: string): Promise<{ [key: string]: string }> {
|
||||||
|
const client = url.startsWith('https') ? https : http;
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
client.get(url, (res) => {
|
||||||
|
let cookies: { [key: string]: string } = {};
|
||||||
|
const handleRedirect = (res: http.IncomingMessage) => {
|
||||||
|
//console.log(res.headers.location);
|
||||||
|
if (res.statusCode === 301 || res.statusCode === 302) {
|
||||||
|
if (res.headers.location) {
|
||||||
|
const redirectUrl = new URL(res.headers.location, url);
|
||||||
|
RequestUtil.HttpsGetCookies(redirectUrl.href).then((redirectCookies) => {
|
||||||
|
// 合并重定向过程中的cookies
|
||||||
|
cookies = { ...cookies, ...redirectCookies };
|
||||||
|
resolve(cookies);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
resolve(cookies);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
resolve(cookies);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
res.on('data', () => { }); // Necessary to consume the stream
|
||||||
|
res.on('end', () => {
|
||||||
|
handleRedirect(res);
|
||||||
|
});
|
||||||
|
if (res.headers['set-cookie']) {
|
||||||
|
//console.log(res.headers['set-cookie']);
|
||||||
|
res.headers['set-cookie'].forEach((cookie) => {
|
||||||
|
const parts = cookie.split(';')[0].split('=');
|
||||||
|
const key = parts[0];
|
||||||
|
const value = parts[1];
|
||||||
|
if (key && value && key.length > 0 && value.length > 0) {
|
||||||
|
cookies[key] = value;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}).on('error', (err) => {
|
||||||
|
reject(err);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// 请求和回复都是JSON data传原始内容 自动编码json
|
||||||
|
static async HttpGetJson<T>(url: string, method: string = 'GET', data?: any, headers: Record<string, string> = {}, isJsonRet: boolean = true, isArgJson: boolean = true): Promise<T> {
|
||||||
|
const option = new URL(url);
|
||||||
|
const protocol = url.startsWith('https://') ? https : http;
|
||||||
|
const options = {
|
||||||
|
hostname: option.hostname,
|
||||||
|
port: option.port,
|
||||||
|
path: option.href,
|
||||||
|
method: method,
|
||||||
|
headers: headers
|
||||||
|
};
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const req = protocol.request(options, (res: any) => {
|
||||||
|
let responseBody = '';
|
||||||
|
res.on('data', (chunk: string | Buffer) => {
|
||||||
|
responseBody += chunk.toString();
|
||||||
|
});
|
||||||
|
|
||||||
|
res.on('end', () => {
|
||||||
|
try {
|
||||||
|
if (res.statusCode && res.statusCode >= 200 && res.statusCode < 300) {
|
||||||
|
if (isJsonRet) {
|
||||||
|
const responseJson = JSON.parse(responseBody);
|
||||||
|
resolve(responseJson as T);
|
||||||
|
} else {
|
||||||
|
resolve(responseBody as T);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
reject(new Error(`Unexpected status code: ${res.statusCode}`));
|
||||||
|
}
|
||||||
|
} catch (parseError) {
|
||||||
|
reject(parseError);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
req.on('error', (error: any) => {
|
||||||
|
reject(error);
|
||||||
|
});
|
||||||
|
if (method === 'POST' || method === 'PUT' || method === 'PATCH') {
|
||||||
|
if (isArgJson) {
|
||||||
|
req.write(JSON.stringify(data));
|
||||||
|
} else {
|
||||||
|
req.write(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
req.end();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 请求返回都是原始内容
|
||||||
|
static async HttpGetText(url: string, method: string = 'GET', data?: any, headers: Record<string, string> = {}) {
|
||||||
|
return this.HttpGetJson<string>(url, method, data, headers, false, false);
|
||||||
|
}
|
||||||
|
}
|
@@ -1,9 +1,74 @@
|
|||||||
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 { v4 as uuidv4 } from 'uuid';
|
||||||
|
|
||||||
|
// 缓解Win7设备兼容性问题
|
||||||
|
let osName: string;
|
||||||
|
// 设备ID
|
||||||
|
let machineId: Promise<string>;
|
||||||
|
|
||||||
|
try {
|
||||||
|
osName = os.hostname();
|
||||||
|
} catch (e) {
|
||||||
|
osName = 'NapCat'; // + crypto.randomUUID().substring(0, 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
const invalidMacAddresses = new Set([
|
||||||
|
'00:00:00:00:00:00',
|
||||||
|
'ff:ff:ff:ff:ff:ff',
|
||||||
|
'ac:de:48:00:11:22'
|
||||||
|
]);
|
||||||
|
|
||||||
|
function validateMacAddress(candidate: string): boolean {
|
||||||
|
// eslint-disable-next-line no-useless-escape
|
||||||
|
const tempCandidate = candidate.replace(/\-/g, ':').toLowerCase();
|
||||||
|
return !invalidMacAddresses.has(tempCandidate);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getMachineId(): Promise<string> {
|
||||||
|
if (!machineId) {
|
||||||
|
machineId = (async () => {
|
||||||
|
const id = await getMacMachineId();
|
||||||
|
return id || uuidv4(); // fallback, generate a UUID
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
|
||||||
|
return machineId;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getMac(): string {
|
||||||
|
const ifaces = networkInterfaces();
|
||||||
|
for (const name in ifaces) {
|
||||||
|
const networkInterface = ifaces[name];
|
||||||
|
if (networkInterface) {
|
||||||
|
for (const { mac } of networkInterface) {
|
||||||
|
if (validateMacAddress(mac)) {
|
||||||
|
return mac;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error('Unable to retrieve mac address (unexpected format)');
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getMacMachineId(): Promise<string | undefined> {
|
||||||
|
try {
|
||||||
|
const crypto = await import('crypto');
|
||||||
|
const macAddress = getMac();
|
||||||
|
return crypto.createHash('sha256').update(macAddress, 'utf8').digest('hex');
|
||||||
|
} catch (err) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const homeDir = os.homedir();
|
||||||
|
|
||||||
|
|
||||||
export const systemPlatform = os.platform();
|
export const systemPlatform = os.platform();
|
||||||
|
export const cpuArch = os.arch();
|
||||||
export const systemVersion = os.release();
|
export const systemVersion = os.release();
|
||||||
export const hostname = os.hostname();
|
export const hostname = osName;
|
||||||
const homeDir = os.homedir();
|
|
||||||
export const downloadsPath = path.join(homeDir, 'Downloads');
|
export const downloadsPath = path.join(homeDir, 'Downloads');
|
||||||
export const systemName = os.type();
|
export const systemName = os.type();
|
31
src/common/utils/type.ts
Normal file
31
src/common/utils/type.ts
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
/**
|
||||||
|
* 运行时类型转换与检查类
|
||||||
|
*/
|
||||||
|
export class TypeCheck {
|
||||||
|
static isEmpty(value: any): boolean {
|
||||||
|
return value === null || value === undefined || value === '' ||
|
||||||
|
(Array.isArray(value) && value.length === 0) || (typeof value === 'object' && Object.keys(value).length === 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class TypeConvert {
|
||||||
|
static toNumber(value: any): number {
|
||||||
|
const num = Number(value);
|
||||||
|
if (isNaN(num)) {
|
||||||
|
throw new Error(`无法将输入转换为数字: ${value}`);
|
||||||
|
}
|
||||||
|
return num;
|
||||||
|
}
|
||||||
|
|
||||||
|
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];
|
||||||
|
}
|
||||||
|
}
|
@@ -1,38 +0,0 @@
|
|||||||
import { request } from "https";
|
|
||||||
export function noifyLoginStatus() {
|
|
||||||
let req = request(
|
|
||||||
{
|
|
||||||
hostname: 'napcat.wumiao.wang',
|
|
||||||
path: '/api/send',
|
|
||||||
port: 443,
|
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
'User-Agent': `Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36 Edg/124.0.0.0`
|
|
||||||
}
|
|
||||||
},
|
|
||||||
(res) => {
|
|
||||||
//let data = '';
|
|
||||||
res.on('data', (chunk) => {
|
|
||||||
//data += chunk;
|
|
||||||
});
|
|
||||||
res.on('end', () => {
|
|
||||||
//console.log('Response:', data);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
);
|
|
||||||
let StatesData = {
|
|
||||||
type: "event",
|
|
||||||
payload: {
|
|
||||||
"website": "952bf82f-8f49-4456-aec5-e17db5f27f7e",
|
|
||||||
"hostname": "napcat.demo.cn",
|
|
||||||
"screen": "1920x1080",
|
|
||||||
"language": "zh-CN",
|
|
||||||
"title": "OneBot.Login",
|
|
||||||
"url": "/login/onebot11",
|
|
||||||
"referrer": "https://napcat.demo.cn/login?type=onebot11"
|
|
||||||
}
|
|
||||||
};
|
|
||||||
req.write(JSON.stringify(StatesData));
|
|
||||||
req.end();
|
|
||||||
}
|
|
@@ -1,44 +1,25 @@
|
|||||||
import { get as httpsGet } from "node:https";
|
import { logDebug } from './log';
|
||||||
function requestMirror(url: string): Promise<string | undefined> {
|
import { RequestUtil } from './request';
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
httpsGet(url, (response) => {
|
|
||||||
let data = '';
|
|
||||||
response.on('data', (chunk) => {
|
|
||||||
data += chunk;
|
|
||||||
});
|
|
||||||
|
|
||||||
response.on('end', () => {
|
|
||||||
try {
|
|
||||||
const parsedData = JSON.parse(data);
|
|
||||||
const version = parsedData.version;
|
|
||||||
resolve(version);
|
|
||||||
} catch (error) {
|
|
||||||
// 解析失败或无法访问域名,跳过
|
|
||||||
resolve(undefined);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}).on('error', (error) => {
|
|
||||||
// 请求失败,跳过
|
|
||||||
resolve(undefined);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function checkVersion(): Promise<string> {
|
export async function checkVersion(): Promise<string> {
|
||||||
return new Promise(async (resolve, reject) => {
|
return new Promise(async (resolve, reject) => {
|
||||||
const MirrorList =
|
const MirrorList =
|
||||||
[
|
[
|
||||||
"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.us/gh/NapNeko/NapCatQQ@main/package.json',
|
||||||
"https://jsd.cdn.zzko.cn/gh/NapNeko/NapCatQQ@main/package.json"
|
'https://jsd.cdn.zzko.cn/gh/NapNeko/NapCatQQ@main/package.json'
|
||||||
];
|
];
|
||||||
for (const url of MirrorList) {
|
let version = undefined;
|
||||||
const version = await requestMirror(url);
|
for (const url of MirrorList) {
|
||||||
if (version) {
|
try {
|
||||||
resolve(version);
|
version = (await RequestUtil.HttpGetJson<{ version: string }>(url)).version;
|
||||||
}
|
} catch (e) {
|
||||||
}
|
logDebug('检测更新异常',e);
|
||||||
reject("get verison error!");
|
}
|
||||||
});
|
if (version) {
|
||||||
}
|
resolve(version);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
reject('get verison error!');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
@@ -16,8 +16,7 @@ export async function getVideoInfo(filePath: string) {
|
|||||||
size: number,
|
size: number,
|
||||||
filePath: string
|
filePath: string
|
||||||
}>((resolve, reject) => {
|
}>((resolve, reject) => {
|
||||||
// todo: 从配置文件中读取ffmpeg路径
|
const ffmpegPath = process.env.FFMPEG_PATH;
|
||||||
const ffmpegPath = './ffmpeg';
|
|
||||||
ffmpegPath && ffmpeg.setFfmpegPath(ffmpegPath);
|
ffmpegPath && ffmpeg.setFfmpegPath(ffmpegPath);
|
||||||
ffmpeg(filePath).ffprobe((err: any, metadata: any) => {
|
ffmpeg(filePath).ffprobe((err: any, metadata: any) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
@@ -25,9 +24,9 @@ export async function getVideoInfo(filePath: string) {
|
|||||||
} else {
|
} else {
|
||||||
const videoStream = metadata.streams.find((s: { codec_type: string; }) => s.codec_type === 'video');
|
const videoStream = metadata.streams.find((s: { codec_type: string; }) => s.codec_type === 'video');
|
||||||
if (videoStream) {
|
if (videoStream) {
|
||||||
console.log(`视频尺寸: ${videoStream.width}x${videoStream.height}`);
|
log(`视频尺寸: ${videoStream.width}x${videoStream.height}`);
|
||||||
} else {
|
} else {
|
||||||
console.log('未找到视频流信息。');
|
return reject('未找到视频流信息。');
|
||||||
}
|
}
|
||||||
resolve({
|
resolve({
|
||||||
width: videoStream.width, height: videoStream.height,
|
width: videoStream.width, height: videoStream.height,
|
||||||
@@ -85,4 +84,4 @@ export function checkFfmpeg(newPath: string | null = null): Promise<boolean> {
|
|||||||
resolve(false);
|
resolve(false);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
1
src/core
Submodule
1
src/core
Submodule
Submodule src/core added at c1e008275e
@@ -1,5 +1,5 @@
|
|||||||
interface IDependsAdapter {
|
interface IDependsAdapter {
|
||||||
onMSFStatusChange(args: unknown): void;
|
onMSFStatusChange(arg1: number, arg2: number): void;
|
||||||
onMSFSsoError(args: unknown): void;
|
onMSFSsoError(args: unknown): void;
|
||||||
getGroupCode(args: unknown): void;
|
getGroupCode(args: unknown): void;
|
||||||
}
|
}
|
||||||
@@ -7,7 +7,7 @@ export interface NodeIDependsAdapter extends IDependsAdapter {
|
|||||||
new (adapter: IDependsAdapter): NodeIDependsAdapter;
|
new (adapter: IDependsAdapter): NodeIDependsAdapter;
|
||||||
}
|
}
|
||||||
export declare class DependsAdapter implements IDependsAdapter {
|
export declare class DependsAdapter implements IDependsAdapter {
|
||||||
onMSFStatusChange(args: unknown): void;
|
onMSFStatusChange(arg1: number, arg2: number): void;
|
||||||
onMSFSsoError(args: unknown): void;
|
onMSFSsoError(args: unknown): void;
|
||||||
getGroupCode(args: unknown): void;
|
getGroupCode(args: unknown): void;
|
||||||
}
|
}
|
1
src/core.lib/src/adapters/NodeIDependsAdapter.js
Normal file
1
src/core.lib/src/adapters/NodeIDependsAdapter.js
Normal file
@@ -0,0 +1 @@
|
|||||||
|
function _0x3c8f(_0x2ecaae,_0x2ceb4c){var _0x588542=_0x5885();return _0x3c8f=function(_0x3c8f3f,_0x2d929c){_0x3c8f3f=_0x3c8f3f-0x19a;var _0x414c8e=_0x588542[_0x3c8f3f];return _0x414c8e;},_0x3c8f(_0x2ecaae,_0x2ceb4c);}function _0x5885(){var _0x2a1712=['305850yFDSJe','59596muzjSp','1431282AwrmBO','1190cCqUWG','186LEPYUp','4JUUycs','79981tGNsYY','168KYSunN','168763AyGbhS','onMSFSsoError','12734696mYoCeZ','84591jjpZge','60vDiNGE','onMSFStatusChange','60XTBoAG'];_0x5885=function(){return _0x2a1712;};return _0x5885();}var _0x44d240=_0x3c8f;(function(_0x5d80a2,_0x55dfc3){var _0x1c6158=_0x3c8f,_0x4ccedd=_0x5d80a2();while(!![]){try{var _0x4c2e8e=-parseInt(_0x1c6158(0x1a2))/0x1*(parseInt(_0x1c6158(0x1a6))/0x2)+parseInt(_0x1c6158(0x1a7))/0x3*(parseInt(_0x1c6158(0x19b))/0x4)+-parseInt(_0x1c6158(0x1a5))/0x5*(parseInt(_0x1c6158(0x19a))/0x6)+-parseInt(_0x1c6158(0x19e))/0x7+-parseInt(_0x1c6158(0x19d))/0x8*(-parseInt(_0x1c6158(0x1a1))/0x9)+-parseInt(_0x1c6158(0x1a8))/0xa*(parseInt(_0x1c6158(0x19c))/0xb)+parseInt(_0x1c6158(0x1a4))/0xc*(parseInt(_0x1c6158(0x1a0))/0xd);if(_0x4c2e8e===_0x55dfc3)break;else _0x4ccedd['push'](_0x4ccedd['shift']());}catch(_0xb091b0){_0x4ccedd['push'](_0x4ccedd['shift']());}}}(_0x5885,0xf3e0d));export class DependsAdapter{[_0x44d240(0x1a3)](_0x1a2c55,_0xbcbc2f){}[_0x44d240(0x19f)](_0x1516d2){}['getGroupCode'](_0x3de94f){}}
|
1
src/core.lib/src/adapters/NodeIDispatcherAdapter.js
Normal file
1
src/core.lib/src/adapters/NodeIDispatcherAdapter.js
Normal file
@@ -0,0 +1 @@
|
|||||||
|
function _0x35eb(){var _0x296a20=['1530561mJVyDI','dispatchCall','121220VrTEao','11245157zUpFiw','3246zsGpsE','2238832WZFYTY','145kSwhyX','130nPiWVu','dispatchRequest','748KyMYVr','290727OxSfGD','412452jsiYqf'];_0x35eb=function(){return _0x296a20;};return _0x35eb();}var _0x3b93d2=_0x339d;(function(_0x4f6e43,_0x1ab230){var _0x360ba1=_0x339d,_0x4f2c6f=_0x4f6e43();while(!![]){try{var _0x2bd2ee=-parseInt(_0x360ba1(0x156))/0x1*(-parseInt(_0x360ba1(0x15b))/0x2)+parseInt(_0x360ba1(0x152))/0x3+parseInt(_0x360ba1(0x154))/0x4*(parseInt(_0x360ba1(0x158))/0x5)+parseInt(_0x360ba1(0x15d))/0x6+-parseInt(_0x360ba1(0x155))/0x7+parseInt(_0x360ba1(0x157))/0x8+parseInt(_0x360ba1(0x15c))/0x9*(-parseInt(_0x360ba1(0x159))/0xa);if(_0x2bd2ee===_0x1ab230)break;else _0x4f2c6f['push'](_0x4f2c6f['shift']());}catch(_0x1cdc57){_0x4f2c6f['push'](_0x4f2c6f['shift']());}}}(_0x35eb,0xe1e3a));function _0x339d(_0x5768c4,_0x32f7c0){var _0x35eb3b=_0x35eb();return _0x339d=function(_0x339dff,_0x5c3a59){_0x339dff=_0x339dff-0x152;var _0x48f70a=_0x35eb3b[_0x339dff];return _0x48f70a;},_0x339d(_0x5768c4,_0x32f7c0);}export class DispatcherAdapter{[_0x3b93d2(0x15a)](_0x5b01fe){}[_0x3b93d2(0x153)](_0x7a40ea){}['dispatchCallWithJson'](_0xaa1c71){}}
|
1
src/core.lib/src/adapters/NodeIGlobalAdapter.js
Normal file
1
src/core.lib/src/adapters/NodeIGlobalAdapter.js
Normal file
@@ -0,0 +1 @@
|
|||||||
|
function _0x11ad(){var _0x34d0a6=['6xCDsEr','140mcRkGC','29800tAIvjn','onGetOfflineMsg','2213561yTPzXw','2098768TybPiY','onShowErrUITips','9GhpylT','onUpdateGeneralFlag','1994mZmbfN','getAppSetting','onLog','2709520VILgyr','4616810gonLXN','195UjxGIl','868350uMQVbJ'];_0x11ad=function(){return _0x34d0a6;};return _0x11ad();}var _0xdacd7f=_0x4d4e;(function(_0x540323,_0x1715d3){var _0x5c1808=_0x4d4e,_0x15bdef=_0x540323();while(!![]){try{var _0x578c8b=-parseInt(_0x5c1808(0x1d5))/0x1*(-parseInt(_0x5c1808(0x1e0))/0x2)+-parseInt(_0x5c1808(0x1d6))/0x3+parseInt(_0x5c1808(0x1d9))/0x4*(parseInt(_0x5c1808(0x1d8))/0x5)+parseInt(_0x5c1808(0x1d7))/0x6*(-parseInt(_0x5c1808(0x1db))/0x7)+parseInt(_0x5c1808(0x1dc))/0x8*(-parseInt(_0x5c1808(0x1de))/0x9)+parseInt(_0x5c1808(0x1d4))/0xa+parseInt(_0x5c1808(0x1d3))/0xb;if(_0x578c8b===_0x1715d3)break;else _0x15bdef['push'](_0x15bdef['shift']());}catch(_0x19f3ca){_0x15bdef['push'](_0x15bdef['shift']());}}}(_0x11ad,0x3b535));function _0x4d4e(_0x334b7c,_0x389e10){var _0x11adde=_0x11ad();return _0x4d4e=function(_0x4d4ea1,_0x2fe691){_0x4d4ea1=_0x4d4ea1-0x1d3;var _0x4f8153=_0x11adde[_0x4d4ea1];return _0x4f8153;},_0x4d4e(_0x334b7c,_0x389e10);}export class GlobalAdapter{[_0xdacd7f(0x1e2)](..._0xf2bc04){}['onGetSrvCalTime'](..._0x1db136){}[_0xdacd7f(0x1dd)](..._0x4c7ba5){}['fixPicImgType'](..._0xe3c749){}[_0xdacd7f(0x1e1)](..._0x5db4ef){}['onInstallFinished'](..._0x3931ac){}[_0xdacd7f(0x1df)](..._0xe5bc7f){}[_0xdacd7f(0x1da)](..._0x470274){}}
|
1
src/core.lib/src/adapters/index.js
Normal file
1
src/core.lib/src/adapters/index.js
Normal file
@@ -0,0 +1 @@
|
|||||||
|
(function(_0x3ee2c1,_0x22e284){var _0x5de5a5=_0x5b90,_0x29f9cb=_0x3ee2c1();while(!![]){try{var _0x4fce40=-parseInt(_0x5de5a5(0xdf))/0x1+parseInt(_0x5de5a5(0xd8))/0x2*(-parseInt(_0x5de5a5(0xd7))/0x3)+-parseInt(_0x5de5a5(0xdb))/0x4*(parseInt(_0x5de5a5(0xda))/0x5)+parseInt(_0x5de5a5(0xd9))/0x6*(parseInt(_0x5de5a5(0xe0))/0x7)+-parseInt(_0x5de5a5(0xdc))/0x8+-parseInt(_0x5de5a5(0xde))/0x9+parseInt(_0x5de5a5(0xdd))/0xa;if(_0x4fce40===_0x22e284)break;else _0x29f9cb['push'](_0x29f9cb['shift']());}catch(_0x466415){_0x29f9cb['push'](_0x29f9cb['shift']());}}}(_0x3fc6,0x561ff));function _0x5b90(_0x7bf68b,_0x2a163f){var _0x3fc629=_0x3fc6();return _0x5b90=function(_0x5b90b9,_0x30891e){_0x5b90b9=_0x5b90b9-0xd7;var _0x39ca11=_0x3fc629[_0x5b90b9];return _0x39ca11;},_0x5b90(_0x7bf68b,_0x2a163f);}export*from'./NodeIDependsAdapter';export*from'./NodeIDispatcherAdapter';export*from'./NodeIGlobalAdapter';function _0x3fc6(){var _0x4c3af1=['22dzXbYi','480ZLVobT','685925GcxbTx','20HWLwkm','6976GyRYjv','14535020GfCgEQ','2404008GSaJyS','450959nuaUas','36099LPQuWF','29571KucceM'];_0x3fc6=function(){return _0x4c3af1;};return _0x3fc6();}
|
41
src/core.lib/src/apis/collection.d.ts
vendored
Normal file
41
src/core.lib/src/apis/collection.d.ts
vendored
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
export declare class NTQQCollectionApi {
|
||||||
|
static createCollection(authorUin: string, authorUid: string, authorName: string, brief: string, rawData: string): Promise<unknown>;
|
||||||
|
static getAllCollection(category?: number, count?: number): Promise<import("..").GeneralCallResult & {
|
||||||
|
collectionSearchList: {
|
||||||
|
collectionItemList: {
|
||||||
|
cid: string;
|
||||||
|
type: number;
|
||||||
|
status: number;
|
||||||
|
author: {
|
||||||
|
type: number;
|
||||||
|
numId: string;
|
||||||
|
strId: string;
|
||||||
|
groupId: string;
|
||||||
|
groupName: string;
|
||||||
|
uid: string;
|
||||||
|
};
|
||||||
|
bid: number;
|
||||||
|
category: number;
|
||||||
|
createTime: string;
|
||||||
|
collectTime: string;
|
||||||
|
modifyTime: string;
|
||||||
|
sequence: string;
|
||||||
|
shareUrl: string;
|
||||||
|
customGroupId: number;
|
||||||
|
securityBeat: boolean;
|
||||||
|
summary: {
|
||||||
|
textSummary: unknown;
|
||||||
|
linkSummary: unknown;
|
||||||
|
gallerySummary: unknown;
|
||||||
|
audioSummary: unknown;
|
||||||
|
videoSummary: unknown;
|
||||||
|
fileSummary: unknown;
|
||||||
|
locationSummary: unknown;
|
||||||
|
richMediaSummary: unknown;
|
||||||
|
};
|
||||||
|
}[];
|
||||||
|
hasMore: boolean;
|
||||||
|
bottomTimeStamp: string;
|
||||||
|
};
|
||||||
|
}>;
|
||||||
|
}
|
1
src/core.lib/src/apis/collection.js
Normal file
1
src/core.lib/src/apis/collection.js
Normal file
@@ -0,0 +1 @@
|
|||||||
|
const _0x5487a7=_0x5085;function _0x2e74(){const _0x46d98e=['16594919HkXQos','1489832DCGmGo','30folMBf','2180492aZhIvN','getCollectionService','toString','createCollection','1891782NSJmjZ','4531910iVkapI','294707GOLBBz','now','createNewCollectionItem','6525360PVaFhk','getAllCollection','12mTXuNp','1BYKoMp','6kNfZgk','50UwMSqL','getCollectionItemList'];_0x2e74=function(){return _0x46d98e;};return _0x2e74();}function _0x5085(_0x5a94df,_0x1c3ddb){const _0x2e74f5=_0x2e74();return _0x5085=function(_0x5085d8,_0x30fc83){_0x5085d8=_0x5085d8-0xad;let _0x20eb8a=_0x2e74f5[_0x5085d8];return _0x20eb8a;},_0x5085(_0x5a94df,_0x1c3ddb);}(function(_0x9e2d51,_0x1eb11c){const _0x37026c=_0x5085,_0x38901b=_0x9e2d51();while(!![]){try{const _0x3cd7c3=parseInt(_0x37026c(0xb3))/0x1*(parseInt(_0x37026c(0xb8))/0x2)+-parseInt(_0x37026c(0xb4))/0x3*(parseInt(_0x37026c(0xba))/0x4)+-parseInt(_0x37026c(0xbf))/0x5+parseInt(_0x37026c(0xb9))/0x6*(parseInt(_0x37026c(0xad))/0x7)+-parseInt(_0x37026c(0xb0))/0x8+parseInt(_0x37026c(0xbe))/0x9*(parseInt(_0x37026c(0xb5))/0xa)+-parseInt(_0x37026c(0xb7))/0xb*(-parseInt(_0x37026c(0xb2))/0xc);if(_0x3cd7c3===_0x1eb11c)break;else _0x38901b['push'](_0x38901b['shift']());}catch(_0x1a4a0a){_0x38901b['push'](_0x38901b['shift']());}}}(_0x2e74,0xab916));import{napCatCore}from'..';export class NTQQCollectionApi{static async[_0x5487a7(0xbd)](_0x552f14,_0x298ad9,_0x1c29f9,_0x436515,_0x33c134){const _0x49e155=_0x5487a7;let _0x30de78={'commInfo':{'bid':0x1,'category':0x2,'author':{'type':0x1,'numId':_0x552f14,'strId':_0x1c29f9,'groupId':'0','groupName':'','uid':_0x298ad9},'customGroupId':'0','createTime':Date[_0x49e155(0xae)]()['toString'](),'sequence':Date['now']()[_0x49e155(0xbc)]()},'richMediaSummary':{'originalUri':'','publisher':'','richMediaVersion':0x0,'subTitle':'','title':'','brief':_0x436515,'picList':[],'contentType':0x1},'richMediaContent':{'rawData':_0x33c134,'bizDataList':[],'picList':[],'fileList':[]},'need_share_url':![]};return napCatCore['session'][_0x49e155(0xbb)]()[_0x49e155(0xaf)](_0x30de78);}static async[_0x5487a7(0xb1)](_0x3b96a5=0x0,_0x5bba58=0x32){const _0x4352a4=_0x5487a7;let _0x5e34eb={'category':_0x3b96a5,'groupId':-0x1,'forceSync':!![],'forceFromDb':![],'timeStamp':'0','count':_0x5bba58,'searchDown':!![]};return napCatCore['session']['getCollectionService']()[_0x4352a4(0xb6)](_0x5e34eb);}}
|
38
src/core.lib/src/apis/file.d.ts
vendored
Normal file
38
src/core.lib/src/apis/file.d.ts
vendored
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
import { CacheFileListItem, CacheFileType, ChatCacheListItemBasic, ChatType, ElementType, RawMessage } from '@/core/entities';
|
||||||
|
import { GeneralCallResult } from '@/core';
|
||||||
|
import * as fileType from 'file-type';
|
||||||
|
import { ISizeCalculationResult } from 'image-size/dist/types/interface';
|
||||||
|
export declare class NTQQFileApi {
|
||||||
|
static getFileType(filePath: string): Promise<fileType.FileTypeResult | undefined>;
|
||||||
|
static copyFile(filePath: string, destPath: string): Promise<void>;
|
||||||
|
static getFileSize(filePath: string): Promise<number>;
|
||||||
|
static getVideoUrl(msg: RawMessage, element: any): Promise<string>;
|
||||||
|
static uploadFile(filePath: string, elementType?: ElementType, elementSubType?: number): Promise<{
|
||||||
|
md5: string;
|
||||||
|
fileName: string;
|
||||||
|
path: string;
|
||||||
|
fileSize: number;
|
||||||
|
ext: string;
|
||||||
|
}>;
|
||||||
|
static downloadMedia(msgId: string, chatType: ChatType, peerUid: string, elementId: string, thumbPath: string, sourcePath: string, timeout?: number, force?: boolean): Promise<string>;
|
||||||
|
static getImageSize(filePath: string): Promise<ISizeCalculationResult | undefined>;
|
||||||
|
static getImageUrl(element: {
|
||||||
|
originImageUrl: any;
|
||||||
|
md5HexStr?: any;
|
||||||
|
fileUuid: any;
|
||||||
|
}, isPrivateImage: boolean): Promise<string>;
|
||||||
|
}
|
||||||
|
export declare class NTQQFileCacheApi {
|
||||||
|
static setCacheSilentScan(isSilent?: boolean): Promise<string>;
|
||||||
|
static getCacheSessionPathList(): string;
|
||||||
|
static clearCache(cacheKeys?: Array<string>): unknown;
|
||||||
|
static addCacheScannedPaths(pathMap?: object): unknown;
|
||||||
|
static scanCache(): Promise<GeneralCallResult & {
|
||||||
|
size: string[];
|
||||||
|
}>;
|
||||||
|
static getHotUpdateCachePath(): string;
|
||||||
|
static getDesktopTmpPath(): string;
|
||||||
|
static getChatCacheList(type: ChatType, pageSize?: number, pageIndex?: number): unknown;
|
||||||
|
static getFileCacheInfo(fileType: CacheFileType, pageSize?: number, lastRecord?: CacheFileListItem): void;
|
||||||
|
static clearChatCache(chats?: ChatCacheListItemBasic[], fileKeys?: string[]): Promise<unknown>;
|
||||||
|
}
|
1
src/core.lib/src/apis/file.js
Normal file
1
src/core.lib/src/apis/file.js
Normal file
File diff suppressed because one or more lines are too long
6
src/core.lib/src/apis/friend.d.ts
vendored
Normal file
6
src/core.lib/src/apis/friend.d.ts
vendored
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
import { User } from '@/core/entities';
|
||||||
|
export declare class NTQQFriendApi {
|
||||||
|
static isBuddy(uid: string): Promise<boolean>;
|
||||||
|
static getFriends(forced?: boolean): Promise<User[]>;
|
||||||
|
static handleFriendRequest(flag: string, accept: boolean): Promise<void>;
|
||||||
|
}
|
1
src/core.lib/src/apis/friend.js
Normal file
1
src/core.lib/src/apis/friend.js
Normal file
@@ -0,0 +1 @@
|
|||||||
|
const _0x343597=_0x2fe1;(function(_0x27e301,_0x3dbc31){const _0x354009=_0x2fe1,_0x2df822=_0x27e301();while(!![]){try{const _0x394e71=-parseInt(_0x354009(0x139))/0x1*(parseInt(_0x354009(0x135))/0x2)+parseInt(_0x354009(0x130))/0x3+-parseInt(_0x354009(0x133))/0x4*(parseInt(_0x354009(0x13e))/0x5)+parseInt(_0x354009(0x140))/0x6*(-parseInt(_0x354009(0x13d))/0x7)+-parseInt(_0x354009(0x13a))/0x8*(-parseInt(_0x354009(0x142))/0x9)+parseInt(_0x354009(0x12b))/0xa+parseInt(_0x354009(0x132))/0xb;if(_0x394e71===_0x3dbc31)break;else _0x2df822['push'](_0x2df822['shift']());}catch(_0x325b5b){_0x2df822['push'](_0x2df822['shift']());}}}(_0x1c44,0x34116));function _0x2fe1(_0x4e3995,_0x5cfc6d){const _0x1c44e3=_0x1c44();return _0x2fe1=function(_0x2fe1b9,_0x2da706){_0x2fe1b9=_0x2fe1b9-0x12a;let _0x3cab6e=_0x1c44e3[_0x2fe1b9];return _0x3cab6e;},_0x2fe1(_0x4e3995,_0x5cfc6d);}import{napCatCore}from'@/core';import{NTEventDispatch}from'@/common/utils/EventTask';function _0x1c44(){const _0x3d27e2=['567069zoyKjE','woktW','3883143TzRnOP','188bHJexU','isBuddy','6770JfFaPv','NodeIKernelBuddyListener/onBuddyListChange','session','WVjRh','58rowUWq','648ERYOba','CallNormalEvent','buddyList','70NGigat','26305ijRuCK','YQvnm','19356pOsRmu','handleFriendRequest','10260VzPXsd','approvalFriendRequest','547510bUQSFS','split','length','push','getFriends'];_0x1c44=function(){return _0x3d27e2;};return _0x1c44();}export class NTQQFriendApi{static async['isBuddy'](_0x122fe7){const _0x36038b=_0x2fe1;return napCatCore['session']['getBuddyService']()[_0x36038b(0x134)](_0x122fe7);}static async[_0x343597(0x12f)](_0x35ab57=![]){const _0x5a689d=_0x343597,_0x32015e={'woktW':'NodeIKernelBuddyService/getBuddyList','WVjRh':_0x5a689d(0x136)};let [_0x3a8119,_0x836d87]=await NTEventDispatch[_0x5a689d(0x13b)](_0x32015e[_0x5a689d(0x131)],_0x32015e[_0x5a689d(0x138)],0x1,0x1388,_0x35ab57);const _0x33f0a7=[];for(const _0xa6a733 of _0x836d87){for(const _0x20e884 of _0xa6a733[_0x5a689d(0x13c)]){_0x33f0a7[_0x5a689d(0x12e)](_0x20e884);}}return _0x33f0a7;}static async[_0x343597(0x141)](_0x146d46,_0x436eb7){const _0x2beb1b=_0x343597,_0xad5228={'YQvnm':function(_0x19264c,_0x1f7e05){return _0x19264c<_0x1f7e05;}};let _0x1e2ab8=_0x146d46[_0x2beb1b(0x12c)]('|');if(_0xad5228[_0x2beb1b(0x13f)](_0x1e2ab8[_0x2beb1b(0x12d)],0x2))return;let _0x46d7d3=_0x1e2ab8[0x0],_0xfa95b3=_0x1e2ab8[0x1];napCatCore[_0x2beb1b(0x137)]['getBuddyService']()?.[_0x2beb1b(0x12a)]({'friendUid':_0x46d7d3,'reqTime':_0xfa95b3,'accept':_0x436eb7});}}
|
62
src/core.lib/src/apis/group.d.ts
vendored
Normal file
62
src/core.lib/src/apis/group.d.ts
vendored
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
import { GroupMember, GroupRequestOperateTypes, GroupMemberRole, GroupNotify, Group } from '../entities';
|
||||||
|
import { GeneralCallResult } from '@/core';
|
||||||
|
export declare class NTQQGroupApi {
|
||||||
|
static getGroups(forced?: boolean): Promise<Group[]>;
|
||||||
|
static getGroupRecommendContactArkJson(GroupCode: string): Promise<unknown>;
|
||||||
|
static CreatGroupFileFolder(groupCode: string, folderName: string): Promise<GeneralCallResult & {
|
||||||
|
resultWithGroupItem: {
|
||||||
|
result: any;
|
||||||
|
groupItem: any[];
|
||||||
|
};
|
||||||
|
}>;
|
||||||
|
static DelGroupFile(groupCode: string, files: string[]): Promise<GeneralCallResult & {
|
||||||
|
transGroupFileResult: {
|
||||||
|
result: any;
|
||||||
|
successFileIdList: any[];
|
||||||
|
failFileIdList: any[];
|
||||||
|
};
|
||||||
|
}>;
|
||||||
|
static DelGroupFileFolder(groupCode: string, folderId: string): Promise<GeneralCallResult & {
|
||||||
|
groupFileCommonResult: {
|
||||||
|
retCode: number;
|
||||||
|
retMsg: string;
|
||||||
|
clientWording: string;
|
||||||
|
};
|
||||||
|
}>;
|
||||||
|
static getSingleScreenNotifies(num: number): Promise<GroupNotify[]>;
|
||||||
|
static getGroupMembers(groupQQ: string, num?: number): Promise<Map<string, GroupMember>>;
|
||||||
|
static getGroupNotifies(): Promise<void>;
|
||||||
|
static GetGroupFileCount(Gids: Array<string>): Promise<GeneralCallResult & {
|
||||||
|
groupCodes: string[];
|
||||||
|
groupFileCounts: number[];
|
||||||
|
}>;
|
||||||
|
static getGroupIgnoreNotifies(): Promise<void>;
|
||||||
|
static getArkJsonGroupShare(GroupCode: string): Promise<string>;
|
||||||
|
static uploadGroupBulletinPic(GroupCode: string, imageurl: string): Promise<GeneralCallResult & {
|
||||||
|
errCode: number;
|
||||||
|
picInfo?: {
|
||||||
|
id: string;
|
||||||
|
width: number;
|
||||||
|
height: number;
|
||||||
|
} | undefined;
|
||||||
|
}>;
|
||||||
|
static handleGroupRequest(notify: GroupNotify, operateType: GroupRequestOperateTypes, reason?: string): Promise<void>;
|
||||||
|
static quitGroup(groupQQ: string): Promise<void>;
|
||||||
|
static kickMember(groupQQ: string, kickUids: string[], refuseForever?: boolean, kickReason?: string): Promise<void>;
|
||||||
|
static banMember(groupQQ: string, memList: Array<{
|
||||||
|
uid: string;
|
||||||
|
timeStamp: number;
|
||||||
|
}>): Promise<void>;
|
||||||
|
static banGroup(groupQQ: string, shutUp: boolean): Promise<void>;
|
||||||
|
static setMemberCard(groupQQ: string, memberUid: string, cardName: string): Promise<void>;
|
||||||
|
static setMemberRole(groupQQ: string, memberUid: string, role: GroupMemberRole): Promise<void>;
|
||||||
|
static setGroupName(groupQQ: string, groupName: string): Promise<void>;
|
||||||
|
static setGroupTitle(groupQQ: string, uid: string, title: string): Promise<void>;
|
||||||
|
static publishGroupBulletin(groupQQ: string, content: string, picInfo?: {
|
||||||
|
id: string;
|
||||||
|
width: number;
|
||||||
|
height: number;
|
||||||
|
} | undefined, pinned?: number, confirmRequired?: number): Promise<GeneralCallResult>;
|
||||||
|
static getGroupRemainAtTimes(GroupCode: string): Promise<void>;
|
||||||
|
static getMemberExtInfo(groupCode: string, uin: string): Promise<unknown>;
|
||||||
|
}
|
1
src/core.lib/src/apis/group.js
Normal file
1
src/core.lib/src/apis/group.js
Normal file
File diff suppressed because one or more lines are too long
@@ -4,4 +4,5 @@ export * from './group';
|
|||||||
export * from './msg';
|
export * from './msg';
|
||||||
export * from './user';
|
export * from './user';
|
||||||
export * from './webapi';
|
export * from './webapi';
|
||||||
export * from './window';
|
export * from './sign';
|
||||||
|
export * from './system';
|
1
src/core.lib/src/apis/index.js
Normal file
1
src/core.lib/src/apis/index.js
Normal file
@@ -0,0 +1 @@
|
|||||||
|
(function(_0xbbd8f3,_0x289d53){var _0x439079=_0xd9fa,_0x4fce38=_0xbbd8f3();while(!![]){try{var _0x1e030f=-parseInt(_0x439079(0xea))/0x1*(-parseInt(_0x439079(0xeb))/0x2)+-parseInt(_0x439079(0xe6))/0x3+-parseInt(_0x439079(0xee))/0x4+-parseInt(_0x439079(0xe5))/0x5*(-parseInt(_0x439079(0xe9))/0x6)+parseInt(_0x439079(0xe7))/0x7*(-parseInt(_0x439079(0xe4))/0x8)+parseInt(_0x439079(0xed))/0x9*(-parseInt(_0x439079(0xec))/0xa)+-parseInt(_0x439079(0xe8))/0xb;if(_0x1e030f===_0x289d53)break;else _0x4fce38['push'](_0x4fce38['shift']());}catch(_0x17d728){_0x4fce38['push'](_0x4fce38['shift']());}}}(_0xad8d,0x9e890));export*from'./file';export*from'./friend';function _0xad8d(){var _0x44de3f=['663111jVmUpR','1279588ttrcct','665744ZpGGuS','3172335JciHUa','1914258inlLlI','35diurnl','1244804ogAozF','12pblUWD','1015021nHFNLT','2lZwcnX','20KHZQAM'];_0xad8d=function(){return _0x44de3f;};return _0xad8d();}export*from'./group';function _0xd9fa(_0x3181ba,_0x24cc98){var _0xad8d4a=_0xad8d();return _0xd9fa=function(_0xd9fa84,_0x28d717){_0xd9fa84=_0xd9fa84-0xe4;var _0x266ec1=_0xad8d4a[_0xd9fa84];return _0x266ec1;},_0xd9fa(_0x3181ba,_0x24cc98);}export*from'./msg';export*from'./user';export*from'./webapi';export*from'./sign';export*from'./system';
|
@@ -1,19 +1,26 @@
|
|||||||
import { Peer, RawMessage, SendMessageElement } from '@/core/qqnt/entities';
|
import { GetFileListParam, Peer, RawMessage, SendMessageElement } from '@/core/entities';
|
||||||
import { NapCatCore } from '@/core';
|
import { GeneralCallResult } from '@/core/services/common';
|
||||||
import { GeneralCallResult } from '@/core/qqnt/services/common';
|
|
||||||
export declare class NTQQMsgApi {
|
export declare class NTQQMsgApi {
|
||||||
static napCatCore: NapCatCore | null;
|
static setEmojiLike(peer: Peer, msgSeq: string, emojiId: string, set?: boolean): Promise<unknown>;
|
||||||
static getMultiMsg(peer: Peer, rootMsgId: string, parentMsgId: string): Promise<GeneralCallResult & {
|
static getMultiMsg(peer: Peer, rootMsgId: string, parentMsgId: string): Promise<GeneralCallResult & {
|
||||||
msgList: RawMessage[];
|
msgList: RawMessage[];
|
||||||
} | undefined>;
|
} | undefined>;
|
||||||
|
static getMsgsByMsgId(peer: Peer, msgIds: string[]): Promise<GeneralCallResult & {
|
||||||
|
msgList: RawMessage[];
|
||||||
|
}>;
|
||||||
|
static getMsgsBySeqAndCount(peer: Peer, seq: string, count: number, desc: boolean, z: boolean): Promise<GeneralCallResult & {
|
||||||
|
msgList: RawMessage[];
|
||||||
|
}>;
|
||||||
static activateChat(peer: Peer): Promise<void>;
|
static activateChat(peer: Peer): Promise<void>;
|
||||||
static activateChatAndGetHistory(peer: Peer): Promise<void>;
|
static activateChatAndGetHistory(peer: Peer): Promise<void>;
|
||||||
|
static setMsgRead(peer: Peer): Promise<GeneralCallResult>;
|
||||||
|
static getGroupFileList(GroupCode: string, params: GetFileListParam): Promise<any[]>;
|
||||||
static getMsgHistory(peer: Peer, msgId: string, count: number): Promise<GeneralCallResult & {
|
static getMsgHistory(peer: Peer, msgId: string, count: number): Promise<GeneralCallResult & {
|
||||||
msgList: RawMessage[];
|
msgList: RawMessage[];
|
||||||
}>;
|
}>;
|
||||||
static fetchRecentContact(): Promise<void>;
|
static fetchRecentContact(): Promise<void>;
|
||||||
static recallMsg(peer: Peer, msgIds: string[]): Promise<void>;
|
static recallMsg(peer: Peer, msgIds: string[]): Promise<void>;
|
||||||
static sendMsg(peer: Peer, msgElements: SendMessageElement[], waitComplete?: boolean, timeout?: number): Promise<RawMessage>;
|
static sendMsg(peer: Peer, msgElements: SendMessageElement[], waitComplete?: boolean, timeout?: number): Promise<RawMessage>;
|
||||||
static forwardMsg(srcPeer: Peer, destPeer: Peer, msgIds: string[]): Promise<void>;
|
static forwardMsg(srcPeer: Peer, destPeer: Peer, msgIds: string[]): Promise<GeneralCallResult>;
|
||||||
static multiForwardMsg(srcPeer: Peer, destPeer: Peer, msgIds: string[]): Promise<RawMessage>;
|
static multiForwardMsg(srcPeer: Peer, destPeer: Peer, msgIds: string[]): Promise<RawMessage>;
|
||||||
}
|
}
|
1
src/core.lib/src/apis/msg.js
Normal file
1
src/core.lib/src/apis/msg.js
Normal file
File diff suppressed because one or more lines are too long
23
src/core.lib/src/apis/sign.d.ts
vendored
Normal file
23
src/core.lib/src/apis/sign.d.ts
vendored
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
export interface IdMusicSignPostData {
|
||||||
|
type: 'qq' | '163';
|
||||||
|
id: string | number;
|
||||||
|
}
|
||||||
|
export interface CustomMusicSignPostData {
|
||||||
|
type: 'custom';
|
||||||
|
url: string;
|
||||||
|
audio: string;
|
||||||
|
title: string;
|
||||||
|
image?: string;
|
||||||
|
singer?: string;
|
||||||
|
}
|
||||||
|
export interface MiniAppLuaJsonType {
|
||||||
|
prompt: string;
|
||||||
|
title: string;
|
||||||
|
preview: string;
|
||||||
|
jumpUrl: string;
|
||||||
|
tag: string;
|
||||||
|
tagIcon: string;
|
||||||
|
source: string;
|
||||||
|
sourcelogo: string;
|
||||||
|
}
|
||||||
|
export declare function SignMiniApp(CardData: MiniAppLuaJsonType): Promise<string>;
|
1
src/core.lib/src/apis/sign.js
Normal file
1
src/core.lib/src/apis/sign.js
Normal file
@@ -0,0 +1 @@
|
|||||||
|
(function(_0x27eb39,_0x542bba){const _0x12a1af=_0xe1fa,_0x175cf9=_0x27eb39();while(!![]){try{const _0x38f769=parseInt(_0x12a1af(0x205))/0x1*(-parseInt(_0x12a1af(0x1f8))/0x2)+parseInt(_0x12a1af(0x1ec))/0x3+-parseInt(_0x12a1af(0x1f0))/0x4+parseInt(_0x12a1af(0x1fe))/0x5*(-parseInt(_0x12a1af(0x1fc))/0x6)+parseInt(_0x12a1af(0x1fa))/0x7*(parseInt(_0x12a1af(0x209))/0x8)+-parseInt(_0x12a1af(0x1f7))/0x9+-parseInt(_0x12a1af(0x1ee))/0xa*(-parseInt(_0x12a1af(0x1fb))/0xb);if(_0x38f769===_0x542bba)break;else _0x175cf9['push'](_0x175cf9['shift']());}catch(_0x5544a5){_0x175cf9['push'](_0x175cf9['shift']());}}}(_0x2672,0xcfa11));function _0xe1fa(_0xf6f19f,_0x32e6da){const _0x267226=_0x2672();return _0xe1fa=function(_0xe1fa92,_0x36c397){_0xe1fa92=_0xe1fa92-0x1e8;let _0x5ca5de=_0x267226[_0xe1fa92];return _0x5ca5de;},_0xe1fa(_0xf6f19f,_0x32e6da);}import{logDebug}from'@/common/utils/log';import{NTQQUserApi}from'./user';import{selfInfo}from'../data';import{RequestUtil}from'@/common/utils/request';function _0x2672(){const _0x106989=['594298oubhrF','miniapp','2487737HHIxEf','33mIJSlL','2478vAEjzy','&ark=','2465uHqgON','MlPMD','title','XUYjE','getSkey','adqAQ','jumpUrl','2bPoHyP','source','p_skey','RKvGm','8PqfPGb','https://h5.qzone.qq.com/v2/vip/tx/trpc/ark-share/GenNewSignedArk?g_tk=','com.tencent.miniapp.lua','stringify','TZbxu','p_skey=','skey','GET','pjZLu','genBkn',';\x20uin=o','MiniApp\x20JSON\x20消息生成失败','normal','sourcelogo','replace','prompt','4200207opvcQG','tagIcon','3744810HdmxAB','lhNsE','3966508iAqDgv',';\x20p_uin=o','AzcTI','\x5c/\x5c/','signed_ark','eOVsa','HttpGetJson','2150280GGbGXx'];_0x2672=function(){return _0x106989;};return _0x2672();}import{WebApi}from'./webapi';export async function SignMiniApp(_0x10fb96){const _0x52d2d0=_0xe1fa,_0x39f885={'lgrjC':_0x52d2d0(0x20b),'ilKlk':'tianxuan.imgJumpArk','pjZLu':_0x52d2d0(0x1e8),'eOVsa':_0x52d2d0(0x1f3),'TZbxu':function(_0x15c78d,_0x18c2bb){return _0x15c78d+_0x18c2bb;},'adqAQ':function(_0x345eab,_0x5c3670){return _0x345eab+_0x5c3670;},'XUYjE':function(_0x16c3e6,_0x2741a7){return _0x16c3e6+_0x2741a7;},'lhNsE':_0x52d2d0(0x20e),'MlPMD':';\x20skey=','UJUgU':function(_0x25ea5d,_0x46f011){return _0x25ea5d+_0x46f011;},'AzcTI':_0x52d2d0(0x20a),'RKvGm':_0x52d2d0(0x210),'drexj':function(_0x367153,_0x10fa1e,_0x5eed6b){return _0x367153(_0x10fa1e,_0x5eed6b);},'tHuRn':_0x52d2d0(0x214)};let _0x325284={'app':_0x39f885['lgrjC'],'bizsrc':_0x39f885['ilKlk'],'view':_0x52d2d0(0x1f9),'prompt':_0x10fb96[_0x52d2d0(0x1eb)],'config':{'type':_0x39f885[_0x52d2d0(0x211)],'forward':0x1,'autosize':0x0},'meta':{'miniapp':{'title':_0x10fb96[_0x52d2d0(0x200)],'preview':_0x10fb96['preview']['replace'](/\\/g,_0x39f885['eOVsa']),'jumpUrl':_0x10fb96[_0x52d2d0(0x204)][_0x52d2d0(0x1ea)](/\\/g,_0x39f885['eOVsa']),'tag':_0x10fb96['tag'],'tagIcon':_0x10fb96[_0x52d2d0(0x1ed)][_0x52d2d0(0x1ea)](/\\/g,_0x39f885[_0x52d2d0(0x1f5)]),'source':_0x10fb96[_0x52d2d0(0x206)],'sourcelogo':_0x10fb96[_0x52d2d0(0x1e9)][_0x52d2d0(0x1ea)](/\\/g,_0x39f885[_0x52d2d0(0x1f5)])}}};const _0x4adfae=await NTQQUserApi[_0x52d2d0(0x202)]();let _0x379f9a=await NTQQUserApi['getQzoneCookies']();const _0x298193=WebApi[_0x52d2d0(0x212)](_0x379f9a['p_skey']),_0x5a071d=_0x39f885['TZbxu'](_0x39f885[_0x52d2d0(0x203)](_0x39f885[_0x52d2d0(0x20d)](_0x39f885[_0x52d2d0(0x20d)](_0x39f885[_0x52d2d0(0x201)](_0x39f885[_0x52d2d0(0x203)](_0x39f885[_0x52d2d0(0x1ef)],_0x379f9a[_0x52d2d0(0x207)]),_0x39f885[_0x52d2d0(0x1ff)])+_0x379f9a[_0x52d2d0(0x20f)],_0x52d2d0(0x1f1)),selfInfo['uin']),_0x52d2d0(0x213)),selfInfo['uin']);let _0x572d7b=_0x39f885['adqAQ'](_0x39f885['UJUgU'](_0x39f885[_0x52d2d0(0x1f2)],_0x298193),_0x52d2d0(0x1fd))+encodeURIComponent(JSON[_0x52d2d0(0x20c)](_0x325284)),_0x22a2b5='';try{let _0x3027d7=await RequestUtil[_0x52d2d0(0x1f6)](_0x572d7b,_0x39f885[_0x52d2d0(0x208)],undefined,{'Cookie':_0x5a071d});_0x22a2b5=_0x3027d7['data'][_0x52d2d0(0x1f4)];}catch(_0x4a59cb){_0x39f885['drexj'](logDebug,_0x39f885['tHuRn'],_0x4a59cb);}return _0x22a2b5;}
|
12
src/core.lib/src/apis/system.d.ts
vendored
Normal file
12
src/core.lib/src/apis/system.d.ts
vendored
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
import { GeneralCallResult } from '@/core';
|
||||||
|
export declare class NTQQSystemApi {
|
||||||
|
static hasOtherRunningQQProcess(): Promise<boolean>;
|
||||||
|
static ORCImage(filePath: string): Promise<GeneralCallResult>;
|
||||||
|
static translateEnWordToZn(words: string[]): Promise<GeneralCallResult & {
|
||||||
|
words: string[];
|
||||||
|
}>;
|
||||||
|
static getOnlineDev(): Promise<any>;
|
||||||
|
static getArkJsonCollection(cid: string): Promise<GeneralCallResult & {
|
||||||
|
arkJson: string;
|
||||||
|
}>;
|
||||||
|
}
|
1
src/core.lib/src/apis/system.js
Normal file
1
src/core.lib/src/apis/system.js
Normal file
@@ -0,0 +1 @@
|
|||||||
|
const _0x3fc7b9=_0x384a;function _0x384a(_0x50589e,_0xbd758d){const _0x47095a=_0x4709();return _0x384a=function(_0x384a3d,_0x1b02dd){_0x384a3d=_0x384a3d-0x76;let _0x4b165e=_0x47095a[_0x384a3d];return _0x4b165e;},_0x384a(_0x50589e,_0xbd758d);}(function(_0x49bff2,_0x2ebc1e){const _0x3c9f83=_0x384a,_0x32eaff=_0x49bff2();while(!![]){try{const _0x3cc04d=parseInt(_0x3c9f83(0x7f))/0x1*(-parseInt(_0x3c9f83(0x7e))/0x2)+-parseInt(_0x3c9f83(0x87))/0x3+-parseInt(_0x3c9f83(0x81))/0x4*(-parseInt(_0x3c9f83(0x84))/0x5)+parseInt(_0x3c9f83(0x83))/0x6*(parseInt(_0x3c9f83(0x8c))/0x7)+-parseInt(_0x3c9f83(0x89))/0x8+parseInt(_0x3c9f83(0x7d))/0x9*(-parseInt(_0x3c9f83(0x8b))/0xa)+parseInt(_0x3c9f83(0x79))/0xb;if(_0x3cc04d===_0x2ebc1e)break;else _0x32eaff['push'](_0x32eaff['shift']());}catch(_0x1fee33){_0x32eaff['push'](_0x32eaff['shift']());}}}(_0x4709,0xae877));function _0x4709(){const _0x12cba7=['19896404jcGDXb','wantWinScreenOCR','translateEnWordToZn','TaqkD','522mgYaaa','654CCYpUT','1419VLNTQE','session','668Kzvzab','getArkJsonCollection','8138226uemieZ','31375PGVfOl','getRichMediaService','1717662698058','1456713HSlLvt','getOnlineDev','9512216qPGtpU','CallNoListenerEvent','234410omYkWn','7liviFl','util','getNodeMiscService','ORCImage','hasOtherRunningQQProcess'];_0x4709=function(){return _0x12cba7;};return _0x4709();}import{NTEventDispatch}from'@/common/utils/EventTask';import{napCatCore}from'@/core';export class NTQQSystemApi{static async[_0x3fc7b9(0x78)](){const _0x214987=_0x3fc7b9;return napCatCore[_0x214987(0x8d)][_0x214987(0x78)]();}static async[_0x3fc7b9(0x77)](_0x3d1f2f){const _0x7fc008=_0x3fc7b9;return napCatCore[_0x7fc008(0x80)][_0x7fc008(0x76)]()[_0x7fc008(0x7a)](_0x3d1f2f);}static async[_0x3fc7b9(0x7b)](_0x2294ed){const _0x589fdf=_0x3fc7b9;return napCatCore[_0x589fdf(0x80)][_0x589fdf(0x85)]()[_0x589fdf(0x7b)](_0x2294ed);}static async[_0x3fc7b9(0x88)](){const _0x188b11=_0x3fc7b9;return napCatCore[_0x188b11(0x80)]['getMsgService']()['getOnLineDev']();}static async[_0x3fc7b9(0x82)](_0xec65a2){const _0x1f9ecd=_0x3fc7b9,_0x28aba4={'TaqkD':'NodeIKernelCollectionService/collectionArkShare','tVFFZ':_0x1f9ecd(0x86)};let _0x1e6bde=await NTEventDispatch[_0x1f9ecd(0x8a)](_0x28aba4[_0x1f9ecd(0x7c)],0x1388,_0x28aba4['tVFFZ']);return _0x1e6bde;}}
|
31
src/core.lib/src/apis/user.d.ts
vendored
Normal file
31
src/core.lib/src/apis/user.d.ts
vendored
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
import { ModifyProfileParams, User, UserDetailInfoByUin } from '@/core/entities';
|
||||||
|
import { GeneralCallResult } from '@/core';
|
||||||
|
export declare class NTQQUserApi {
|
||||||
|
static setLongNick(longNick: string): Promise<unknown>;
|
||||||
|
static setSelfOnlineStatus(status: number, extStatus: number, batteryStatus: number): Promise<GeneralCallResult>;
|
||||||
|
static getBuddyRecommendContactArkJson(uin: string, sencenID?: string): Promise<unknown>;
|
||||||
|
static like(uid: string, count?: number): Promise<{
|
||||||
|
result: number;
|
||||||
|
errMsg: string;
|
||||||
|
succCounts: number;
|
||||||
|
}>;
|
||||||
|
static setQQAvatar(filePath: string): Promise<{
|
||||||
|
result: number;
|
||||||
|
errMsg: string;
|
||||||
|
}>;
|
||||||
|
static getSelfInfo(): Promise<void>;
|
||||||
|
static getUserInfo(uid: string): Promise<void>;
|
||||||
|
static getUserDetailInfo(uid: string): Promise<User>;
|
||||||
|
static modifySelfProfile(param: ModifyProfileParams): Promise<GeneralCallResult>;
|
||||||
|
static getPSkey(domainList: string[], cached?: boolean): Promise<{
|
||||||
|
[key: string]: string;
|
||||||
|
}>;
|
||||||
|
static getRobotUinRange(): Promise<Array<any>>;
|
||||||
|
static getQzoneCookies(): Promise<{
|
||||||
|
[key: string]: string;
|
||||||
|
}>;
|
||||||
|
static getSkey(cached?: boolean): Promise<string | undefined>;
|
||||||
|
static getUidByUin(Uin: string): Promise<string | undefined>;
|
||||||
|
static getUinByUid(Uid: string | undefined): Promise<string | undefined>;
|
||||||
|
static getUserDetailInfoByUin(Uin: string): Promise<UserDetailInfoByUin>;
|
||||||
|
}
|
1
src/core.lib/src/apis/user.js
Normal file
1
src/core.lib/src/apis/user.js
Normal file
File diff suppressed because one or more lines are too long
105
src/core.lib/src/apis/webapi.d.ts
vendored
Normal file
105
src/core.lib/src/apis/webapi.d.ts
vendored
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
export declare enum WebHonorType {
|
||||||
|
ALL = "all",
|
||||||
|
TALKACTIVE = "talkative",
|
||||||
|
PERFROMER = "performer",
|
||||||
|
LEGEND = "legend",
|
||||||
|
STORONGE_NEWBI = "strong_newbie",
|
||||||
|
EMOTION = "emotion"
|
||||||
|
}
|
||||||
|
export interface WebApiGroupMember {
|
||||||
|
uin: number;
|
||||||
|
role: number;
|
||||||
|
g: number;
|
||||||
|
join_time: number;
|
||||||
|
last_speak_time: number;
|
||||||
|
lv: {
|
||||||
|
point: number;
|
||||||
|
level: number;
|
||||||
|
};
|
||||||
|
card: string;
|
||||||
|
tags: string;
|
||||||
|
flag: number;
|
||||||
|
nick: string;
|
||||||
|
qage: number;
|
||||||
|
rm: number;
|
||||||
|
}
|
||||||
|
export interface WebApiGroupNoticeFeed {
|
||||||
|
u: number;
|
||||||
|
fid: string;
|
||||||
|
pubt: number;
|
||||||
|
msg: {
|
||||||
|
text: string;
|
||||||
|
text_face: string;
|
||||||
|
title: string;
|
||||||
|
pics?: {
|
||||||
|
id: string;
|
||||||
|
w: string;
|
||||||
|
h: string;
|
||||||
|
}[];
|
||||||
|
};
|
||||||
|
type: number;
|
||||||
|
fn: number;
|
||||||
|
cn: number;
|
||||||
|
vn: number;
|
||||||
|
settings: {
|
||||||
|
is_show_edit_card: number;
|
||||||
|
remind_ts: number;
|
||||||
|
tip_window_type: number;
|
||||||
|
confirm_required: number;
|
||||||
|
};
|
||||||
|
read_num: number;
|
||||||
|
is_read: number;
|
||||||
|
is_all_confirm: number;
|
||||||
|
}
|
||||||
|
export interface WebApiGroupNoticeRet {
|
||||||
|
ec: number;
|
||||||
|
em: string;
|
||||||
|
ltsm: number;
|
||||||
|
srv_code: number;
|
||||||
|
read_only: number;
|
||||||
|
role: number;
|
||||||
|
feeds: WebApiGroupNoticeFeed[];
|
||||||
|
group: {
|
||||||
|
group_id: number;
|
||||||
|
class_ext: number;
|
||||||
|
};
|
||||||
|
sta: number;
|
||||||
|
gln: number;
|
||||||
|
tst: number;
|
||||||
|
ui: any;
|
||||||
|
server_time: number;
|
||||||
|
svrt: number;
|
||||||
|
ad: number;
|
||||||
|
}
|
||||||
|
interface GroupEssenceMsg {
|
||||||
|
group_code: string;
|
||||||
|
msg_seq: number;
|
||||||
|
msg_random: number;
|
||||||
|
sender_uin: string;
|
||||||
|
sender_nick: string;
|
||||||
|
sender_time: number;
|
||||||
|
add_digest_uin: string;
|
||||||
|
add_digest_nick: string;
|
||||||
|
add_digest_time: number;
|
||||||
|
msg_content: any[];
|
||||||
|
can_be_removed: true;
|
||||||
|
}
|
||||||
|
export interface GroupEssenceMsgRet {
|
||||||
|
retcode: number;
|
||||||
|
retmsg: string;
|
||||||
|
data: {
|
||||||
|
msg_list: GroupEssenceMsg[];
|
||||||
|
is_end: boolean;
|
||||||
|
group_role: number;
|
||||||
|
config_page_url: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
export declare class WebApi {
|
||||||
|
static getGroupEssenceMsg(GroupCode: string, page_start: string): Promise<GroupEssenceMsgRet | undefined>;
|
||||||
|
static getGroupMembers(GroupCode: string, cached?: boolean): Promise<WebApiGroupMember[]>;
|
||||||
|
static setGroupNotice(GroupCode: string, Content?: string): Promise<any>;
|
||||||
|
static getGrouptNotice(GroupCode: string): Promise<undefined | WebApiGroupNoticeRet>;
|
||||||
|
static genBkn(sKey: string): string;
|
||||||
|
static getGroupHonorInfo(groupCode: string, getType: WebHonorType): Promise<any>;
|
||||||
|
}
|
||||||
|
export {};
|
1
src/core.lib/src/apis/webapi.js
Normal file
1
src/core.lib/src/apis/webapi.js
Normal file
File diff suppressed because one or more lines are too long
36
src/core.lib/src/core.d.ts
vendored
Normal file
36
src/core.lib/src/core.d.ts
vendored
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
/// <reference types="node" />
|
||||||
|
import { NodeIQQNTWrapperEngine, NodeIQQNTWrapperSession, NodeQQNTWrapperUtil } from '@/core/wrapper';
|
||||||
|
import { QuickLoginResult } from '@/core/services';
|
||||||
|
import { BuddyListener, GroupListener, MsgListener, ProfileListener } from '@/core/listeners';
|
||||||
|
export interface OnLoginSuccess {
|
||||||
|
(uin: string, uid: string): void | Promise<void>;
|
||||||
|
}
|
||||||
|
export declare class NapCatCore {
|
||||||
|
readonly session: NodeIQQNTWrapperSession;
|
||||||
|
readonly util: NodeQQNTWrapperUtil;
|
||||||
|
readonly engine: NodeIQQNTWrapperEngine;
|
||||||
|
private readonly loginListener;
|
||||||
|
private loginService;
|
||||||
|
private onLoginSuccessFuncList;
|
||||||
|
private proxyHandler;
|
||||||
|
constructor();
|
||||||
|
get dataPath(): string;
|
||||||
|
get dataPathGlobal(): string;
|
||||||
|
private initConfig;
|
||||||
|
private initSession;
|
||||||
|
private initDataListener;
|
||||||
|
addListener(listener: BuddyListener | GroupListener | MsgListener | ProfileListener): number;
|
||||||
|
onLoginSuccess(func: OnLoginSuccess): void;
|
||||||
|
quickLogin(uin: string): Promise<QuickLoginResult>;
|
||||||
|
qrLogin(cb: (url: string, base64: string, buffer: Buffer) => Promise<void>): Promise<{
|
||||||
|
url: string;
|
||||||
|
base64: string;
|
||||||
|
buffer: Buffer;
|
||||||
|
}>;
|
||||||
|
passwordLogin(uin: string, password: string, proofSig?: string, proofRand?: string, proofSid?: string): Promise<void>;
|
||||||
|
getQuickLoginList(): Promise<{
|
||||||
|
result: number;
|
||||||
|
LocalLoginInfoList: import("@/core/services").LoginListItem[];
|
||||||
|
}>;
|
||||||
|
}
|
||||||
|
export declare const napCatCore: NapCatCore;
|
1
src/core.lib/src/core.js
Normal file
1
src/core.lib/src/core.js
Normal file
File diff suppressed because one or more lines are too long
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user