mirror of
https://github.com/NapNeko/NapCatQQ.git
synced 2025-07-19 12:03:37 +00:00
Compare commits
1146 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
0f8af273ae | ||
![]() |
fa29a31da9 | ||
![]() |
9e0d2606d8 | ||
![]() |
338dedd6e0 | ||
![]() |
1f893b1393 | ||
![]() |
b783d6f928 | ||
![]() |
8ca0d40f05 | ||
![]() |
2f40a80434 | ||
![]() |
812d8eb5bb | ||
![]() |
bf5f548349 | ||
![]() |
ebf90e72b9 | ||
![]() |
31d7d42edf | ||
![]() |
833875b42f | ||
![]() |
b901c10f3c | ||
![]() |
8ab678bd97 | ||
![]() |
55d5072f46 | ||
![]() |
a379ffd0f2 | ||
![]() |
4501d73134 | ||
![]() |
5f15774ec7 | ||
![]() |
c9e057599e | ||
![]() |
66851f5625 | ||
![]() |
b33c235b4d | ||
![]() |
d6693b6114 | ||
![]() |
36b4d26c78 | ||
![]() |
617592d90a | ||
![]() |
15a77b8070 | ||
![]() |
606eccd22b | ||
![]() |
5613450313 | ||
![]() |
c59b5564af | ||
![]() |
330b086b8b | ||
![]() |
9837ef4f36 | ||
![]() |
add46b3251 | ||
![]() |
e169199107 | ||
![]() |
92fe654850 | ||
![]() |
b257486404 | ||
![]() |
bdf2e33f40 | ||
![]() |
224d361923 | ||
![]() |
3452fa56df | ||
![]() |
cd256235da | ||
![]() |
361a164f2a | ||
![]() |
0e60d4b198 | ||
![]() |
67b47e39b4 | ||
![]() |
8f54310f63 | ||
![]() |
c7a7494d7e | ||
![]() |
af88b3166d | ||
![]() |
b7837b2a14 | ||
![]() |
950ddc749e | ||
![]() |
df081ef0cf | ||
![]() |
7b24f90d9f | ||
![]() |
f2e4579fd8 | ||
![]() |
97cb351827 | ||
![]() |
c1ec53fdbb | ||
![]() |
98214aa429 | ||
![]() |
ce7deac2dd | ||
![]() |
612092b867 | ||
![]() |
92579d5949 | ||
![]() |
9ab07060ae | ||
![]() |
0d45125d79 | ||
![]() |
9ced152778 | ||
![]() |
3685ab2e3e | ||
![]() |
be605f11f2 | ||
![]() |
8cca8df976 | ||
![]() |
990a31e961 | ||
![]() |
5db201c342 | ||
![]() |
a625e30dd4 | ||
![]() |
b236cdd060 | ||
![]() |
2db9899184 | ||
![]() |
fe5d6db986 | ||
![]() |
7c7bf8fecf | ||
![]() |
76e3a46378 | ||
![]() |
16f3897fec | ||
![]() |
045e120854 | ||
![]() |
2b7fcce9b2 | ||
![]() |
9685931694 | ||
![]() |
1dc844435a | ||
![]() |
18892379de | ||
![]() |
620d61c8dc | ||
![]() |
9f91398875 | ||
![]() |
a29b1154a9 | ||
![]() |
34d19a471a | ||
![]() |
2ef6477d7c | ||
![]() |
26e6800836 | ||
![]() |
9dbbcf3872 | ||
![]() |
6b3343e1e4 | ||
![]() |
ef48f754a5 | ||
![]() |
3be1ede847 | ||
![]() |
7bff1b61e8 | ||
![]() |
6affd0eb68 | ||
![]() |
0a112d15e0 | ||
![]() |
4e03f582bb | ||
![]() |
8f186c1c5e | ||
![]() |
cd1bae9a1f | ||
![]() |
60796c26ca | ||
![]() |
28927f950d | ||
![]() |
95f16ebc8c | ||
![]() |
25bca8385d | ||
![]() |
965c7f23b4 | ||
![]() |
33082af9cc | ||
![]() |
1f7f3565b0 | ||
![]() |
f784363696 | ||
![]() |
37bd51e138 | ||
![]() |
c367728c43 | ||
![]() |
61f0f5d884 | ||
![]() |
5f2ebeead7 | ||
![]() |
7646037fc7 | ||
![]() |
eac6d285ff | ||
![]() |
b921d5e734 | ||
![]() |
831d808e63 | ||
![]() |
451b88d7e3 | ||
![]() |
f916682a71 | ||
![]() |
2d76bcf0cf | ||
![]() |
8db294efe6 | ||
![]() |
5cc5149aed | ||
![]() |
7ecb01dc9f | ||
![]() |
8bf1a545d9 | ||
![]() |
efb2be2f94 | ||
![]() |
bd2edda494 | ||
![]() |
991172eae4 | ||
![]() |
fc7631f9aa | ||
![]() |
a21efb7d2f | ||
![]() |
03bc844ad0 | ||
![]() |
f7bdc35ed6 | ||
![]() |
0efdffd857 | ||
![]() |
6a16a42d0c | ||
![]() |
e9c00c72b1 | ||
![]() |
ab22f36b8a | ||
![]() |
c57497cd91 | ||
![]() |
ba123236e5 | ||
![]() |
338b6e4607 | ||
![]() |
f88c717560 | ||
![]() |
f8ffc92db5 | ||
![]() |
98c23c172c | ||
![]() |
781c107d8c | ||
![]() |
186668c075 | ||
![]() |
cf9f785193 | ||
![]() |
72d2d3f224 | ||
![]() |
087c76b394 | ||
![]() |
4f9fb2c8c3 | ||
![]() |
334e43e764 | ||
![]() |
7843256402 | ||
![]() |
0522ba35fe | ||
![]() |
24d3b52e0b | ||
![]() |
3177110f0f | ||
![]() |
e1b8243a67 | ||
![]() |
b1c6ce3885 | ||
![]() |
0b4b25a11e | ||
![]() |
1176fe984a | ||
![]() |
6ca768c3ee | ||
![]() |
3da1659c8d | ||
![]() |
9aa4cd319c | ||
![]() |
5af866cdca | ||
![]() |
2b421fa447 | ||
![]() |
30ec964325 | ||
![]() |
714d7d72eb | ||
![]() |
687aa0f363 | ||
![]() |
8363ab07a7 | ||
![]() |
c46a757339 | ||
![]() |
557864395b | ||
![]() |
3f7a85d80b | ||
![]() |
8d18d2ce1f | ||
![]() |
7141ba1587 | ||
![]() |
44d350a225 | ||
![]() |
239b8e72d9 | ||
![]() |
279bdb6fb1 | ||
![]() |
a0cea819da | ||
![]() |
9ab7f60544 | ||
![]() |
aaf7191bf3 | ||
![]() |
628c9be0c8 | ||
![]() |
733052720c | ||
![]() |
a10f007194 | ||
![]() |
6fa50c58d3 | ||
![]() |
c54a58d6e4 | ||
![]() |
34cb1ea3fd | ||
![]() |
f640b0ca91 | ||
![]() |
60e16da42e | ||
![]() |
0cdceb95d6 | ||
![]() |
70bd22d925 | ||
![]() |
82462dd647 | ||
![]() |
c0466e943d | ||
![]() |
b187b4695d | ||
![]() |
b1956d2a37 | ||
![]() |
590b622e5f | ||
![]() |
3d8174396a | ||
![]() |
b8dc6e9bd9 | ||
![]() |
8ee99109dc | ||
![]() |
902041d4ee | ||
![]() |
cc34aef47e | ||
![]() |
0afbbe7c7a | ||
![]() |
8004553ba7 | ||
![]() |
0023b2846a | ||
![]() |
34775c1816 | ||
![]() |
e0759e704b | ||
![]() |
0aa225ca78 | ||
![]() |
b43b4ee5c0 | ||
![]() |
0cdb8cecbf | ||
![]() |
fd6a306742 | ||
![]() |
7f3b3d2277 | ||
![]() |
8be5b977bf | ||
![]() |
d7ddb15f9c | ||
![]() |
9a6a1798d0 | ||
![]() |
14196fd349 | ||
![]() |
941b89a523 | ||
![]() |
a5f9e5f8c0 | ||
![]() |
80c3356c8f | ||
![]() |
914136b750 | ||
![]() |
f9a60795f5 | ||
![]() |
19640927c7 | ||
![]() |
22faac7e36 | ||
![]() |
30d260ab32 | ||
![]() |
115120d066 | ||
![]() |
1327844736 | ||
![]() |
29904f3cb7 | ||
![]() |
50395594b7 | ||
![]() |
9360af88b3 | ||
![]() |
376370336c | ||
![]() |
70df6e3302 | ||
![]() |
0a1fc2dc12 | ||
![]() |
9857f6e437 | ||
![]() |
56d6ebe916 | ||
![]() |
81134ea2d4 | ||
![]() |
a9f3e7fc54 | ||
![]() |
eb84e2f8c9 | ||
![]() |
61cfa0e86d | ||
![]() |
0a01b8ade9 | ||
![]() |
1457efa9a4 | ||
![]() |
fa5c7add7a | ||
![]() |
d644eba4d1 | ||
![]() |
9c422c1a8f | ||
![]() |
b6db37202f | ||
![]() |
4ca3891089 | ||
![]() |
4c7ed01776 | ||
![]() |
45c922c377 | ||
![]() |
f854c258bd | ||
![]() |
a643fac073 | ||
![]() |
861f105bea | ||
![]() |
bf10ce9f1e | ||
![]() |
ccf521d0a8 | ||
![]() |
6075d98eaa | ||
![]() |
a3801fc243 | ||
![]() |
a1f0c05f3a | ||
![]() |
a568c96929 | ||
![]() |
d58bcf3c0e | ||
![]() |
985f2e6436 | ||
![]() |
ad441fa793 | ||
![]() |
316300cc86 | ||
![]() |
5c4f37b234 | ||
![]() |
77b51a072d | ||
![]() |
2536e1ae6a | ||
![]() |
14822c9599 | ||
![]() |
e53c37adc9 | ||
![]() |
c37539354c | ||
![]() |
ae0277f33c | ||
![]() |
b863896249 | ||
![]() |
5b42f8b743 | ||
![]() |
3883fab614 | ||
![]() |
61d6bcec4b | ||
![]() |
3b5902b033 | ||
![]() |
3a88c21a3b | ||
![]() |
91a5055dee | ||
![]() |
7befd1469f | ||
![]() |
c72ebe495c | ||
![]() |
19e06b97e6 | ||
![]() |
7519825303 | ||
![]() |
d9315bf309 | ||
![]() |
8c36c809a0 | ||
![]() |
8138aa3cb2 | ||
![]() |
87aef3ca78 | ||
![]() |
a3f1d26d6b | ||
![]() |
06cebc5670 | ||
![]() |
867fd62d77 | ||
![]() |
650cdf2916 | ||
![]() |
ebf461f2fd | ||
![]() |
27fa319b2a | ||
![]() |
d95ac894f4 | ||
![]() |
ae84a8dd11 | ||
![]() |
2fc963f986 | ||
![]() |
be1f938ebd | ||
![]() |
cccf4d503d | ||
![]() |
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 | ||
![]() |
7edd5a7a8e | ||
![]() |
c1edc1b99b |
@@ -1,21 +1,21 @@
|
|||||||
# EditorConfig is awesome: https://EditorConfig.org
|
# EditorConfig is awesome: https://EditorConfig.org
|
||||||
|
|
||||||
# top-most EditorConfig file
|
# top-most EditorConfig file
|
||||||
root = true
|
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
|
||||||
insert_final_newline = true
|
insert_final_newline = true
|
||||||
|
|
||||||
# Matches multiple files with brace expansion notation
|
# Matches multiple files with brace expansion notation
|
||||||
# Set default charset
|
# Set default charset
|
||||||
charset = utf-8
|
charset = utf-8
|
||||||
|
|
||||||
# 2 space indentation
|
# 2 space indentation
|
||||||
[*.{cjs,mjs,js,jsx,ts,tsx,css,scss,sass,html,json}]
|
[*.{cjs,mjs,js,jsx,ts,tsx,css,scss,sass,html,json}]
|
||||||
indent_style = space
|
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/', 'src/core.lib/'],
|
'ignorePatterns': ['src/core/', 'src/core.lib/','src/proto/'],
|
||||||
'extends': [
|
'extends': [
|
||||||
'eslint:recommended',
|
'eslint:recommended',
|
||||||
'plugin:@typescript-eslint/recommended'
|
'plugin:@typescript-eslint/recommended'
|
||||||
|
15
.github/workflows/build.yml
vendored
15
.github/workflows/build.yml
vendored
@@ -1,6 +1,11 @@
|
|||||||
name: "Build"
|
name: "Build Action"
|
||||||
on:
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
push:
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
|
||||||
|
permissions: write-all
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build-linux:
|
build-linux:
|
||||||
@@ -9,7 +14,7 @@ jobs:
|
|||||||
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
|
||||||
@@ -17,6 +22,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
repository: 'NapNeko/NapCatQQ'
|
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
|
||||||
@@ -41,13 +47,14 @@ jobs:
|
|||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
target_platform: [win32]
|
target_platform: [win32]
|
||||||
target_arch: [x64]
|
target_arch: [x64,ia32]
|
||||||
steps:
|
steps:
|
||||||
- name: Clone Main Repository
|
- name: Clone Main Repository
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
repository: 'NapNeko/NapCatQQ'
|
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
|
||||||
@@ -64,4 +71,4 @@ jobs:
|
|||||||
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
|
||||||
|
31
.github/workflows/release.yml
vendored
31
.github/workflows/release.yml
vendored
@@ -1,4 +1,4 @@
|
|||||||
name: "release"
|
name: "Build Release"
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
@@ -14,6 +14,7 @@ jobs:
|
|||||||
- name: Clone Repository
|
- name: Clone Repository
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
|
ref: main
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
- name: Extract version from tag
|
- name: Extract version from tag
|
||||||
@@ -30,11 +31,12 @@ jobs:
|
|||||||
node ./script/checkVersion.cjs
|
node ./script/checkVersion.cjs
|
||||||
sh ./checkVersion.sh
|
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
|
||||||
@@ -42,6 +44,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
repository: 'NapNeko/NapCatQQ'
|
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
|
||||||
@@ -50,6 +53,8 @@ jobs:
|
|||||||
|
|
||||||
- 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
|
||||||
@@ -62,17 +67,19 @@ 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:
|
||||||
target_platform: [win32]
|
target_platform: [win32]
|
||||||
target_arch: [x64]
|
target_arch: [x64,ia32]
|
||||||
steps:
|
steps:
|
||||||
- name: Clone Main Repository
|
- name: Clone Main Repository
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
repository: 'NapNeko/NapCatQQ'
|
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
|
||||||
@@ -82,6 +89,8 @@ jobs:
|
|||||||
|
|
||||||
- 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
|
||||||
@@ -107,16 +116,24 @@ jobs:
|
|||||||
base=$(basename "$dir")
|
base=$(basename "$dir")
|
||||||
zip -r "${base}.zip" "$dir"
|
zip -r "${base}.zip" "$dir"
|
||||||
done
|
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
|
- name: Create Release Draft and Upload Artifacts
|
||||||
uses: softprops/action-gh-release@v1
|
uses: softprops/action-gh-release@v1
|
||||||
with:
|
with:
|
||||||
name: NapCat V0.0.0
|
name: NapCat V${{ env.VERSION }}
|
||||||
token: ${{ secrets.NAPCAT_BUILD }}
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
body_path: CHANGELOG.md
|
||||||
files: |
|
files: |
|
||||||
|
NapCat.win32.ia32.zip
|
||||||
NapCat.win32.x64.zip
|
NapCat.win32.x64.zip
|
||||||
NapCat.linux.x64.zip
|
NapCat.linux.x64.zip
|
||||||
NapCat.linux.arm64.zip
|
NapCat.linux.arm64.zip
|
||||||
NapCat.darwin.x64.zip
|
# NapCat.darwin.x64.zip
|
||||||
NapCat.darwin.arm64.zip
|
# NapCat.darwin.arm64.zip
|
||||||
draft: true
|
draft: true
|
||||||
|
69
.github/workflows/test.yml
vendored
Normal file
69
.github/workflows/test.yml
vendored
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
name: "Build Test"
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
permissions: write-all
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build-linux:
|
||||||
|
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:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
target_platform: [win32]
|
||||||
|
target_arch: [x64,ia32]
|
||||||
|
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
|
9
.gitignore
vendored
9
.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,4 +14,5 @@ src/core.lib/common/
|
|||||||
|
|
||||||
# Build
|
# Build
|
||||||
*.db
|
*.db
|
||||||
checkVersion.sh
|
checkVersion.sh
|
||||||
|
bun.lockb
|
||||||
|
4
.gitmodules
vendored
4
.gitmodules
vendored
@@ -1,4 +0,0 @@
|
|||||||
[submodule "src/core"]
|
|
||||||
path = src/core
|
|
||||||
url = https://github.com/NapNeko/core.git
|
|
||||||
branch = master
|
|
156
README.md
156
README.md
@@ -4,162 +4,30 @@
|
|||||||
|
|
||||||
## 项目介绍
|
## 项目介绍
|
||||||
|
|
||||||
NapCatQQ(瞌睡猫QQ,不准叫我NCQQ!),像睡着了一样在后台低占用运行的无头(没有界面)的NTQQ
|
NapCatQQ 是基于 PC NTQQ 本体实现一套无头 Bot 框架。
|
||||||
|
|
||||||
目前测试在 Windows 上表现优秀,最低可达只占用内存 **20M**左右
|
名字寓意 瞌睡猫QQ,像睡着了一样在后台低占用运行的无需GUI界面的NTQQ。
|
||||||
|
|
||||||
由于 Linux 上的 QQ 图形依赖较多,会导致内存占用小高,大约 **100+M**,目前正在研究如何优化
|
## 如何使用
|
||||||
|
|
||||||
具体占用会因人而异,QQ 群、好友越多占用越高
|
可前往 [Release](https://github.com/NapNeko/NapCatQQ/releases/) 页面下载最新版本
|
||||||
|
|
||||||
## 下载
|
**首次使用** 请务必前往 [官方文档](https://napneko.github.io/) 查看使用文档与教程
|
||||||
|
|
||||||
前往 Release 页面下载最新版本
|
|
||||||
|
|
||||||
## 启动
|
## 项目声明
|
||||||
|
|
||||||
NapCat 是基于 官方NTQQ 实现的Bot框架,因此先需要安装官方QQ,**注意同个账号不能同时登录原版 QQ 和 NapCatQQ**
|
|
||||||
|
|
||||||
*如果没有安装 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` 或 `napcat-utf8.bat`
|
|
||||||
|
|
||||||
*如果出现 powershell 运行不了,以管理员身份打开 powershell,输入 `Set-ExecutionPolicy RemoteSigned`*
|
|
||||||
|
|
||||||
### Linux 启动
|
|
||||||
|
|
||||||
运行`napcat.sh`
|
|
||||||
|
|
||||||
## 使用无需扫码快速登录
|
|
||||||
|
|
||||||
前提是你已经成功登录过QQ,可以加参数` -q <你的QQ>` 进行登录,如`napcat.sh -q 1234567`
|
|
||||||
|
|
||||||
## 安装
|
|
||||||
|
|
||||||
### Linux安装
|
|
||||||
|
|
||||||
#### 安装 Linux QQ(22741),已经安装了的可以跳过
|
|
||||||
|
|
||||||
目前还在研究怎么精简安装,暂时只能安装官方QQ整体依赖
|
|
||||||
|
|
||||||
下载QQ
|
|
||||||
|
|
||||||
[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
|
|
||||||
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 本身的问题,不影响使用
|
|
||||||
|
|
||||||
## API 文档
|
|
||||||
|
|
||||||
参考 [LLOneBot](https://llonebot.github.io/zh-CN/develop/api) 的文档
|
|
||||||
|
|
||||||
<!--
|
|
||||||
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
|
||||||
|
-->
|
||||||
|
32
docs/changelogs/CHANGELOG.v1.7.0.md
Normal file
32
docs/changelogs/CHANGELOG.v1.7.0.md
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
# v1.6.8
|
||||||
|
|
||||||
|
QQ Version: Windows 9.9.12-26000 / Linux 3.2.9-26000
|
||||||
|
## 使用前警告
|
||||||
|
1. 在最近版本由于QQ本体大幅变动,为了保证NapCat可用性,NapCat近期启动与安装方式将将大幅变动,请关注文档和社群获取。
|
||||||
|
2. 在Core上完全执行开源,请不要用于违法用途,如此可能造成NapCat完全停止更新。
|
||||||
|
3. 针对原启动方式的围堵,NapCat研发了多种方式,除此其余理论与扩展的分析和思路将部分展示于Docs,以便各位参与开发与维护NapCat。
|
||||||
|
## 其余·备注
|
||||||
|
启动方式: WayBoot.03 (Electron Main进程为Node 直接注入代码 同理项目: LiteLoader)
|
||||||
|
|
||||||
|
## 修复与优化
|
||||||
|
1. 移除数据库文件读写 ~ 优化性能
|
||||||
|
2. 重构消息发送 极限速度优化 ~ 优化性能
|
||||||
|
3. WebUi配置热重载优化 ~ 修复问题
|
||||||
|
4. 修复偶现崩溃问题 ~ 修复问题
|
||||||
|
5. 修复群邀请通知事件多次推送问题 ~ 修复问题
|
||||||
|
6. 尝试修复因缓存引起的字段不全问题 ~ 修复问题
|
||||||
|
7. 修复在非常非常高并发的情况 上报自身消息 回复回错问题 ~ 修复问题
|
||||||
|
8. 修复图片SubType字段位置错误问题 ~ 修复问题
|
||||||
|
9. 修复Uid/Uin转换问题 ~ 修复问题
|
||||||
|
|
||||||
|
## 新增与调整
|
||||||
|
1. 最后发言时间重构 入群时间失效 ~ 替换功能
|
||||||
|
2. 重构文件发送/获取 ~ 优化性能
|
||||||
|
3. 支持GOCQ私聊上传接口 ~ 新增功能
|
||||||
|
4. 悄悄告诉你ws http可以同一个端口 ~ 新增功能
|
||||||
|
5. 根据config目录的默认配置初始化新的配置文件 ~ 新增功能
|
||||||
|
6. WebUi可以部署在nginx代理二级目录 配置端口设置为0可关闭WebUi ~ 新增功能
|
||||||
|
7. 新增收藏表情拉取接口 ~ 新增功能
|
||||||
|
8. 新增群头像设置接口 ~ 新增功能
|
||||||
|
|
||||||
|
新增的 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)
|
11
docs/changelogs/old/CHANGELOG.v1.5.5.md
Normal file
11
docs/changelogs/old/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)
|
11
docs/changelogs/old/CHANGELOG.v1.5.6.md
Normal file
11
docs/changelogs/old/CHANGELOG.v1.5.6.md
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
# v1.5.6
|
||||||
|
|
||||||
|
QQ Version: Windows 9.9.11-24568 / Linux 3.2.9-24568
|
||||||
|
|
||||||
|
## 修复与优化
|
||||||
|
* 修复一些问题
|
||||||
|
|
||||||
|
## 新增与调整
|
||||||
|
|
||||||
|
|
||||||
|
新增的 API 详细见[API文档](https://napneko.github.io/zh-CN/develop/extends_api)
|
11
docs/changelogs/old/CHANGELOG.v1.5.7.md
Normal file
11
docs/changelogs/old/CHANGELOG.v1.5.7.md
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
# v1.5.7
|
||||||
|
|
||||||
|
QQ Version: Windows 9.9.11-24568 / Linux 3.2.9-24568
|
||||||
|
|
||||||
|
## 修复与优化
|
||||||
|
* 修复一些问题
|
||||||
|
|
||||||
|
## 新增与调整
|
||||||
|
|
||||||
|
|
||||||
|
新增的 API 详细见[API文档](https://napneko.github.io/zh-CN/develop/extends_api)
|
14
docs/changelogs/old/CHANGELOG.v1.5.8.md
Normal file
14
docs/changelogs/old/CHANGELOG.v1.5.8.md
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
# v1.5.8
|
||||||
|
|
||||||
|
QQ Version: Windows 9.9.11-24568 / Linux 3.2.9-24568
|
||||||
|
|
||||||
|
## 修复与优化
|
||||||
|
* 修复视频文件残留问题
|
||||||
|
* 重构 getcookies接口 支持大部分常见域
|
||||||
|
|
||||||
|
## 新增与调整
|
||||||
|
* 日志大小限制
|
||||||
|
* 支持 QQ音乐 卡片 无签名支持时 启用内置方法(缺点没有封面 限速1min/条)
|
||||||
|
* 支持Window X86-32机器
|
||||||
|
|
||||||
|
新增的 API 详细见[API文档](https://napneko.github.io/zh-CN/develop/extends_api)
|
12
docs/changelogs/old/CHANGELOG.v1.5.9.md
Normal file
12
docs/changelogs/old/CHANGELOG.v1.5.9.md
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
# v1.5.9
|
||||||
|
|
||||||
|
QQ Version: Windows 9.9.11-24815 / Linux 3.2.9-24815
|
||||||
|
|
||||||
|
## 修复与优化
|
||||||
|
* 优化缓存问题
|
||||||
|
* 修复poke异常上报
|
||||||
|
|
||||||
|
## 新增与调整
|
||||||
|
|
||||||
|
|
||||||
|
新增的 API 详细见[API文档](https://napneko.github.io/zh-CN/develop/extends_api)
|
11
docs/changelogs/old/CHANGELOG.v1.6.0.md
Normal file
11
docs/changelogs/old/CHANGELOG.v1.6.0.md
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
# v1.6.0
|
||||||
|
|
||||||
|
QQ Version: Windows 9.9.11-24815 / Linux 3.2.9-24815
|
||||||
|
|
||||||
|
## 修复与优化
|
||||||
|
|
||||||
|
|
||||||
|
## 新增与调整
|
||||||
|
* 新增图片subtype属性 区分表情图片与商城图片
|
||||||
|
|
||||||
|
新增的 API 详细见[API文档](https://napneko.github.io/zh-CN/develop/extends_api)
|
11
docs/changelogs/old/CHANGELOG.v1.6.1.md
Normal file
11
docs/changelogs/old/CHANGELOG.v1.6.1.md
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
# v1.6.1
|
||||||
|
|
||||||
|
QQ Version: Windows 9.9.11-24815 / Linux 3.2.9-24815
|
||||||
|
|
||||||
|
## 修复与优化
|
||||||
|
|
||||||
|
|
||||||
|
## 新增与调整
|
||||||
|
* 修复poke异常事件
|
||||||
|
|
||||||
|
新增的 API 详细见[API文档](https://napneko.github.io/zh-CN/develop/extends_api)
|
13
docs/changelogs/old/CHANGELOG.v1.6.2.md
Normal file
13
docs/changelogs/old/CHANGELOG.v1.6.2.md
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
# v1.6.2
|
||||||
|
|
||||||
|
QQ Version: Windows 9.9.11-24815 / Linux 3.2.9-24815
|
||||||
|
|
||||||
|
## 修复与优化
|
||||||
|
* 修复获取Cookies异常崩溃问题
|
||||||
|
* 尝试修复成员退群缓存问题
|
||||||
|
* 修复自身退群后群缓存清理问题
|
||||||
|
|
||||||
|
## 新增与调整
|
||||||
|
|
||||||
|
|
||||||
|
新增的 API 详细见[API文档](https://napneko.github.io/zh-CN/develop/extends_api)
|
13
docs/changelogs/old/CHANGELOG.v1.6.3.md
Normal file
13
docs/changelogs/old/CHANGELOG.v1.6.3.md
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
# v1.6.3
|
||||||
|
|
||||||
|
QQ Version: Windows 9.9.11-24815 / Linux 3.2.9-24815
|
||||||
|
|
||||||
|
## 修复与优化
|
||||||
|
* 修复带有groupid的私聊消息异常发送到群聊消息
|
||||||
|
* 尝试修复rws热重载失效问题
|
||||||
|
* 尝试修复进群事件无法正常获取uin
|
||||||
|
|
||||||
|
## 新增与调整
|
||||||
|
|
||||||
|
|
||||||
|
新增的 API 详细见[API文档](https://napneko.github.io/zh-CN/develop/extends_api)
|
18
docs/changelogs/old/CHANGELOG.v1.6.4.md
Normal file
18
docs/changelogs/old/CHANGELOG.v1.6.4.md
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
# v1.6.4
|
||||||
|
|
||||||
|
QQ Version: Windows 9.9.12-26000 / Linux 3.2.9-26000
|
||||||
|
## 使用前警告
|
||||||
|
1. 在最近版本由于QQ本体大幅变动,为了保证NapCat可用性,NapCat近期启动与安装方式将将大幅变动,请关注文档和社群获取。
|
||||||
|
2. 在Core上完全执行开源,请不要用于违法用途,如此可能造成NapCat完全停止更新。
|
||||||
|
3. 针对原启动方式的围堵,NapCat研发了多种方式,除此其余理论与扩展的分析和思路将部分展示于Docs,以便各位参与开发与维护NapCat。
|
||||||
|
## 其余·备注
|
||||||
|
启动方式: WayBoot.03 (Electron Main进程为Node 直接注入代码 同理项目: LiteLoader)
|
||||||
|
|
||||||
|
## 修复与优化
|
||||||
|
1. 支持Win平台 9.9.12
|
||||||
|
2. 修复部分发送图片下载异常情况
|
||||||
|
|
||||||
|
## 新增与调整
|
||||||
|
没有哦
|
||||||
|
|
||||||
|
新增的 API 详细见[API文档](https://napneko.github.io/zh-CN/develop/extends_api)
|
18
docs/changelogs/old/CHANGELOG.v1.6.5.md
Normal file
18
docs/changelogs/old/CHANGELOG.v1.6.5.md
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
# v1.6.5
|
||||||
|
|
||||||
|
QQ Version: Windows 9.9.12-26000 / Linux 3.2.9-26000
|
||||||
|
## 使用前警告
|
||||||
|
1. 在最近版本由于QQ本体大幅变动,为了保证NapCat可用性,NapCat近期启动与安装方式将将大幅变动,请关注文档和社群获取。
|
||||||
|
2. 在Core上完全执行开源,请不要用于违法用途,如此可能造成NapCat完全停止更新。
|
||||||
|
3. 针对原启动方式的围堵,NapCat研发了多种方式,除此其余理论与扩展的分析和思路将部分展示于Docs,以便各位参与开发与维护NapCat。
|
||||||
|
## 其余·备注
|
||||||
|
启动方式: WayBoot.03 (Electron Main进程为Node 直接注入代码 同理项目: LiteLoader)
|
||||||
|
|
||||||
|
## 修复与优化
|
||||||
|
1. 优化了WrapperNative载入代码
|
||||||
|
2. 优化缓存
|
||||||
|
|
||||||
|
## 新增与调整
|
||||||
|
没有哦
|
||||||
|
|
||||||
|
新增的 API 详细见[API文档](https://napneko.github.io/zh-CN/develop/extends_api)
|
17
docs/changelogs/old/CHANGELOG.v1.6.6.md
Normal file
17
docs/changelogs/old/CHANGELOG.v1.6.6.md
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
# v1.6.6
|
||||||
|
|
||||||
|
QQ Version: Windows 9.9.12-26000 / Linux 3.2.9-26000
|
||||||
|
## 使用前警告
|
||||||
|
1. 在最近版本由于QQ本体大幅变动,为了保证NapCat可用性,NapCat近期启动与安装方式将将大幅变动,请关注文档和社群获取。
|
||||||
|
2. 在Core上完全执行开源,请不要用于违法用途,如此可能造成NapCat完全停止更新。
|
||||||
|
3. 针对原启动方式的围堵,NapCat研发了多种方式,除此其余理论与扩展的分析和思路将部分展示于Docs,以便各位参与开发与维护NapCat。
|
||||||
|
## 其余·备注
|
||||||
|
启动方式: WayBoot.03 (Electron Main进程为Node 直接注入代码 同理项目: LiteLoader)
|
||||||
|
|
||||||
|
## 修复与优化
|
||||||
|
1. 修复了一些问题
|
||||||
|
|
||||||
|
## 新增与调整
|
||||||
|
没有哦
|
||||||
|
|
||||||
|
新增的 API 详细见[API文档](https://napneko.github.io/zh-CN/develop/extends_api)
|
49
docs/develop/Image.NTAndroid.md
Normal file
49
docs/develop/Image.NTAndroid.md
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
|
||||||
|
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;
|
||||||
|
```
|
16
docs/develop/NC 1.6.X的计划.md
Normal file
16
docs/develop/NC 1.6.X的计划.md
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
# 开发方向
|
||||||
|
方向一 NativeCall/Hook:
|
||||||
|
1. 崩溃检测机制的实现
|
||||||
|
2. Api_Caller 的Hook 可以拿到Event/Handler 进一步提升NC 即时的拦截与处理一些事件比如ReCall拦截
|
||||||
|
3. Node包装层 进一步分析,拿到脱离自带Listener/Adapter,可以拿到一些更加底层的数据变动 或许包括更多二进制数据
|
||||||
|
|
||||||
|
方向二 全新的无头启动 Way01
|
||||||
|
1. 基于Node启动原理,借助导出符号获取函数地址 再次还原NodeMain
|
||||||
|
|
||||||
|
方向三 发包与收包
|
||||||
|
1. 参考 方向一/3 大概可以收包
|
||||||
|
2. 发包 (暂时没有计划)
|
||||||
|
|
||||||
|
方向四 版本控制
|
||||||
|
1. 根据不同版本进行逻辑控制
|
||||||
|
2. 某些参数的自动提取
|
8
docs/develop/碎碎的研究记录.md
Normal file
8
docs/develop/碎碎的研究记录.md
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
# Api方向
|
||||||
|
## getMsgUniqueId √ 已应用
|
||||||
|
getMsgUniqueId 传入时间 产出一个唯一ID 发送消息作为一个参数
|
||||||
|
|
||||||
|
# Native方向
|
||||||
|
## magic_load
|
||||||
|
## api_caller
|
||||||
|
## NodeMain
|
133
package.json
133
package.json
@@ -1,63 +1,70 @@
|
|||||||
{
|
{
|
||||||
"name": "napcat",
|
"name": "napcat",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"version": "1.0.2",
|
"version": "1.7.2",
|
||||||
"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",
|
"@babel/core": "^7.24.7",
|
||||||
"@rollup/plugin-node-resolve": "^15.2.3",
|
"@babel/plugin-proposal-class-properties": "^7.18.6",
|
||||||
"@rollup/plugin-typescript": "^11.1.6",
|
"@babel/plugin-proposal-decorators": "^7.24.7",
|
||||||
"@types/express": "^4.17.21",
|
"@babel/preset-typescript": "^7.24.7",
|
||||||
"@types/figlet": "^1.5.8",
|
"@log4js-node/log4js-api": "^1.0.2",
|
||||||
"@types/fluent-ffmpeg": "^2.1.24",
|
"@protobuf-ts/plugin": "^2.9.4",
|
||||||
"@types/node": "^20.11.30",
|
"@rollup/plugin-node-resolve": "^15.2.3",
|
||||||
"@types/qrcode-terminal": "^0.12.2",
|
"@rollup/plugin-typescript": "^11.1.6",
|
||||||
"@types/uuid": "^9.0.8",
|
"@types/cors": "^2.8.17",
|
||||||
"@types/ws": "^8.5.10",
|
"@types/express": "^4.17.21",
|
||||||
"@typescript-eslint/eslint-plugin": "^7.4.0",
|
"@types/figlet": "^1.5.8",
|
||||||
"@typescript-eslint/parser": "^7.4.0",
|
"@types/fluent-ffmpeg": "^2.1.24",
|
||||||
"eslint": "^8.57.0",
|
"@types/jest": "^29.5.12",
|
||||||
"eslint-import-resolver-typescript": "^3.6.1",
|
"@types/node": "^22.0.0",
|
||||||
"eslint-plugin-import": "^2.29.1",
|
"@types/qrcode-terminal": "^0.12.2",
|
||||||
"i": "^0.3.7",
|
"@types/ws": "^8.5.10",
|
||||||
"javascript-obfuscator": "^4.1.0",
|
"@typescript-eslint/eslint-plugin": "^7.4.0",
|
||||||
"protobufjs-cli": "^1.1.2",
|
"@typescript-eslint/parser": "^7.4.0",
|
||||||
"rollup": "^4.13.2",
|
"eslint": "^8.57.0",
|
||||||
"rollup-plugin-dts": "^6.1.0",
|
"eslint-import-resolver-typescript": "^3.6.1",
|
||||||
"rollup-plugin-obfuscator": "^1.1.0",
|
"eslint-plugin-import": "^2.29.1",
|
||||||
"typescript": "^5.3.3",
|
"i": "^0.3.7",
|
||||||
"vite": "^5.2.6",
|
"javascript-obfuscator": "^4.1.0",
|
||||||
"vite-plugin-cp": "^4.0.8",
|
"rollup": "^4.13.2",
|
||||||
"vite-plugin-dts": "^3.8.2",
|
"rollup-plugin-dts": "^6.1.0",
|
||||||
"vite-tsconfig-paths": "^4.3.2"
|
"rollup-plugin-obfuscator": "^1.1.0",
|
||||||
},
|
"typescript": "^5.3.3",
|
||||||
"dependencies": {
|
"vite": "^5.2.6",
|
||||||
"commander": "^12.0.0",
|
"vite-plugin-babel": "^1.2.0",
|
||||||
"express": "^5.0.0-beta.2",
|
"vite-plugin-cp": "^4.0.8",
|
||||||
"file-type": "^19.0.0",
|
"vite-plugin-dts": "^3.8.2",
|
||||||
"fluent-ffmpeg": "^2.1.2",
|
"vite-tsconfig-paths": "^4.3.2"
|
||||||
"image-size": "^1.1.1",
|
},
|
||||||
"log4js": "^6.9.1",
|
"dependencies": {
|
||||||
"protobufjs": "^7.2.6",
|
"ajv": "^8.13.0",
|
||||||
"qrcode-terminal": "^0.12.0",
|
"chalk": "^5.3.0",
|
||||||
"silk-wasm": "^3.3.4",
|
"commander": "^12.0.0",
|
||||||
"sqlite3": "^5.1.7",
|
"cors": "^2.8.5",
|
||||||
"uuid": "^9.0.1",
|
"express": "^5.0.0-beta.2",
|
||||||
"ws": "^8.16.0",
|
"fast-xml-parser": "^4.3.6",
|
||||||
"yaml": "^2.4.1"
|
"file-type": "^19.0.0",
|
||||||
}
|
"fluent-ffmpeg": "^2.1.2",
|
||||||
}
|
"image-size": "^1.1.1",
|
||||||
|
"json-schema-to-ts": "^3.1.0",
|
||||||
|
"log4js": "^6.9.1",
|
||||||
|
"qrcode-terminal": "^0.12.0",
|
||||||
|
"silk-wasm": "^3.6.1",
|
||||||
|
"ws": "^8.16.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
45
script/BootWay.03.ps1
Normal file
45
script/BootWay.03.ps1
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
# Dont Use This Script
|
||||||
|
# 2024.7.3
|
||||||
|
function Get-QQpath {
|
||||||
|
try {
|
||||||
|
$key = Get-ItemProperty -Path "HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\QQ"
|
||||||
|
$uninstallString = $key.UninstallString
|
||||||
|
return [System.IO.Path]::GetDirectoryName($uninstallString) + "\QQ.exe"
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
throw "get QQ path error: $_"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function Select-QQPath {
|
||||||
|
Add-Type -AssemblyName System.Windows.Forms
|
||||||
|
[System.Windows.Forms.Application]::EnableVisualStyles()
|
||||||
|
|
||||||
|
$dialogTitle = "Select QQ.exe"
|
||||||
|
|
||||||
|
$filePicker = New-Object System.Windows.Forms.OpenFileDialog
|
||||||
|
$filePicker.Title = $dialogTitle
|
||||||
|
$filePicker.Filter = "Executable Files (*.exe)|*.exe|All Files (*.*)|*.*"
|
||||||
|
$filePicker.FilterIndex = 1
|
||||||
|
$null = $filePicker.ShowDialog()
|
||||||
|
if (-not ($filePicker.FileName)) {
|
||||||
|
throw "User did not select an .exe file."
|
||||||
|
}
|
||||||
|
return $filePicker.FileName
|
||||||
|
}
|
||||||
|
|
||||||
|
$params = $args -join " "
|
||||||
|
Try {
|
||||||
|
$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
|
||||||
|
$commandInfo = Get-Command $QQpath -ErrorAction Stop
|
||||||
|
Start-Process powershell -ArgumentList "-noexit", "-noprofile", "-command &{& chcp 65001;& '$($commandInfo.Path)' --enable-logging }"
|
123
script/BootWay05.ps1
Normal file
123
script/BootWay05.ps1
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
# 检查当前会话是否具有管理员权限
|
||||||
|
function Test-Administrator {
|
||||||
|
$user = [Security.Principal.WindowsIdentity]::GetCurrent()
|
||||||
|
(New-Object Security.Principal.WindowsPrincipal $user).IsInRole([Security.Principal.WindowsBuiltinRole]::Administrator)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (-not (Test-Administrator)) {
|
||||||
|
# 如果不是管理员,则重新启动脚本以管理员模式运行
|
||||||
|
$scriptPath = $myInvocation.MyCommand.Path
|
||||||
|
if (-not $scriptPath) {
|
||||||
|
$scriptPath = $PSCommandPath
|
||||||
|
}
|
||||||
|
$newProcess = New-Object System.Diagnostics.ProcessStartInfo "powershell";
|
||||||
|
$newProcess.Arguments = "-File `"$scriptPath`" $args"
|
||||||
|
$newProcess.Verb = "runas";
|
||||||
|
[System.Diagnostics.Process]::Start($newProcess);
|
||||||
|
exit
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-QQpath {
|
||||||
|
try {
|
||||||
|
$key = Get-ItemProperty -Path "HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\QQ"
|
||||||
|
$uninstallString = $key.UninstallString
|
||||||
|
return [System.IO.Path]::GetDirectoryName($uninstallString) + "\QQ.exe"
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
throw "get QQ path error: $_"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function Select-QQPath {
|
||||||
|
Add-Type -AssemblyName System.Windows.Forms
|
||||||
|
[System.Windows.Forms.Application]::EnableVisualStyles()
|
||||||
|
|
||||||
|
$dialogTitle = "Select QQ.exe"
|
||||||
|
|
||||||
|
$filePicker = New-Object System.Windows.Forms.OpenFileDialog
|
||||||
|
$filePicker.Title = $dialogTitle
|
||||||
|
$filePicker.Filter = "Executable Files (*.exe)|*.exe|All Files (*.*)|*.*"
|
||||||
|
$filePicker.FilterIndex = 1
|
||||||
|
$null = $filePicker.ShowDialog()
|
||||||
|
if (-not ($filePicker.FileName)) {
|
||||||
|
throw "User did not select an .exe file."
|
||||||
|
}
|
||||||
|
return $filePicker.FileName
|
||||||
|
}
|
||||||
|
|
||||||
|
# 设置当前工作目录
|
||||||
|
$scriptDirectory = Split-Path -Path $MyInvocation.MyCommand.Definition -Parent
|
||||||
|
Set-Location $scriptDirectory
|
||||||
|
|
||||||
|
# 获取当前目录路径
|
||||||
|
$currentPath = Get-Location
|
||||||
|
|
||||||
|
# 替换\为/
|
||||||
|
$currentPath = $currentPath -replace '\\', '/'
|
||||||
|
|
||||||
|
# 生成JavaScript代码
|
||||||
|
$jsCode = @"
|
||||||
|
(async () => {
|
||||||
|
await import('file:///$currentPath/napcat.mjs');
|
||||||
|
})();
|
||||||
|
"@
|
||||||
|
|
||||||
|
# 将JavaScript代码保存到文件中
|
||||||
|
$jsFilePath = Join-Path $currentPath "loadScript.js"
|
||||||
|
$jsCode | Out-File -FilePath $jsFilePath -Encoding UTF8
|
||||||
|
|
||||||
|
Write-Output "JavaScript code has been generated and saved to $jsFilePath"
|
||||||
|
# 设置NAPCAT_PATH环境变量为 当前目录的loadScript.js地址
|
||||||
|
$env:NAPCAT_PATH = $jsFilePath
|
||||||
|
|
||||||
|
$params = $args -join " "
|
||||||
|
Try {
|
||||||
|
$QQpath = Get-QQpath
|
||||||
|
}
|
||||||
|
Catch {
|
||||||
|
$QQpath = Select-QQPath
|
||||||
|
}
|
||||||
|
# 拿不到QQ路径则退出
|
||||||
|
if (!(Test-Path $QQpath)) {
|
||||||
|
Write-Output "provided QQ path is invalid: $QQpath"
|
||||||
|
Read-Host "Press any key to continue..."
|
||||||
|
exit
|
||||||
|
}
|
||||||
|
|
||||||
|
$commandInfo = Get-Command $QQpath -ErrorAction Stop
|
||||||
|
|
||||||
|
# 收集dbghelp.dll路径和HASH信息
|
||||||
|
$QQpath = Split-Path $QQpath
|
||||||
|
$oldDllPath = Join-Path $QQpath "dbghelp.dll"
|
||||||
|
$oldDllHash = Get-FileHash $oldDllPath -Algorithm MD5
|
||||||
|
$newDllPath = Join-Path $currentPath "dbghelp.dll"
|
||||||
|
$newDllHash = Get-FileHash $newDllPath -Algorithm MD5
|
||||||
|
# 如果文件一致则跳过
|
||||||
|
if ($oldDllHash.Hash -ne $newDllHash.Hash) {
|
||||||
|
$processes = Get-Process -Name QQ -ErrorAction SilentlyContinue
|
||||||
|
if ($processes) {
|
||||||
|
# 文件占用则退出
|
||||||
|
Write-Output "dbghelp.dll is in use by the following processes:"
|
||||||
|
$processes | ForEach-Object { Write-Output "$($_.Id) $($_.Name) $($_.Path)" }
|
||||||
|
Write-Output "dbghelp.dll is in use, cannot continue."
|
||||||
|
Read-Host "Press any key to continue..."
|
||||||
|
exit
|
||||||
|
} else {
|
||||||
|
# 文件未占用则尝试覆盖
|
||||||
|
try {
|
||||||
|
Copy-Item -Path "$newDllPath" -Destination "$oldDllPath" -Force
|
||||||
|
Write-Output "dbghelp.dll has been copied to $QQpath"
|
||||||
|
} catch {
|
||||||
|
Write-Output "Failed to copy dbghelp.dll: $_"
|
||||||
|
Read-Host "Press any key to continue..."
|
||||||
|
exit
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# 带参数启动QQ
|
||||||
|
try {
|
||||||
|
Start-Process powershell -ArgumentList '-noexit', '-noprofile', "-command &{& chcp 65001;& '$($commandInfo.Path)' --enable-logging $params}" -NoNewWindow -ErrorAction Stop
|
||||||
|
} catch {
|
||||||
|
Write-Output "Failed to start process as administrator: $_"
|
||||||
|
Read-Host "Press any key to continue..."
|
||||||
|
}
|
28
script/NapCat.164.bat
Normal file
28
script/NapCat.164.bat
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
@echo off
|
||||||
|
chcp 65001
|
||||||
|
:: 检查是否有管理员权限
|
||||||
|
net session >nul 2>&1
|
||||||
|
if %errorlevel% neq 0 (
|
||||||
|
echo 请求管理员权限...
|
||||||
|
powershell -Command "Start-Process '%~f0' -Verb runAs"
|
||||||
|
exit /b
|
||||||
|
)
|
||||||
|
:: 如果有管理员权限,继续执行
|
||||||
|
setlocal enabledelayedexpansion
|
||||||
|
:loop_read
|
||||||
|
for /f "tokens=2*" %%a in ('reg query "HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\QQ" /v "UninstallString"') do (
|
||||||
|
set "RetString=%%b"
|
||||||
|
goto :napcat_boot
|
||||||
|
)
|
||||||
|
|
||||||
|
:napcat_boot
|
||||||
|
for %%a in ("!RetString!") do (
|
||||||
|
set "pathWithoutUninstall=%%~dpa"
|
||||||
|
)
|
||||||
|
|
||||||
|
set "QQPath=!pathWithoutUninstall!QQ.exe"
|
||||||
|
|
||||||
|
echo !QQPath!
|
||||||
|
"!QQPath!" --enable-logging %*
|
||||||
|
|
||||||
|
pause
|
3
script/NapCat.Way01.bat
Normal file
3
script/NapCat.Way01.bat
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
REM 全新启动脚本 基于 Hook Native 预计版本1.6.0左右发布
|
||||||
|
@echo off
|
||||||
|
pause
|
@@ -1,15 +1,42 @@
|
|||||||
let fs = require("fs");
|
const fs = require("fs");
|
||||||
let process = require("process")
|
const process = require("process");
|
||||||
|
|
||||||
console.log("[NapCat] [CheckVersion] 开始检测当前仓库版本...");
|
console.log("[NapCat] [CheckVersion] 开始检测当前仓库版本...");
|
||||||
let currentVersion = require("../package.json").version;
|
try {
|
||||||
let targetVersion = process.env.VERSION;
|
const packageJson = require("../package.json");
|
||||||
console.log("[NapCat] [CheckVersion] currentVersion:", currentVersion, " targetVersion:", targetVersion);
|
const currentVersion = packageJson.version;
|
||||||
// fs.mkdirSync("./dist");
|
const targetVersion = process.env.VERSION;
|
||||||
if (currentVersion === targetVersion) {
|
|
||||||
fs.appendFileSync("../checkVersion.sh", "#!/bin/bashe\necho \"CheckVersion Is Done\"")
|
console.log("[NapCat] [CheckVersion] currentVersion:", currentVersion, "targetVersion:", targetVersion);
|
||||||
} else {
|
|
||||||
let packageJson = JSON.parse(fs.readFileSync("./package.json"));
|
// 验证 targetVersion 格式
|
||||||
packageJson.version = targetVersion;
|
if (!targetVersion || typeof targetVersion !== 'string') {
|
||||||
fs.writeFileSync("../package.json", JSON.stringify(packageJson));
|
console.error("[NapCat] [CheckVersion] 目标版本格式不正确或未设置!");
|
||||||
fs.appendFileSync("../checkVersion.sh", "#!/bin/bashe\ngit add .\n git commit -m \"chore:version change\"\n git push")
|
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);
|
BIN
script/dbghelp.dll
Normal file
BIN
script/dbghelp.dll
Normal file
Binary file not shown.
20
script/index.js
Normal file
20
script/index.js
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
// --------------------
|
||||||
|
// 2024.7.3 9.9.12 BootWay.03 其余方法暂不公开(此方案为临时方案 Win平台已验证)
|
||||||
|
// 缺陷 (已知)
|
||||||
|
// 1.与非入侵式不同 现在破坏本体代码
|
||||||
|
// 2.重启代码与正常启动代码失效
|
||||||
|
// 3.Win需要补丁
|
||||||
|
// 4.更新后丢失内容 需要重写此文件
|
||||||
|
// 5.安装难度上升与周围基础设施失效
|
||||||
|
// --------------------
|
||||||
|
|
||||||
|
const path = require('path');
|
||||||
|
const CurrentPath = path.dirname(__filename)
|
||||||
|
const hasNapcatParam = process.argv.includes('--enable-logging');
|
||||||
|
if (hasNapcatParam) {
|
||||||
|
(async () => {
|
||||||
|
await import("file://" + path.join(CurrentPath, './napcat/napcat.mjs'));
|
||||||
|
})();
|
||||||
|
} else {
|
||||||
|
require('./launcher.node').load('external_index', module);
|
||||||
|
}
|
18
script/napcat-9912-utf8.bat
Normal file
18
script/napcat-9912-utf8.bat
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
@echo off
|
||||||
|
setlocal enabledelayedexpansion
|
||||||
|
chcp 65001
|
||||||
|
:loop_read
|
||||||
|
for /f "tokens=2*" %%a in ('reg query "HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\QQ" /v "UninstallString"') do (
|
||||||
|
set "RetString=%%b"
|
||||||
|
goto :napcat_boot
|
||||||
|
)
|
||||||
|
|
||||||
|
:napcat_boot
|
||||||
|
for %%a in ("!RetString!") do (
|
||||||
|
set "pathWithoutUninstall=%%~dpa"
|
||||||
|
)
|
||||||
|
|
||||||
|
set "QQPath=!pathWithoutUninstall!"
|
||||||
|
cd /d !QQPath!
|
||||||
|
echo !QQPath!
|
||||||
|
QQ.exe --enable-logging %*
|
41
script/napcat-9912-utf8.ps1
Normal file
41
script/napcat-9912-utf8.ps1
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
function Get-QQpath {
|
||||||
|
try {
|
||||||
|
$key = Get-ItemProperty -Path "HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\QQ"
|
||||||
|
$uninstallString = $key.UninstallString
|
||||||
|
return [System.IO.Path]::GetDirectoryName($uninstallString) + "\"
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
throw "get QQ path error: $_"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function Select-QQPath {
|
||||||
|
Add-Type -AssemblyName System.Windows.Forms
|
||||||
|
[System.Windows.Forms.Application]::EnableVisualStyles()
|
||||||
|
|
||||||
|
$dialogTitle = "Select QQ.exe"
|
||||||
|
|
||||||
|
$filePicker = New-Object System.Windows.Forms.OpenFileDialog
|
||||||
|
$filePicker.Title = $dialogTitle
|
||||||
|
$filePicker.Filter = "Executable Files (*.exe)|*.exe|All Files (*.*)|*.*"
|
||||||
|
$filePicker.FilterIndex = 1
|
||||||
|
$null = $filePicker.ShowDialog()
|
||||||
|
if (-not ($filePicker.FileName)) {
|
||||||
|
throw "User did not select an .exe file."
|
||||||
|
}
|
||||||
|
return $filePicker.FileName
|
||||||
|
}
|
||||||
|
|
||||||
|
$params = $args -join " "
|
||||||
|
Try {
|
||||||
|
$QQpath = Get-QQpath
|
||||||
|
}
|
||||||
|
Catch {
|
||||||
|
$QQpath = Select-QQPath
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(Test-Path $QQpath)) {
|
||||||
|
throw "provided QQ path is invalid: $QQpath"
|
||||||
|
}
|
||||||
|
|
||||||
|
Set-Location -Path $QQpath
|
||||||
|
Start-Process powershell -ArgumentList "-noexit", "-noprofile", "-command &{& chcp 65001;& ./QQ.exe --enable-logging $params}"
|
17
script/napcat-9912.bat
Normal file
17
script/napcat-9912.bat
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
@echo off
|
||||||
|
setlocal enabledelayedexpansion
|
||||||
|
:loop_read
|
||||||
|
for /f "tokens=2*" %%a in ('reg query "HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\QQ" /v "UninstallString"') do (
|
||||||
|
set "RetString=%%b"
|
||||||
|
goto :napcat_boot
|
||||||
|
)
|
||||||
|
|
||||||
|
:napcat_boot
|
||||||
|
for %%a in ("!RetString!") do (
|
||||||
|
set "pathWithoutUninstall=%%~dpa"
|
||||||
|
)
|
||||||
|
|
||||||
|
set QQPath=!pathWithoutUninstall!
|
||||||
|
cd /d !QQPath!
|
||||||
|
echo !QQPath!
|
||||||
|
QQ.exe --enable-logging %*
|
41
script/napcat-9912.ps1
Normal file
41
script/napcat-9912.ps1
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
function Get-QQpath {
|
||||||
|
try {
|
||||||
|
$key = Get-ItemProperty -Path "HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\QQ"
|
||||||
|
$uninstallString = $key.UninstallString
|
||||||
|
return [System.IO.Path]::GetDirectoryName($uninstallString) + "\"
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
throw "get QQ path error: $_"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function Select-QQPath {
|
||||||
|
Add-Type -AssemblyName System.Windows.Forms
|
||||||
|
[System.Windows.Forms.Application]::EnableVisualStyles()
|
||||||
|
|
||||||
|
$dialogTitle = "Select QQ.exe"
|
||||||
|
|
||||||
|
$filePicker = New-Object System.Windows.Forms.OpenFileDialog
|
||||||
|
$filePicker.Title = $dialogTitle
|
||||||
|
$filePicker.Filter = "Executable Files (*.exe)|*.exe|All Files (*.*)|*.*"
|
||||||
|
$filePicker.FilterIndex = 1
|
||||||
|
$null = $filePicker.ShowDialog()
|
||||||
|
if (-not ($filePicker.FileName)) {
|
||||||
|
throw "User did not select an .exe file."
|
||||||
|
}
|
||||||
|
return $filePicker.FileName
|
||||||
|
}
|
||||||
|
|
||||||
|
$params = $args -join " "
|
||||||
|
Try {
|
||||||
|
$QQpath = Get-QQpath
|
||||||
|
}
|
||||||
|
Catch {
|
||||||
|
$QQpath = Select-QQPath
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(Test-Path $QQpath)) {
|
||||||
|
throw "provided QQ path is invalid: $QQpath"
|
||||||
|
}
|
||||||
|
|
||||||
|
Set-Location -Path $QQpath
|
||||||
|
Start-Process powershell -ArgumentList "-noexit", "-noprofile", "-command &{& ./QQ.exe --enable-logging $params}"
|
@@ -15,4 +15,4 @@ for %%a in ("!RetString!") do (
|
|||||||
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}"
|
@@ -14,4 +14,4 @@ for %%a in ("!RetString!") do (
|
|||||||
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,27 @@
|
|||||||
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;
|
||||||
|
|
||||||
|
public get server(): http.Server | null {
|
||||||
|
return this._server;
|
||||||
|
}
|
||||||
|
|
||||||
|
private set server(value: http.Server | null) {
|
||||||
|
this._server = value;
|
||||||
|
}
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.expressAPP = express();
|
this.expressAPP = express();
|
||||||
|
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 +31,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 +45,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 +61,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 +96,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 +104,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 +116,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());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
import { WebSocket, WebSocketServer } from 'ws';
|
import { WebSocket, WebSocketServer } from 'ws';
|
||||||
|
import http from 'http';
|
||||||
import urlParse from 'url';
|
import urlParse from 'url';
|
||||||
import { IncomingMessage } from 'node:http';
|
import { IncomingMessage } from 'node:http';
|
||||||
import { log } from '@/common/utils/log';
|
import { log } from '@/common/utils/log';
|
||||||
@@ -27,11 +28,36 @@ export class WebsocketServerBase {
|
|||||||
constructor() {
|
constructor() {
|
||||||
}
|
}
|
||||||
|
|
||||||
start(port: number) {
|
start(port: number | http.Server, host: string = '') {
|
||||||
try {
|
if (port instanceof http.Server) {
|
||||||
this.ws = new WebSocketServer({ port });
|
try {
|
||||||
} catch (e: any) {
|
const wss = new WebSocketServer({
|
||||||
throw Error('ws服务启动失败, ' + e.toString());
|
noServer: true,
|
||||||
|
maxPayload: 1024 * 1024 * 1024
|
||||||
|
}).on('error', () => {
|
||||||
|
});
|
||||||
|
this.ws = wss;
|
||||||
|
port.on('upgrade', function upgrade(request, socket, head) {
|
||||||
|
wss.handleUpgrade(request, socket, head, function done(ws) {
|
||||||
|
wss.emit('connection', ws, request);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
log('ws服务启动成功, 绑定到HTTP服务');
|
||||||
|
} catch (e: any) {
|
||||||
|
throw Error('ws服务启动失败, 可能是绑定的HTTP服务异常' + e.toString());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
this.ws = new WebSocketServer({
|
||||||
|
port,
|
||||||
|
host: '',
|
||||||
|
maxPayload: 1024 * 1024 * 1024
|
||||||
|
}).on('error', () => {
|
||||||
|
});
|
||||||
|
log(`ws服务启动成功, ${host}:${port}`);
|
||||||
|
} catch (e: any) {
|
||||||
|
throw Error('ws服务启动失败, 请检查监听的ip和端口' + e.toString());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
this.ws.on('connection', (wsClient, req) => {
|
this.ws.on('connection', (wsClient, req) => {
|
||||||
const url: string = req.url!.split('?').shift() || '/';
|
const url: string = req.url!.split('?').shift() || '/';
|
||||||
@@ -44,10 +70,12 @@ export class WebsocketServerBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
stop() {
|
stop() {
|
||||||
this.ws && this.ws.close((err) => {
|
if (this.ws) {
|
||||||
log('ws server close failed!', err);
|
this.ws.close((err) => {
|
||||||
});
|
if (err) log('ws server close failed!', err);
|
||||||
this.ws = null;
|
});
|
||||||
|
this.ws = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
restart(port: number) {
|
restart(port: number) {
|
||||||
|
89
src/common/utils/ConfigBase.ts
Normal file
89
src/common/utils/ConfigBase.ts
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
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';
|
||||||
|
import { selfInfo } from '@/core/data';
|
||||||
|
|
||||||
|
|
||||||
|
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> {
|
||||||
|
public name: string = 'default_config';
|
||||||
|
private pathName: string | null = null; // 本次读取的文件路径
|
||||||
|
constructor() {
|
||||||
|
}
|
||||||
|
|
||||||
|
protected getKeys(): string[] | null {
|
||||||
|
// 决定 key 在json配置文件中的顺序
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
getConfigDir() {
|
||||||
|
const configDir = path.resolve(__dirname, 'config');
|
||||||
|
fs.mkdirSync(configDir, { recursive: true });
|
||||||
|
return configDir;
|
||||||
|
}
|
||||||
|
getConfigPath(pathName: string | null): string {
|
||||||
|
const suffix = pathName ? `_${pathName}` : '';
|
||||||
|
const filename = `${this.name}${suffix}.json`;
|
||||||
|
return path.join(this.getConfigDir(), filename);
|
||||||
|
}
|
||||||
|
read() {
|
||||||
|
// 尝试加载当前账号配置
|
||||||
|
if (this.read_from_file(selfInfo.uin, false)) return this;
|
||||||
|
// 尝试加载默认配置
|
||||||
|
return this.read_from_file('', true);
|
||||||
|
}
|
||||||
|
read_from_file(pathName: string, createIfNotExist: boolean) {
|
||||||
|
const configPath = this.getConfigPath(pathName);
|
||||||
|
if (!fs.existsSync(configPath)) {
|
||||||
|
if (!createIfNotExist) return null;
|
||||||
|
this.pathName = pathName; // 记录有效的设置文件
|
||||||
|
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, overwrite: boolean = false) {
|
||||||
|
Object.assign(this, config);
|
||||||
|
if (overwrite) {
|
||||||
|
// 用户要求强制写入,则变更当前文件为目标文件
|
||||||
|
this.pathName = `${selfInfo.uin}`;
|
||||||
|
}
|
||||||
|
const configPath = this.getConfigPath(this.pathName);
|
||||||
|
try {
|
||||||
|
fs.writeFileSync(configPath, JSON.stringify(this, this.getKeys(), 2));
|
||||||
|
} catch (e: any) {
|
||||||
|
logError(`保存配置文件 ${configPath} 时发生错误:`, e.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
227
src/common/utils/EventTask.ts
Normal file
227
src/common/utils/EventTask.ts
Normal file
@@ -0,0 +1,227 @@
|
|||||||
|
import { NodeIQQNTWrapperSession } from '@/core/wrapper';
|
||||||
|
import { randomUUID } from 'crypto';
|
||||||
|
|
||||||
|
interface Internal_MapKey {
|
||||||
|
timeout: number,
|
||||||
|
createtime: number,
|
||||||
|
func: (...arg: any[]) => any,
|
||||||
|
checker: ((...args: any[]) => boolean) | undefined,
|
||||||
|
}
|
||||||
|
|
||||||
|
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, ListenerSubName, ...args,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;
|
||||||
|
}
|
||||||
|
if (task.checker && task.checker(...args)) {
|
||||||
|
task.func(...args);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
async CallNoListenerEvent<EventType extends (...args: any[]) => Promise<any> | any>(EventName = '', timeout: number = 3000, ...args: Parameters<EventType>) {
|
||||||
|
return new Promise<Awaited<ReturnType<EventType>>>(async (resolve, reject) => {
|
||||||
|
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 RegisterListen<ListenerType extends (...args: any[]) => void>(ListenerName = '', waitTimes = 1, timeout = 5000, checker: (...args: Parameters<ListenerType>) => boolean) {
|
||||||
|
return new Promise<Parameters<ListenerType>>((resolve, reject) => {
|
||||||
|
const ListenerNameList = ListenerName.split('/');
|
||||||
|
const ListenerMainName = ListenerNameList[0];
|
||||||
|
const ListenerSubName = ListenerNameList[1];
|
||||||
|
const id = randomUUID();
|
||||||
|
let complete = 0;
|
||||||
|
let retData: Parameters<ListenerType> | undefined = undefined;
|
||||||
|
const databack = () => {
|
||||||
|
if (complete == 0) {
|
||||||
|
reject(new Error(' ListenerName:' + ListenerName + ' timeout'));
|
||||||
|
} else {
|
||||||
|
resolve(retData!);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const Timeouter = setTimeout(databack, timeout);
|
||||||
|
const eventCallbak = {
|
||||||
|
timeout: timeout,
|
||||||
|
createtime: Date.now(),
|
||||||
|
checker: checker,
|
||||||
|
func: (...args: Parameters<ListenerType>) => {
|
||||||
|
complete++;
|
||||||
|
retData = args;
|
||||||
|
if (complete >= waitTimes) {
|
||||||
|
clearTimeout(Timeouter);
|
||||||
|
databack();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if (!this.EventTask.get(ListenerMainName)) {
|
||||||
|
this.EventTask.set(ListenerMainName, new Map());
|
||||||
|
}
|
||||||
|
if (!(this.EventTask.get(ListenerMainName)?.get(ListenerSubName))) {
|
||||||
|
this.EventTask.get(ListenerMainName)?.set(ListenerSubName, new Map());
|
||||||
|
}
|
||||||
|
this.EventTask.get(ListenerMainName)?.get(ListenerSubName)?.set(id, eventCallbak);
|
||||||
|
this.CreatListenerFunction(ListenerMainName);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
async CallNormalEvent<EventType extends (...args: any[]) => Promise<any>, ListenerType extends (...args: any[]) => void>
|
||||||
|
(EventName = '', ListenerName = '', waitTimes = 1, timeout: number = 3000, checker: (...args: Parameters<ListenerType>) => boolean, ...args: Parameters<EventType>) {
|
||||||
|
return new Promise<[EventRet: Awaited<ReturnType<EventType>>, ...Parameters<ListenerType>]>(async (resolve, reject) => {
|
||||||
|
const id = randomUUID();
|
||||||
|
let complete = 0;
|
||||||
|
let retData: Parameters<ListenerType> | undefined = undefined;
|
||||||
|
let retEvent: any = {};
|
||||||
|
const databack = () => {
|
||||||
|
if (complete == 0) {
|
||||||
|
reject(new Error('Timeout: NTEvent EventName:' + EventName + ' ListenerName:' + ListenerName + ' EventRet:\n' + JSON.stringify(retEvent, null, 4) + '\n'));
|
||||||
|
} else {
|
||||||
|
resolve([retEvent as Awaited<ReturnType<EventType>>, ...retData!]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
const ListenerNameList = ListenerName.split('/');
|
||||||
|
const ListenerMainName = ListenerNameList[0];
|
||||||
|
const ListenerSubName = ListenerNameList[1];
|
||||||
|
|
||||||
|
const Timeouter = setTimeout(databack, timeout);
|
||||||
|
|
||||||
|
const eventCallbak = {
|
||||||
|
timeout: timeout,
|
||||||
|
createtime: Date.now(),
|
||||||
|
checker: checker,
|
||||||
|
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);
|
||||||
|
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) });
|
142
src/common/utils/MessageUnique.ts
Normal file
142
src/common/utils/MessageUnique.ts
Normal file
@@ -0,0 +1,142 @@
|
|||||||
|
import { Peer } from '@/core';
|
||||||
|
import crypto, { randomInt, randomUUID } from 'crypto';
|
||||||
|
import { logError } from './log';
|
||||||
|
|
||||||
|
class LimitedHashTable<K, V> {
|
||||||
|
private keyToValue: Map<K, V> = new Map();
|
||||||
|
private valueToKey: Map<V, K> = new Map();
|
||||||
|
private maxSize: number;
|
||||||
|
|
||||||
|
constructor(maxSize: number) {
|
||||||
|
this.maxSize = maxSize;
|
||||||
|
}
|
||||||
|
resize(count: number) {
|
||||||
|
this.maxSize = count;
|
||||||
|
}
|
||||||
|
|
||||||
|
set(key: K, value: V): void {
|
||||||
|
// const isExist = this.keyToValue.get(key);
|
||||||
|
// if (isExist && isExist === value) {
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
this.keyToValue.set(key, value);
|
||||||
|
this.valueToKey.set(value, key);
|
||||||
|
while (this.keyToValue.size !== this.valueToKey.size) {
|
||||||
|
console.log('keyToValue.size !== valueToKey.size Error Atom');
|
||||||
|
this.keyToValue.clear();
|
||||||
|
this.valueToKey.clear();
|
||||||
|
}
|
||||||
|
// console.log('---------------');
|
||||||
|
// console.log(this.keyToValue);
|
||||||
|
// console.log(this.valueToKey);
|
||||||
|
// console.log('---------------');
|
||||||
|
while (this.keyToValue.size > this.maxSize || this.valueToKey.size > this.maxSize) {
|
||||||
|
//console.log(this.keyToValue.size > this.maxSize, this.valueToKey.size > this.maxSize);
|
||||||
|
const oldestKey = this.keyToValue.keys().next().value;
|
||||||
|
this.valueToKey.delete(this.keyToValue.get(oldestKey)!);
|
||||||
|
this.keyToValue.delete(oldestKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getValue(key: K): V | undefined {
|
||||||
|
return this.keyToValue.get(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
getKey(value: V): K | undefined {
|
||||||
|
return this.valueToKey.get(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteByValue(value: V): void {
|
||||||
|
const key = this.valueToKey.get(value);
|
||||||
|
if (key !== undefined) {
|
||||||
|
this.keyToValue.delete(key);
|
||||||
|
this.valueToKey.delete(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteByKey(key: K): void {
|
||||||
|
const value = this.keyToValue.get(key);
|
||||||
|
if (value !== undefined) {
|
||||||
|
this.keyToValue.delete(key);
|
||||||
|
this.valueToKey.delete(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getKeyList(): K[] {
|
||||||
|
return Array.from(this.keyToValue.keys());
|
||||||
|
}
|
||||||
|
//获取最近刚写入的几个值
|
||||||
|
getHeads(size: number): { key: K; value: V }[] | undefined {
|
||||||
|
const keyList = this.getKeyList();
|
||||||
|
if (keyList.length === 0) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
const result: { key: K; value: V }[] = [];
|
||||||
|
const listSize = Math.min(size, keyList.length);
|
||||||
|
for (let i = 0; i < listSize; i++) {
|
||||||
|
const key = keyList[listSize - i];
|
||||||
|
result.push({ key, value: this.keyToValue.get(key)! });
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class MessageUniqueWrapper {
|
||||||
|
private msgDataMap: LimitedHashTable<string, number>;
|
||||||
|
private msgIdMap: LimitedHashTable<string, number>;
|
||||||
|
constructor(maxMap: number = 1000) {
|
||||||
|
this.msgIdMap = new LimitedHashTable<string, number>(maxMap);
|
||||||
|
this.msgDataMap = new LimitedHashTable<string, number>(maxMap);
|
||||||
|
}
|
||||||
|
getRecentMsgIds(Peer: Peer, size: number): string[] {
|
||||||
|
const heads = this.msgIdMap.getHeads(size);
|
||||||
|
if (!heads) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
const data = heads.map((t) => MessageUnique.getMsgIdAndPeerByShortId(t.value));
|
||||||
|
const ret = data.filter((t) => t?.Peer.chatType === Peer.chatType && t?.Peer.peerUid === Peer.peerUid);
|
||||||
|
return ret.map((t) => t?.MsgId).filter((t) => t !== undefined);
|
||||||
|
}
|
||||||
|
createMsg(peer: Peer, msgId: string): number | undefined {
|
||||||
|
const key = `${msgId}|${peer.chatType}|${peer.peerUid}`;
|
||||||
|
const hash = crypto.createHash('md5').update(key);
|
||||||
|
const shortId = hash.digest().readInt32BE(0);
|
||||||
|
//减少性能损耗
|
||||||
|
// const isExist = this.msgIdMap.getKey(shortId);
|
||||||
|
// if (isExist && isExist === msgId) {
|
||||||
|
// return shortId;
|
||||||
|
// }
|
||||||
|
this.msgIdMap.set(msgId, shortId);
|
||||||
|
this.msgDataMap.set(key, shortId);
|
||||||
|
return shortId;
|
||||||
|
}
|
||||||
|
|
||||||
|
getMsgIdAndPeerByShortId(shortId: number): { MsgId: string; Peer: Peer } | undefined {
|
||||||
|
const data = this.msgDataMap.getKey(shortId);
|
||||||
|
if (data) {
|
||||||
|
const [msgId, chatTypeStr, peerUid] = data.split('|');
|
||||||
|
const peer: Peer = {
|
||||||
|
chatType: parseInt(chatTypeStr),
|
||||||
|
peerUid,
|
||||||
|
guildId: '',
|
||||||
|
};
|
||||||
|
return { MsgId: msgId, Peer: peer };
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
getShortIdByMsgId(msgId: string): number | undefined {
|
||||||
|
return this.msgIdMap.getValue(msgId);
|
||||||
|
}
|
||||||
|
getPeerByMsgId(msgId: string) {
|
||||||
|
const shortId = this.msgIdMap.getValue(msgId);
|
||||||
|
if (!shortId) return undefined;
|
||||||
|
return this.getMsgIdAndPeerByShortId(shortId);
|
||||||
|
}
|
||||||
|
resize(maxSize: number): void {
|
||||||
|
this.msgIdMap.resize(maxSize);
|
||||||
|
this.msgDataMap.resize(maxSize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const MessageUnique: MessageUniqueWrapper = new MessageUniqueWrapper();
|
17
src/common/utils/Packet.ts
Normal file
17
src/common/utils/Packet.ts
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
// 方案一 MiniApp发包方案
|
||||||
|
// 前置条件: 处于GUI环境 存在MiniApp
|
||||||
|
|
||||||
|
import { NTQQSystemApi } from '@/core';
|
||||||
|
|
||||||
|
// 前排提示: 开发验证仅Win平台开展
|
||||||
|
export class MiniAppUtil {
|
||||||
|
static async RunMiniAppWithGUI() {
|
||||||
|
//process.env.ELECTRON_RUN_AS_NODE = undefined;//没用还是得自己用cpp之类的语言写个程序转发参数
|
||||||
|
return NTQQSystemApi.BootMiniApp(process.execPath, 'miniapp://open/1007?url=https%3A%2F%2Fm.q.qq.com%2Fa%2Fs%2Fedd0a83d3b8afe233dfa07adaaf8033f%3Fscene%3D1007%26min_refer%3D10001');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 方案二 MiniApp发包方案 替代MiniApp方案
|
||||||
|
// 前置条件: 无
|
||||||
|
export class MojoMiniAppUtil{
|
||||||
|
|
||||||
|
}
|
@@ -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,40 @@ type QQVersionConfigInfo = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let _qqVersionConfigInfo: QQVersionConfigInfo = {
|
let _qqVersionConfigInfo: QQVersionConfigInfo = {
|
||||||
'baseVersion': '9.9.9-22578',
|
'baseVersion': '9.9.12-25765',
|
||||||
'curVersion': '9.9.9-22578',
|
'curVersion': '9.9.12-25765',
|
||||||
'prevVersion': '',
|
'prevVersion': '',
|
||||||
'onErrorVersions': [],
|
'onErrorVersions': [],
|
||||||
'buildId': '22578'
|
'buildId': '25765'
|
||||||
};
|
};
|
||||||
|
|
||||||
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;
|
||||||
|
//V1_WIN_NQ_9.9.12_25765_GW_B
|
||||||
|
export const qqPkgInfo: QQPkgInfo = JSON.parse(fs.readFileSync(pkgInfoPath).toString());
|
||||||
|
// platform_type: 3,
|
||||||
|
// app_type: 4,
|
||||||
|
// app_version: '9.9.12-25765',
|
||||||
|
// qua: 'V1_WIN_NQ_9.9.12_25765_GW_B',
|
||||||
|
// appid: '537234702',
|
||||||
|
// platVer: '10.0.26100',
|
||||||
|
// clientVer: '9.9.9-25765',
|
||||||
|
|
||||||
export const qqPkgInfo: QQPkgInfo = require(pkgInfoPath);
|
// Linux
|
||||||
|
// app_version: '3.2.9-25765',
|
||||||
|
// qua: 'V1_LNX_NQ_3.2.10_25765_GW_B',
|
||||||
|
|
||||||
let _appid: string = '537213335'; // 默认为 Windows 平台的 appid
|
let _appid: string = '537234702'; // 默认为 Windows 平台的 appid
|
||||||
if (systemPlatform === 'linux') {
|
if (systemPlatform === 'linux') {
|
||||||
_appid = '537213710';
|
_appid = '537234773';
|
||||||
}
|
}
|
||||||
// todo: mac 平台的 appid
|
// todo: mac 平台的 appid
|
||||||
export const appid = _appid;
|
export const appid = _appid;
|
@@ -1,9 +1,9 @@
|
|||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import { encode, getDuration, getWavFileInfo, isWav } from 'silk-wasm';
|
import { encode, getDuration, getWavFileInfo, isWav, isSilk } from 'silk-wasm';
|
||||||
import fsPromise from 'fs/promises';
|
import fsPromise from 'fs/promises';
|
||||||
import { log } from './log';
|
import { log, logError } from './log';
|
||||||
import path from 'node:path';
|
import path from 'node:path';
|
||||||
import { v4 as uuidv4 } from 'uuid';
|
import { randomUUID } from 'crypto';
|
||||||
import { spawn } from 'node:child_process';
|
import { spawn } from 'node:child_process';
|
||||||
import { getTempDir } from '@/common/utils/file';
|
import { getTempDir } from '@/common/utils/file';
|
||||||
|
|
||||||
@@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -63,10 +63,11 @@ export async function encodeSilk(filePath: string) {
|
|||||||
// }
|
// }
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const pttPath = path.join(TEMP_DIR, uuidv4());
|
const file = await fsPromise.readFile(filePath);
|
||||||
if (getFileHeader(filePath) !== '02232153494c4b') {
|
const pttPath = path.join(TEMP_DIR, randomUUID());
|
||||||
|
if (!isSilk(file)) {
|
||||||
log(`语音文件${filePath}需要转换成silk`);
|
log(`语音文件${filePath}需要转换成silk`);
|
||||||
const _isWav = await isWavFile(filePath);
|
const _isWav = isWav(file);
|
||||||
const pcmPath = pttPath + '.pcm';
|
const pcmPath = pttPath + '.pcm';
|
||||||
let sampleRate = 0;
|
let sampleRate = 0;
|
||||||
const convert = () => {
|
const convert = () => {
|
||||||
@@ -96,7 +97,7 @@ export async function encodeSilk(filePath: string) {
|
|||||||
if (!_isWav) {
|
if (!_isWav) {
|
||||||
input = await convert();
|
input = await convert();
|
||||||
} else {
|
} else {
|
||||||
input = fs.readFileSync(filePath);
|
input = file;
|
||||||
const allowSampleRate = [8000, 12000, 16000, 24000, 32000, 44100, 48000];
|
const allowSampleRate = [8000, 12000, 16000, 24000, 32000, 44100, 48000];
|
||||||
const { fmt } = getWavFileInfo(input);
|
const { fmt } = getWavFileInfo(input);
|
||||||
// log(`wav文件信息`, fmt)
|
// log(`wav文件信息`, fmt)
|
||||||
@@ -113,7 +114,7 @@ export async function encodeSilk(filePath: string) {
|
|||||||
duration: silk.duration / 1000
|
duration: silk.duration / 1000
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
const silk = fs.readFileSync(filePath);
|
const silk = file;
|
||||||
let duration = 0;
|
let duration = 0;
|
||||||
try {
|
try {
|
||||||
duration = getDuration(silk) / 1000;
|
duration = getDuration(silk) / 1000;
|
||||||
@@ -129,7 +130,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,323 +0,0 @@
|
|||||||
import { ElementType, FileElement, PicElement, PttElement, RawMessage, VideoElement } from '@/core/qqnt/entities';
|
|
||||||
|
|
||||||
import sqlite3 from 'sqlite3';
|
|
||||||
import { log } from '@/common/utils/log';
|
|
||||||
|
|
||||||
type DBMsg = {
|
|
||||||
id: number,
|
|
||||||
longId: string,
|
|
||||||
seq: number,
|
|
||||||
peerUid: string,
|
|
||||||
msg: string
|
|
||||||
}
|
|
||||||
|
|
||||||
type DBFile = {
|
|
||||||
name: string; // 文件名
|
|
||||||
path: string;
|
|
||||||
url: string;
|
|
||||||
size: number;
|
|
||||||
uuid: string;
|
|
||||||
msgId: string;
|
|
||||||
elementId: string;
|
|
||||||
element: PicElement | VideoElement | FileElement | PttElement;
|
|
||||||
elementType: ElementType.PIC | ElementType.VIDEO | ElementType.FILE | ElementType.PTT;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class DBUtilBase {
|
|
||||||
protected db: sqlite3.Database | undefined;
|
|
||||||
|
|
||||||
createConnection(dbPath: string) {
|
|
||||||
if (this.db) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.db = new sqlite3.Database(dbPath, sqlite3.OPEN_READWRITE | sqlite3.OPEN_CREATE, (err) => {
|
|
||||||
if (err) {
|
|
||||||
log('Could not connect to database', err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.createTable();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
protected createTable() {
|
|
||||||
throw new Error('Method not implemented.');
|
|
||||||
}
|
|
||||||
|
|
||||||
close() {
|
|
||||||
this.db?.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class DBUtil extends DBUtilBase {
|
|
||||||
private msgCache: Map<string, RawMessage> = new Map<string, RawMessage>();
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
super();
|
|
||||||
const interval = 1000 * 60 * 10; // 10分钟清理一次缓存
|
|
||||||
setInterval(() => {
|
|
||||||
log('清理消息缓存');
|
|
||||||
this.msgCache.forEach((msg, key) => {
|
|
||||||
if ((Date.now() - parseInt(msg.msgTime) * 1000) > interval) {
|
|
||||||
this.msgCache.delete(key);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}, interval);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
protected createTable() {
|
|
||||||
// 消息记录
|
|
||||||
const createTableSQL = `
|
|
||||||
CREATE TABLE IF NOT EXISTS msgs (
|
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
||||||
long_id TEXT NOT NULL UNIQUE,
|
|
||||||
seq INTEGER NOT NULL,
|
|
||||||
peer_uid TEXT NOT NULL,
|
|
||||||
msg TEXT NOT NULL
|
|
||||||
)`;
|
|
||||||
this.db!.run(createTableSQL, function (err) {
|
|
||||||
if (err) {
|
|
||||||
log('Could not create table', err);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// 文件缓存
|
|
||||||
const createFileTableSQL = `
|
|
||||||
CREATE TABLE IF NOT EXISTS files (
|
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
||||||
name TEXT NOT NULL,
|
|
||||||
path TEXT NOT NULL,
|
|
||||||
url TEXT,
|
|
||||||
size INTEGER NOT NULL,
|
|
||||||
uuid TEXT,
|
|
||||||
elementType INTEGER,
|
|
||||||
element TEXT NOT NULL,
|
|
||||||
elementId TEXT NOT NULL,
|
|
||||||
msgId TEXT NOT NULL
|
|
||||||
)`;
|
|
||||||
this.db!.run(createFileTableSQL, function (err) {
|
|
||||||
if (err) {
|
|
||||||
log('Could not create table files', err);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// 接收到的临时会话消息uid
|
|
||||||
const createTempUinTableSQL = `
|
|
||||||
CREATE TABLE IF NOT EXISTS temp_uins (
|
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
||||||
uid TEXT,
|
|
||||||
uin TEXT
|
|
||||||
)`;
|
|
||||||
this.db!.run(createTempUinTableSQL, function (err) {
|
|
||||||
if (err) {
|
|
||||||
log('Could not create table temp_uins', err);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private async getMsg(query: string, params: any[]) {
|
|
||||||
const stmt = this.db!.prepare(query);
|
|
||||||
return new Promise<RawMessage | null>((resolve, reject) => {
|
|
||||||
stmt.get(...params, (err: any, row: DBMsg) => {
|
|
||||||
// log("getMsg", row, err);
|
|
||||||
if (err) {
|
|
||||||
log('Could not get msg by short id', err);
|
|
||||||
resolve(null);
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
const msg = JSON.parse(row.msg);
|
|
||||||
msg.id = row.id;
|
|
||||||
return resolve(msg);
|
|
||||||
} catch (e) {
|
|
||||||
return resolve(null);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async getMsgByShortId(shortId: number): Promise<RawMessage | null> {
|
|
||||||
const getStmt = 'SELECT * FROM msgs WHERE id = ?';
|
|
||||||
return this.getMsg(getStmt, [shortId]);
|
|
||||||
}
|
|
||||||
|
|
||||||
async getMsgByLongId(longId: string): Promise<RawMessage | null> {
|
|
||||||
if (this.msgCache.has(longId)) {
|
|
||||||
return this.msgCache.get(longId)!;
|
|
||||||
}
|
|
||||||
return this.getMsg('SELECT * FROM msgs WHERE long_id = ?', [longId]);
|
|
||||||
}
|
|
||||||
|
|
||||||
async getMsgBySeq(peerUid: string, seq: string): Promise<RawMessage | null> {
|
|
||||||
const stmt = 'SELECT * FROM msgs WHERE peer_uid = ? AND seq = ?';
|
|
||||||
return this.getMsg(stmt, [peerUid, seq]);
|
|
||||||
}
|
|
||||||
|
|
||||||
async addMsg(msg: RawMessage, update = true): Promise<number> {
|
|
||||||
log('正在记录消息到数据库', msg.msgId);
|
|
||||||
const existMsg = await this.getMsgByLongId(msg.msgId);
|
|
||||||
if (existMsg) {
|
|
||||||
// log('消息已存在,更新数据库', msg.msgId);
|
|
||||||
if (update) this.updateMsg(msg).then();
|
|
||||||
return existMsg.id!;
|
|
||||||
}
|
|
||||||
const stmt = this.db!.prepare('INSERT INTO msgs (long_id, seq, peer_uid, msg) VALUES (?, ?, ?, ?)');
|
|
||||||
|
|
||||||
// const runAsync = promisify(stmt.run.bind(stmt));
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
||||||
const dbInstance = this;
|
|
||||||
stmt.run(msg.msgId, msg.msgSeq, msg.peerUid, JSON.stringify(msg), function (err: any) {
|
|
||||||
if (err) {
|
|
||||||
if (err.errno === 19) {
|
|
||||||
// log('消息已存在,更新数据库', msg.msgId);
|
|
||||||
dbInstance.getMsgByLongId(msg.msgId).then((msg: RawMessage | null) => {
|
|
||||||
if (msg) {
|
|
||||||
dbInstance.msgCache.set(msg.msgId, msg);
|
|
||||||
// log('获取消息短id成功', msg.id);
|
|
||||||
resolve(msg.id!);
|
|
||||||
} else {
|
|
||||||
log('db could not get msg by long id', err);
|
|
||||||
resolve(-1);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
log('db could not add msg', err);
|
|
||||||
resolve(-1);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// log("addMsg", this);
|
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async updateMsg(msg: RawMessage) {
|
|
||||||
const existMsg = this.msgCache.get(msg.msgId);
|
|
||||||
if (existMsg) {
|
|
||||||
Object.assign(existMsg, msg);
|
|
||||||
}
|
|
||||||
const stmt = this.db!.prepare('UPDATE msgs SET msg = ?, seq = ? WHERE long_id = ?');
|
|
||||||
try {
|
|
||||||
stmt.run(JSON.stringify(msg), msg.msgSeq, msg.msgId);
|
|
||||||
} catch (e) {
|
|
||||||
log('updateMsg db error', e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async addFileCache(file: DBFile) {
|
|
||||||
const stmt = this.db!.prepare('INSERT INTO files (name, path, url, size, uuid, elementType ,element, elementId, msgId) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)');
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
stmt.run(file.name, file.path, file.url, file.size, file.uuid,
|
|
||||||
file.elementType,
|
|
||||||
JSON.stringify(file.element),
|
|
||||||
file.elementId,
|
|
||||||
file.msgId,
|
|
||||||
function (err: any) {
|
|
||||||
if (err) {
|
|
||||||
log('db could not add file', err);
|
|
||||||
reject(err);
|
|
||||||
}
|
|
||||||
resolve(null);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private async getFileCache(query: string, params: any[]) {
|
|
||||||
const stmt = this.db!.prepare(query);
|
|
||||||
return new Promise<DBFile | null>((resolve, reject) => {
|
|
||||||
stmt.get(...params, (err: any, row: DBFile & { element: string }) => {
|
|
||||||
if (err) {
|
|
||||||
log('db could not get file cache', err);
|
|
||||||
reject(err);
|
|
||||||
}
|
|
||||||
if (row) {
|
|
||||||
row.element = JSON.parse(row.element);
|
|
||||||
}
|
|
||||||
resolve(row);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async getFileCacheByName(name: string): Promise<DBFile | null> {
|
|
||||||
return this.getFileCache('SELECT * FROM files WHERE name = ?', [name]);
|
|
||||||
}
|
|
||||||
|
|
||||||
async getFileCacheByUuid(uuid: string): Promise<DBFile | null> {
|
|
||||||
return this.getFileCache('SELECT * FROM files WHERE uuid = ?', [uuid]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// todo: 是否所有的文件都有uuid?语音消息有没有uuid?
|
|
||||||
async updateFileCache(file: DBFile) {
|
|
||||||
const stmt = this.db!.prepare('UPDATE files SET path = ?, url = ? WHERE uuid = ?');
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
stmt.run(file.path, file.url, function (err: any) {
|
|
||||||
if (err) {
|
|
||||||
log('db could not update file cache', err);
|
|
||||||
reject(err);
|
|
||||||
}
|
|
||||||
resolve(null);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// 被动收到的临时会话消息uin->uid
|
|
||||||
async getReceivedTempUinMap() {
|
|
||||||
const stmt = 'SELECT * FROM temp_uins';
|
|
||||||
return new Promise<Record<string, string>>((resolve, reject) => {
|
|
||||||
this.db!.all(stmt, (err, rows: { uin: string, uid: string }[]) => {
|
|
||||||
if (err) {
|
|
||||||
log('db could not get temp uin map', err);
|
|
||||||
reject(err);
|
|
||||||
}
|
|
||||||
const map: Record<string, string> = {};
|
|
||||||
rows.forEach(row => {
|
|
||||||
map[row.uin] = row.uid;
|
|
||||||
});
|
|
||||||
resolve(map);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// 通过uin获取临时会话消息uid
|
|
||||||
async getUidByTempUin(uid: string) {
|
|
||||||
const stmt = 'SELECT * FROM temp_uins WHERE uin = ?';
|
|
||||||
return new Promise<string>((resolve, reject) => {
|
|
||||||
this.db!.get(stmt, [uid], (err, row: { uin: string, uid: string }) => {
|
|
||||||
if (err) {
|
|
||||||
log('db could not get temp uin map', err);
|
|
||||||
reject(err);
|
|
||||||
}
|
|
||||||
resolve(row?.uid);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
export const dbUtil = new DBUtil();
|
|
@@ -1,16 +1,15 @@
|
|||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import fsPromise from 'fs/promises';
|
import fsPromise, { stat } 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 * as fileType from 'file-type';
|
import * as fileType from 'file-type';
|
||||||
import { v4 as uuidv4 } from 'uuid';
|
import { randomUUID } from 'crypto';
|
||||||
import { napCatCore } from '@/core';
|
import { napCatCore } from '@/core';
|
||||||
|
|
||||||
export const getNapCatDir = () => {
|
export const getNapCatDir = () => {
|
||||||
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;
|
||||||
};
|
};
|
||||||
@@ -50,7 +49,40 @@ export function checkFileReceived(path: string, timeout: number = 3000): Promise
|
|||||||
check();
|
check();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
// 定义一个异步函数来检查文件是否存在
|
||||||
|
export async function checkFileReceived2(path: string, timeout: number = 3000): Promise<void> {
|
||||||
|
// 使用 Promise.race 来同时进行文件状态检查和超时计时
|
||||||
|
// Promise.race 会返回第一个解决(resolve)或拒绝(reject)的 Promise
|
||||||
|
await Promise.race([
|
||||||
|
checkFile(path),
|
||||||
|
timeoutPromise(timeout, `文件不存在: ${path}`),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 转换超时时间至 Promise
|
||||||
|
function timeoutPromise(timeout: number, errorMsg: string): Promise<void> {
|
||||||
|
return new Promise((_, reject) => {
|
||||||
|
setTimeout(() => {
|
||||||
|
reject(new Error(errorMsg));
|
||||||
|
}, timeout);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 异步检查文件是否存在
|
||||||
|
async function checkFile(path: string): Promise<void> {
|
||||||
|
try {
|
||||||
|
await stat(path);
|
||||||
|
} catch (error: any) {
|
||||||
|
if (error.code === 'ENOENT') {
|
||||||
|
// 如果文件不存在,则抛出一个错误
|
||||||
|
throw new Error(`文件不存在: ${path}`);
|
||||||
|
} else {
|
||||||
|
// 对于 stat 调用的其他错误,重新抛出
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 如果文件存在,则无需做任何事情,Promise 解决(resolve)自身
|
||||||
|
}
|
||||||
export async function file2base64(path: string) {
|
export async function file2base64(path: string) {
|
||||||
const readFile = util.promisify(fs.readFile);
|
const readFile = util.promisify(fs.readFile);
|
||||||
const result = {
|
const result = {
|
||||||
@@ -115,6 +147,8 @@ export async function httpDownload(options: string | HttpDownloadOptions): Promi
|
|||||||
};
|
};
|
||||||
if (typeof options === 'string') {
|
if (typeof options === 'string') {
|
||||||
url = options;
|
url = options;
|
||||||
|
const host = new URL(url).hostname;
|
||||||
|
headers['Host'] = host;
|
||||||
} else {
|
} else {
|
||||||
url = options.url;
|
url = options.url;
|
||||||
if (options.headers) {
|
if (options.headers) {
|
||||||
@@ -125,7 +159,12 @@ export async function httpDownload(options: string | HttpDownloadOptions): Promi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const fetchRes = await fetch(url, headers);
|
const fetchRes = await fetch(url, { headers }).catch((err) => {
|
||||||
|
if (err.cause) {
|
||||||
|
throw err.cause;
|
||||||
|
}
|
||||||
|
throw err;
|
||||||
|
});
|
||||||
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();
|
||||||
@@ -152,7 +191,7 @@ export async function uri2local(uri: string, fileName: string | null = null): Pr
|
|||||||
isLocal: false
|
isLocal: false
|
||||||
};
|
};
|
||||||
if (!fileName) {
|
if (!fileName) {
|
||||||
fileName = uuidv4();
|
fileName = randomUUID();
|
||||||
}
|
}
|
||||||
let filePath = path.join(getTempDir(), fileName);
|
let filePath = path.join(getTempDir(), fileName);
|
||||||
let url = null;
|
let url = null;
|
||||||
@@ -193,8 +232,9 @@ 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(), randomUUID() + fileName);
|
||||||
fs.writeFileSync(filePath, buffer);
|
fs.writeFileSync(filePath, buffer);
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
res.errMsg = `${url}下载失败,` + e.toString();
|
res.errMsg = `${url}下载失败,` + e.toString();
|
||||||
@@ -210,13 +250,14 @@ export async function uri2local(uri: string, fileName: string | null = null): Pr
|
|||||||
} else {
|
} else {
|
||||||
filePath = pathname;
|
filePath = pathname;
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
const cache = await dbUtil.getFileCacheByName(uri);
|
else {
|
||||||
if (cache) {
|
// const cache = await dbUtil.getFileCacheByName(uri);
|
||||||
filePath = cache.path;
|
// if (cache) {
|
||||||
} else {
|
// filePath = cache.path;
|
||||||
filePath = uri;
|
// } else {
|
||||||
}
|
// filePath = uri;
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
res.isLocal = true;
|
res.isLocal = true;
|
||||||
@@ -261,12 +302,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,9 +1,36 @@
|
|||||||
import crypto from 'node:crypto';
|
import crypto from 'node:crypto';
|
||||||
|
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';
|
||||||
|
import * as fsPromise from 'node:fs/promises';
|
||||||
|
|
||||||
|
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));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function PromiseTimer<T>(promise: Promise<T>, ms: number): Promise<T> {
|
||||||
|
const timeoutPromise = new Promise<T>((_, reject) =>
|
||||||
|
setTimeout(() => reject(new Error('PromiseTimer: Operation timed out')), ms)
|
||||||
|
);
|
||||||
|
return Promise.race([promise, timeoutPromise]);
|
||||||
|
}
|
||||||
|
export async function runAllWithTimeout<T>(tasks: Promise<T>[], timeout: number): Promise<T[]> {
|
||||||
|
const wrappedTasks = tasks.map(task =>
|
||||||
|
PromiseTimer(task, timeout).then(
|
||||||
|
result => ({ status: 'fulfilled', value: result }),
|
||||||
|
error => ({ status: 'rejected', reason: error })
|
||||||
|
)
|
||||||
|
);
|
||||||
|
const results = await Promise.all(wrappedTasks);
|
||||||
|
return results
|
||||||
|
.filter(result => result.status === 'fulfilled')
|
||||||
|
.map(result => (result as { status: 'fulfilled'; value: T }).value);
|
||||||
|
}
|
||||||
|
|
||||||
export function getMd5(s: string) {
|
export function getMd5(s: string) {
|
||||||
|
|
||||||
const h = crypto.createHash('md5');
|
const h = crypto.createHash('md5');
|
||||||
@@ -18,3 +45,290 @@ 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;
|
||||||
|
}
|
||||||
|
export function simpleDecorator(target: any, context: any) {
|
||||||
|
}
|
||||||
|
|
||||||
|
// export function CacheClassFunc(ttl: number = 3600 * 1000, customKey: string = '') {
|
||||||
|
// const cache = new Map<string, { expiry: number; value: any }>();
|
||||||
|
// return function CacheClassFuncDecorator(originalMethod: Function, context: ClassMethodDecoratorContext) {
|
||||||
|
// async function CacheClassFuncDecoratorInternal(this: any, ...args: any[]) {
|
||||||
|
// const key = `${customKey}${String(context.name)}.(${args.map(arg => JSON.stringify(arg)).join(', ')})`;
|
||||||
|
// const cachedValue = cache.get(key);
|
||||||
|
// if (cachedValue && cachedValue.expiry > Date.now()) {
|
||||||
|
// return cachedValue.value;
|
||||||
|
// }
|
||||||
|
// const result = originalMethod.call(this, ...args);
|
||||||
|
// cache.set(key, { expiry: Date.now() + ttl, value: result });
|
||||||
|
// return result;
|
||||||
|
// }
|
||||||
|
// return CacheClassFuncDecoratorInternal;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
export function CacheClassFuncAsync(ttl: number = 3600 * 1000, customKey: string = '') {
|
||||||
|
//console.log('CacheClassFuncAsync', ttl, customKey);
|
||||||
|
function logExecutionTime(target: any, methodName: string, descriptor: PropertyDescriptor) {
|
||||||
|
//console.log('logExecutionTime', target, methodName, descriptor);
|
||||||
|
const cache = new Map<string, { expiry: number; value: any }>();
|
||||||
|
const originalMethod = descriptor.value;
|
||||||
|
descriptor.value = async function (...args: any[]) {
|
||||||
|
const key = `${customKey}${String(methodName)}.(${args.map(arg => JSON.stringify(arg)).join(', ')})`;
|
||||||
|
cache.forEach((value, key) => {
|
||||||
|
if (value.expiry < Date.now()) {
|
||||||
|
cache.delete(key);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const cachedValue = cache.get(key);
|
||||||
|
if (cachedValue && cachedValue.expiry > Date.now()) {
|
||||||
|
return cachedValue.value;
|
||||||
|
}
|
||||||
|
// const start = Date.now();
|
||||||
|
const result = await originalMethod.apply(this, args);
|
||||||
|
// const end = Date.now();
|
||||||
|
// console.log(`Method ${methodName} executed in ${end - start} ms.`);
|
||||||
|
cache.set(key, { expiry: Date.now() + ttl, value: result });
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return logExecutionTime;
|
||||||
|
}
|
||||||
|
export function CacheClassFuncAsyncExtend(ttl: number = 3600 * 1000, customKey: string = '', checker: any = (...data: any[]) => { return true; }) {
|
||||||
|
//console.log('CacheClassFuncAsync', ttl, customKey);
|
||||||
|
function logExecutionTime(target: any, methodName: string, descriptor: PropertyDescriptor) {
|
||||||
|
//console.log('logExecutionTime', target, methodName, descriptor);
|
||||||
|
const cache = new Map<string, { expiry: number; value: any }>();
|
||||||
|
const originalMethod = descriptor.value;
|
||||||
|
descriptor.value = async function (...args: any[]) {
|
||||||
|
const key = `${customKey}${String(methodName)}.(${args.map(arg => JSON.stringify(arg)).join(', ')})`;
|
||||||
|
cache.forEach((value, key) => {
|
||||||
|
if (value.expiry < Date.now()) {
|
||||||
|
cache.delete(key);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const cachedValue = cache.get(key);
|
||||||
|
if (cachedValue && cachedValue.expiry > Date.now()) {
|
||||||
|
return cachedValue.value;
|
||||||
|
}
|
||||||
|
// const start = Date.now();
|
||||||
|
const result = await originalMethod.apply(this, args);
|
||||||
|
if (!checker(...args, result)) {
|
||||||
|
return result;//丢弃缓存
|
||||||
|
}
|
||||||
|
// const end = Date.now();
|
||||||
|
// console.log(`Method ${methodName} executed in ${end - start} ms.`);
|
||||||
|
cache.set(key, { expiry: Date.now() + ttl, value: result });
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return logExecutionTime;
|
||||||
|
}
|
||||||
|
// export function CacheClassFuncAsync(ttl: number = 3600 * 1000, customKey: string = ''): any {
|
||||||
|
// const cache = new Map<string, { expiry: number; value: any }>();
|
||||||
|
|
||||||
|
// // 注意:在JavaScript装饰器中,我们通常不直接处理ClassMethodDecoratorContext这样的类型,
|
||||||
|
// // 因为装饰器的参数通常是目标类(对于类装饰器)、属性名(对于属性装饰器)等。
|
||||||
|
// // 对于方法装饰器,我们关注的是方法本身及其描述符。
|
||||||
|
// // 但这里我们维持原逻辑,假设有一个自定义的处理上下文的方式。
|
||||||
|
|
||||||
|
// return function (originalMethod: Function): any {
|
||||||
|
// console.log(originalMethod);
|
||||||
|
// // 由于JavaScript装饰器原生不支持异步直接定义,我们保持async定义以便处理异步方法。
|
||||||
|
// async function decoratorWrapper(this: any, ...args: any[]): Promise<any> {
|
||||||
|
// console.log(...args);
|
||||||
|
// const key = `${customKey}${originalMethod.name}.(${args.map(arg => JSON.stringify(arg)).join(', ')})`;
|
||||||
|
// const cachedValue = cache.get(key);
|
||||||
|
// // 遍历cache 清除expiry内容
|
||||||
|
// cache.forEach((value, key) => {
|
||||||
|
// if (value.expiry < Date.now()) {
|
||||||
|
// cache.delete(key);
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
// if (cachedValue && cachedValue.expiry > Date.now()) {
|
||||||
|
// return cachedValue.value;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // 直接await异步方法的结果
|
||||||
|
// const result = await originalMethod.apply(this, args);
|
||||||
|
// cache.set(key, { expiry: Date.now() + ttl, value: result });
|
||||||
|
// return result;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // 返回装饰后的方法,保持与原方法相同的名称和描述符(如果需要更精细的控制,可以考虑使用Object.getOwnPropertyDescriptor等)
|
||||||
|
// return decoratorWrapper;
|
||||||
|
// };
|
||||||
|
// }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 函数缓存装饰器,根据方法名、参数、自定义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;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function deleteOldFiles(directoryPath: string, daysThreshold: number) {
|
||||||
|
try {
|
||||||
|
const files = await fsPromise.readdir(directoryPath);
|
||||||
|
|
||||||
|
for (const file of files) {
|
||||||
|
const filePath = path.join(directoryPath, file);
|
||||||
|
const stats = await fsPromise.stat(filePath);
|
||||||
|
const lastModifiedTime = stats.mtimeMs;
|
||||||
|
const currentTime = Date.now();
|
||||||
|
const timeDifference = currentTime - lastModifiedTime;
|
||||||
|
const daysDifference = timeDifference / (1000 * 60 * 60 * 24);
|
||||||
|
|
||||||
|
if (daysDifference > daysThreshold) {
|
||||||
|
await fsPromise.unlink(filePath); // Delete the file
|
||||||
|
//console.log(`Deleted: ${filePath}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
//console.error('Error deleting files:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@@ -1,4 +1,138 @@
|
|||||||
|
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, // 指定日志文件的位置和文件名
|
||||||
|
maxLogSize: 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 (msgItem instanceof Error) { // 判断是否是错误
|
||||||
|
logMsg += msgItem.stack + ' ';
|
||||||
|
continue;
|
||||||
|
} else 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,5 +1,5 @@
|
|||||||
// QQ等级换算
|
// QQ等级换算
|
||||||
import { QQLevel } from '@/core/qqnt/entities';
|
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;
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
193
src/common/utils/request.ts
Normal file
193
src/common/utils/request.ts
Normal file
@@ -0,0 +1,193 @@
|
|||||||
|
import https from 'node:https';
|
||||||
|
import http from 'node:http';
|
||||||
|
import { readFileSync } from 'node:fs';
|
||||||
|
import { NTQQUserApi } from '@/core';
|
||||||
|
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) => {
|
||||||
|
const req = 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);
|
||||||
|
}).catch((err) => {
|
||||||
|
reject(err);
|
||||||
|
});
|
||||||
|
} 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;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
req.on('error', (error: any) => {
|
||||||
|
reject(error);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// 请求和回复都是JSON data传原始内容 自动编码json
|
||||||
|
static async HttpGetJson<T>(url: string, method: string = 'GET', data?: any, headers: { [key: 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
|
||||||
|
};
|
||||||
|
// headers: {
|
||||||
|
// 'Content-Type': 'application/json',
|
||||||
|
// 'Content-Length': Buffer.byteLength(postData),
|
||||||
|
// },
|
||||||
|
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: { [key: string]: string } = {}) {
|
||||||
|
return this.HttpGetJson<string>(url, method, data, headers, false, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
static async createFormData(boundary: string, filePath: string): Promise<Buffer> {
|
||||||
|
let type = 'image/png';
|
||||||
|
if (filePath.endsWith('.jpg')) {
|
||||||
|
type = 'image/jpeg';
|
||||||
|
}
|
||||||
|
const formDataParts = [
|
||||||
|
`------${boundary}\r\n`,
|
||||||
|
`Content-Disposition: form-data; name="share_image"; filename="${filePath}"\r\n`,
|
||||||
|
'Content-Type: ' + type + '\r\n\r\n'
|
||||||
|
];
|
||||||
|
|
||||||
|
const fileContent = readFileSync(filePath);
|
||||||
|
const footer = `\r\n------${boundary}--`;
|
||||||
|
return Buffer.concat([
|
||||||
|
Buffer.from(formDataParts.join(''), 'utf8'),
|
||||||
|
fileContent,
|
||||||
|
Buffer.from(footer, 'utf8')
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static async uploadImageForOpenPlatform(filePath: string): Promise<string> {
|
||||||
|
return new Promise(async (resolve, reject) => {
|
||||||
|
type retType = { retcode: number, result?: { url: string } };
|
||||||
|
try {
|
||||||
|
const cookies = Object.entries(await NTQQUserApi.getCookies('connect.qq.com')).map(([key, value]) => `${key}=${value}`).join('; ');
|
||||||
|
const options = {
|
||||||
|
hostname: 'cgi.connect.qq.com',
|
||||||
|
port: 443,
|
||||||
|
path: '/qqconnectopen/upload_share_image',
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Referer': 'https://cgi.connect.qq.com',
|
||||||
|
'Cookie': cookies,
|
||||||
|
'Accept': '*/*',
|
||||||
|
'Connection': 'keep-alive',
|
||||||
|
'Content-Type': 'multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const req = https.request(options, async (res) => {
|
||||||
|
let responseBody = '';
|
||||||
|
|
||||||
|
res.on('data', (chunk: string | Buffer) => {
|
||||||
|
responseBody += chunk.toString();
|
||||||
|
});
|
||||||
|
|
||||||
|
res.on('end', () => {
|
||||||
|
try {
|
||||||
|
if (res.statusCode && res.statusCode >= 200 && res.statusCode < 300) {
|
||||||
|
const responseJson = JSON.parse(responseBody) as retType;
|
||||||
|
resolve(responseJson.result!.url!);
|
||||||
|
} else {
|
||||||
|
reject(new Error(`Unexpected status code: ${res.statusCode}`));
|
||||||
|
}
|
||||||
|
} catch (parseError) {
|
||||||
|
reject(parseError);
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
req.on('error', (error) => {
|
||||||
|
reject(error);
|
||||||
|
console.error('Error during upload:', error);
|
||||||
|
});
|
||||||
|
|
||||||
|
const body = await RequestUtil.createFormData('WebKitFormBoundary7MA4YWxkTrZu0gW', filePath);
|
||||||
|
// req.setHeader('Content-Length', Buffer.byteLength(body));
|
||||||
|
// console.log(`Prepared data size: ${Buffer.byteLength(body)} bytes`);
|
||||||
|
req.write(body);
|
||||||
|
req.end();
|
||||||
|
return;
|
||||||
|
} catch (error) {
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@@ -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 { randomUUID } from 'crypto';
|
||||||
|
|
||||||
|
// 缓解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 || randomUUID(); // 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() {
|
|
||||||
const 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);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
);
|
|
||||||
const 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'
|
||||||
];
|
];
|
||||||
|
let version = undefined;
|
||||||
for (const url of MirrorList) {
|
for (const url of MirrorList) {
|
||||||
const version = await requestMirror(url);
|
try {
|
||||||
|
version = (await RequestUtil.HttpGetJson<{ version: string }>(url)).version;
|
||||||
|
} catch (e) {
|
||||||
|
logDebug('检测更新异常',e);
|
||||||
|
}
|
||||||
if (version) {
|
if (version) {
|
||||||
resolve(version);
|
resolve(version);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
reject('get verison error!');
|
reject('get verison error!');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
File diff suppressed because one or more lines are too long
27
src/core.lib/src/index.d.ts
vendored
27
src/core.lib/src/index.d.ts
vendored
@@ -1,27 +0,0 @@
|
|||||||
/// <reference types="node" />
|
|
||||||
import { GlobalAdapter } from './qqnt/adapters';
|
|
||||||
import { QRCodeLoginSucceedType } from './qqnt/services';
|
|
||||||
import { NapCatCoreWrapper } from './wrapper';
|
|
||||||
import { NapCatCoreLogin } from './login';
|
|
||||||
import { NapCatCoreSession } from './session';
|
|
||||||
import { NapCatCoreService } from './service';
|
|
||||||
import { EventEmitter } from 'node:events';
|
|
||||||
import * as log4js from '@log4js-node/log4js-api';
|
|
||||||
export interface LoginSuccessCallback {
|
|
||||||
(): void | Promise<void>;
|
|
||||||
}
|
|
||||||
export declare class NapCatCore extends EventEmitter {
|
|
||||||
readonly log: log4js.Logger;
|
|
||||||
readonly adapter: GlobalAdapter;
|
|
||||||
readonly wrapper: NapCatCoreWrapper;
|
|
||||||
readonly login: NapCatCoreLogin;
|
|
||||||
readonly session: NapCatCoreSession;
|
|
||||||
readonly service: NapCatCoreService;
|
|
||||||
private loginSuccessCbList;
|
|
||||||
constructor();
|
|
||||||
initPostLogin(args: QRCodeLoginSucceedType): Promise<void>;
|
|
||||||
private onLoginSuccess;
|
|
||||||
private onMessage;
|
|
||||||
addLoginSuccessCallback(cb: LoginSuccessCallback): void;
|
|
||||||
}
|
|
||||||
export declare const napCatCore: NapCatCore;
|
|
File diff suppressed because one or more lines are too long
48
src/core.lib/src/login.d.ts
vendored
48
src/core.lib/src/login.d.ts
vendored
@@ -1,48 +0,0 @@
|
|||||||
import { LoginListener } from './qqnt/listeners';
|
|
||||||
import { LoginInitConfig, NodeIKernelLoginService } from './qqnt/services';
|
|
||||||
import { NapCatCore } from '.';
|
|
||||||
/**
|
|
||||||
* NapCat 登录相关核心类
|
|
||||||
*
|
|
||||||
* **【注意】**:只有在调用 `init` 方法后才会被真正初始化!
|
|
||||||
*/
|
|
||||||
export declare class NapCatCoreLogin {
|
|
||||||
readonly core: NapCatCore;
|
|
||||||
readonly service: NodeIKernelLoginService;
|
|
||||||
readonly listener: LoginListener;
|
|
||||||
constructor(core: NapCatCore);
|
|
||||||
/**
|
|
||||||
* 初始化 `NodeIKernelLoginService`
|
|
||||||
* @param {LoginInitConfig} config `NodeIKernelLoginService` 初始化配置
|
|
||||||
* @returns {void}
|
|
||||||
*/
|
|
||||||
init(config: LoginInitConfig): void;
|
|
||||||
/**
|
|
||||||
* 初始化监听器,用于向父级 `NapCatCore` 发送事件
|
|
||||||
*/
|
|
||||||
private initListener;
|
|
||||||
/**
|
|
||||||
* 获取在此客户端上登录过的账号列表
|
|
||||||
* @returns {Promise<{ result: number, LocalLoginInfoList: LoginListItem[] }>}
|
|
||||||
*/
|
|
||||||
private getLoginList;
|
|
||||||
/**
|
|
||||||
* 使用二维码方式登录账号,获取到的二维码链接可通过 `system.login.qrcode` 事件获取。
|
|
||||||
*/
|
|
||||||
qrcode(): Promise<void>;
|
|
||||||
/**
|
|
||||||
* 使用快速登录方式登录账号,欲登录的账号必须在此客户端上登录过
|
|
||||||
* @param {string} uin 欲登录账户的 Uin
|
|
||||||
* @returns {Promise<void>}
|
|
||||||
*/
|
|
||||||
quick(uin: string): Promise<void>;
|
|
||||||
/**
|
|
||||||
* 使用账号密码方式登录,需要滑块验证会发送 `system.login.slider` 事件,登录错误会发送 `system.login.error` 事件。
|
|
||||||
* @param {string} uin 登录账号
|
|
||||||
* @param {string} password 登录密码
|
|
||||||
* @param {string} [proofSig] 验证码返回的 ticket
|
|
||||||
* @param {string} [proofRand] 验证码返回的随机字符串值
|
|
||||||
* @param {string} [proofSid] 验证码的 sid
|
|
||||||
*/
|
|
||||||
password(uin: string, password: string, proofSig?: string, proofRand?: string, proofSid?: string): Promise<void>;
|
|
||||||
}
|
|
File diff suppressed because one or more lines are too long
@@ -1,14 +0,0 @@
|
|||||||
interface IDependsAdapter {
|
|
||||||
onMSFStatusChange(args: unknown): void;
|
|
||||||
onMSFSsoError(args: unknown): void;
|
|
||||||
getGroupCode(args: unknown): void;
|
|
||||||
}
|
|
||||||
export interface NodeIDependsAdapter extends IDependsAdapter {
|
|
||||||
new (adapter: IDependsAdapter): NodeIDependsAdapter;
|
|
||||||
}
|
|
||||||
export declare class DependsAdapter implements IDependsAdapter {
|
|
||||||
onMSFStatusChange(args: unknown): void;
|
|
||||||
onMSFSsoError(args: unknown): void;
|
|
||||||
getGroupCode(args: unknown): void;
|
|
||||||
}
|
|
||||||
export {};
|
|
@@ -1 +0,0 @@
|
|||||||
var _0x57b317=_0x36a8;function _0x36a8(_0x362896,_0x32c0b4){var _0xa26f0=_0xa26f();return _0x36a8=function(_0x36a8be,_0x3cc4a8){_0x36a8be=_0x36a8be-0x98;var _0x5c3910=_0xa26f0[_0x36a8be];return _0x5c3910;},_0x36a8(_0x362896,_0x32c0b4);}(function(_0x4bfc95,_0x27bdf2){var _0x2d33b2=_0x36a8,_0x2e43ac=_0x4bfc95();while(!![]){try{var _0x101bef=-parseInt(_0x2d33b2(0xa2))/0x1+-parseInt(_0x2d33b2(0xa0))/0x2+-parseInt(_0x2d33b2(0x9e))/0x3+parseInt(_0x2d33b2(0x9d))/0x4*(-parseInt(_0x2d33b2(0x9f))/0x5)+-parseInt(_0x2d33b2(0x98))/0x6+parseInt(_0x2d33b2(0xa1))/0x7+-parseInt(_0x2d33b2(0x99))/0x8*(-parseInt(_0x2d33b2(0x9b))/0x9);if(_0x101bef===_0x27bdf2)break;else _0x2e43ac['push'](_0x2e43ac['shift']());}catch(_0x2e66e0){_0x2e43ac['push'](_0x2e43ac['shift']());}}}(_0xa26f,0x390fc));export class DependsAdapter{[_0x57b317(0x9a)](_0x1a4f3d){}[_0x57b317(0x9c)](_0x160db6){}['getGroupCode'](_0x134d12){}}function _0xa26f(){var _0x1fb9a3=['1770958DwPkqM','174548cPvLOd','912234yRWXqs','88tYhZtg','onMSFStatusChange','728127LcGGeG','onMSFSsoError','4sPbEYq','892254AVBpCc','1155685djlRYh','108122wQMDgr'];_0xa26f=function(){return _0x1fb9a3;};return _0xa26f();}
|
|
@@ -1,14 +0,0 @@
|
|||||||
interface IDispatcherAdapter {
|
|
||||||
dispatchRequest(arg: unknown): void;
|
|
||||||
dispatchCall(arg: unknown): void;
|
|
||||||
dispatchCallWithJson(arg: unknown): void;
|
|
||||||
}
|
|
||||||
export interface NodeIDispatcherAdapter extends IDispatcherAdapter {
|
|
||||||
new (adapter: IDispatcherAdapter): NodeIDispatcherAdapter;
|
|
||||||
}
|
|
||||||
export declare class DispatcherAdapter implements IDispatcherAdapter {
|
|
||||||
dispatchRequest(arg: unknown): void;
|
|
||||||
dispatchCall(arg: unknown): void;
|
|
||||||
dispatchCallWithJson(arg: unknown): void;
|
|
||||||
}
|
|
||||||
export {};
|
|
@@ -1 +0,0 @@
|
|||||||
function _0x4645(_0x29d00c,_0x944e8a){var _0x4a794b=_0x4a79();return _0x4645=function(_0x464531,_0x6a25e2){_0x464531=_0x464531-0xf2;var _0x468ff7=_0x4a794b[_0x464531];return _0x468ff7;},_0x4645(_0x29d00c,_0x944e8a);}function _0x4a79(){var _0x26cc01=['dispatchRequest','311920KeGlva','535480XzIKhH','2056210YksXki','192516eVLrcf','3265535aYTtGW','dispatchCallWithJson','81lllQjj','2970342zDVsGP','4WJlAhF','dispatchCall','108422nvfYww'];_0x4a79=function(){return _0x26cc01;};return _0x4a79();}var _0x569cde=_0x4645;(function(_0x2c0bf7,_0x545202){var _0x2d129d=_0x4645,_0x3a277a=_0x2c0bf7();while(!![]){try{var _0x51083b=parseInt(_0x2d129d(0xfb))/0x1+-parseInt(_0x2d129d(0xf9))/0x2+parseInt(_0x2d129d(0xf2))/0x3*(parseInt(_0x2d129d(0xf7))/0x4)+-parseInt(_0x2d129d(0xfd))/0x5+parseInt(_0x2d129d(0xf6))/0x6+parseInt(_0x2d129d(0xf3))/0x7+parseInt(_0x2d129d(0xfc))/0x8*(-parseInt(_0x2d129d(0xf5))/0x9);if(_0x51083b===_0x545202)break;else _0x3a277a['push'](_0x3a277a['shift']());}catch(_0x2c2a74){_0x3a277a['push'](_0x3a277a['shift']());}}}(_0x4a79,0x41dda));export class DispatcherAdapter{[_0x569cde(0xfa)](_0xde046){}[_0x569cde(0xf8)](_0x3b470d){}[_0x569cde(0xf4)](_0x199a33){}}
|
|
@@ -1,24 +0,0 @@
|
|||||||
interface IGlobalAdapter {
|
|
||||||
onLog(...args: unknown[]): void;
|
|
||||||
onGetSrvCalTime(...args: unknown[]): void;
|
|
||||||
onShowErrUITips(...args: unknown[]): void;
|
|
||||||
fixPicImgType(...args: unknown[]): void;
|
|
||||||
getAppSetting(...args: unknown[]): void;
|
|
||||||
onInstallFinished(...args: unknown[]): void;
|
|
||||||
onUpdateGeneralFlag(...args: unknown[]): void;
|
|
||||||
onGetOfflineMsg(...args: unknown[]): void;
|
|
||||||
}
|
|
||||||
export interface NodeIGlobalAdapter extends IGlobalAdapter {
|
|
||||||
new (adapter: IGlobalAdapter): NodeIGlobalAdapter;
|
|
||||||
}
|
|
||||||
export declare class GlobalAdapter implements IGlobalAdapter {
|
|
||||||
onLog(...args: unknown[]): void;
|
|
||||||
onGetSrvCalTime(...args: unknown[]): void;
|
|
||||||
onShowErrUITips(...args: unknown[]): void;
|
|
||||||
fixPicImgType(...args: unknown[]): void;
|
|
||||||
getAppSetting(...args: unknown[]): void;
|
|
||||||
onInstallFinished(...args: unknown[]): void;
|
|
||||||
onUpdateGeneralFlag(...args: unknown[]): void;
|
|
||||||
onGetOfflineMsg(...args: unknown[]): void;
|
|
||||||
}
|
|
||||||
export {};
|
|
@@ -1 +0,0 @@
|
|||||||
var _0x25a6e4=_0x19d4;(function(_0x17fa2a,_0x5a61f7){var _0x153e10=_0x19d4,_0x335668=_0x17fa2a();while(!![]){try{var _0xacf79e=-parseInt(_0x153e10(0x1ae))/0x1+parseInt(_0x153e10(0x1a2))/0x2*(-parseInt(_0x153e10(0x1a8))/0x3)+-parseInt(_0x153e10(0x19f))/0x4*(parseInt(_0x153e10(0x1a7))/0x5)+-parseInt(_0x153e10(0x1a5))/0x6+-parseInt(_0x153e10(0x1a9))/0x7*(parseInt(_0x153e10(0x1aa))/0x8)+-parseInt(_0x153e10(0x1a4))/0x9+parseInt(_0x153e10(0x1a1))/0xa;if(_0xacf79e===_0x5a61f7)break;else _0x335668['push'](_0x335668['shift']());}catch(_0x3a454c){_0x335668['push'](_0x335668['shift']());}}}(_0x2518,0x4e7b5));function _0x2518(){var _0x273672=['5ylrcJh','26799lvrnMh','2458862bUpwCR','8HhILZy','getAppSetting','onInstallFinished','onGetOfflineMsg','369889pZOrDs','804872ifFlmX','onShowErrUITips','21505450cWWtjO','50cWKjWB','onUpdateGeneralFlag','2306016LtyBae','2562972jZIHTW','fixPicImgType'];_0x2518=function(){return _0x273672;};return _0x2518();}function _0x19d4(_0x2acdec,_0x24fa31){var _0x251832=_0x2518();return _0x19d4=function(_0x19d485,_0x552415){_0x19d485=_0x19d485-0x19f;var _0x18198c=_0x251832[_0x19d485];return _0x18198c;},_0x19d4(_0x2acdec,_0x24fa31);}export class GlobalAdapter{['onLog'](..._0x11fd3b){}['onGetSrvCalTime'](..._0x93f2a8){}[_0x25a6e4(0x1a0)](..._0x537250){}[_0x25a6e4(0x1a6)](..._0x8bd0e2){}[_0x25a6e4(0x1ab)](..._0x40e232){}[_0x25a6e4(0x1ac)](..._0x479fcd){}[_0x25a6e4(0x1a3)](..._0x20cb52){}[_0x25a6e4(0x1ad)](..._0x381ed0){}}
|
|
@@ -1 +0,0 @@
|
|||||||
(function(_0x3f0548,_0x4fbec7){var _0x30f47c=_0x5520,_0x23c841=_0x3f0548();while(!![]){try{var _0x6e452c=-parseInt(_0x30f47c(0xd0))/0x1*(-parseInt(_0x30f47c(0xd3))/0x2)+-parseInt(_0x30f47c(0xd5))/0x3*(-parseInt(_0x30f47c(0xd4))/0x4)+parseInt(_0x30f47c(0xd7))/0x5*(-parseInt(_0x30f47c(0xd1))/0x6)+-parseInt(_0x30f47c(0xcd))/0x7*(parseInt(_0x30f47c(0xcc))/0x8)+parseInt(_0x30f47c(0xd6))/0x9*(-parseInt(_0x30f47c(0xd2))/0xa)+parseInt(_0x30f47c(0xcf))/0xb+parseInt(_0x30f47c(0xce))/0xc;if(_0x6e452c===_0x4fbec7)break;else _0x23c841['push'](_0x23c841['shift']());}catch(_0x49d4c9){_0x23c841['push'](_0x23c841['shift']());}}}(_0x490d,0x33833));export*from'./NodeIDependsAdapter';export*from'./NodeIDispatcherAdapter';export*from'./NodeIGlobalAdapter';function _0x5520(_0x6df29c,_0x34b267){var _0x490de6=_0x490d();return _0x5520=function(_0x552034,_0x30d8c9){_0x552034=_0x552034-0xcc;var _0x1349c1=_0x490de6[_0x552034];return _0x1349c1;},_0x5520(_0x6df29c,_0x34b267);}function _0x490d(){var _0x14daaf=['246HEKUvN','7016JTzFAQ','51ToMDrp','598194CIeOFq','17745KHyvbm','6376SvVkGK','3647qlxjSN','9452604ZWfenL','1399618PDbghS','2082QsnFYw','522UnokdV','40fvQpoj'];_0x490d=function(){return _0x14daaf;};return _0x490d();}
|
|
17
src/core.lib/src/qqnt/apis/file.d.ts
vendored
17
src/core.lib/src/qqnt/apis/file.d.ts
vendored
@@ -1,17 +0,0 @@
|
|||||||
import { ChatType, ElementType } from '@/core/qqnt/entities';
|
|
||||||
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 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): Promise<string>;
|
|
||||||
static getImageSize(filePath: string): Promise<ISizeCalculationResult | undefined>;
|
|
||||||
}
|
|
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