mirror of
https://github.com/NapNeko/NapCatQQ.git
synced 2026-03-02 00:30:25 +00:00
Compare commits
1655 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fba2078fc0 | ||
|
|
20a37fe2de | ||
|
|
8f6d26b65c | ||
|
|
b58a194c8a | ||
|
|
52f1b0a0ce | ||
|
|
c2b8fb223b | ||
|
|
20e4eff899 | ||
|
|
0efcca36d2 | ||
|
|
ab417802a0 | ||
|
|
73d68cce4a | ||
|
|
e4ac2de660 | ||
|
|
8d1241808a | ||
|
|
b810040145 | ||
|
|
c01d7dae2d | ||
|
|
dfca3c2483 | ||
|
|
1bb4be086f | ||
|
|
fd226c45f6 | ||
|
|
21fed5b25f | ||
|
|
dde093d321 | ||
|
|
b99fb247ac | ||
|
|
28930fdad4 | ||
|
|
ea4d1d3275 | ||
|
|
62e852d510 | ||
|
|
7ddd4d6461 | ||
|
|
6b9307de2a | ||
|
|
234046ce10 | ||
|
|
73b29cf1e2 | ||
|
|
4b3bf170c0 | ||
|
|
a7fbaba2d7 | ||
|
|
fc79241f3d | ||
|
|
a88c37ea56 | ||
|
|
9b2358b7f1 | ||
|
|
257135763f | ||
|
|
610a3499f2 | ||
|
|
69752b8837 | ||
|
|
610473b57c | ||
|
|
1e5721d7d5 | ||
|
|
6c2b45679a | ||
|
|
6785922379 | ||
|
|
4e85124aeb | ||
|
|
6b30a03f55 | ||
|
|
876894d8c6 | ||
|
|
ea20d94146 | ||
|
|
c7669777cb | ||
|
|
3b43bba4a0 | ||
|
|
0ab4946bf1 | ||
|
|
a7fb18d5c0 | ||
|
|
a0fbb0f861 | ||
|
|
e6c93ab1c0 | ||
|
|
7152213344 | ||
|
|
a8e913cfde | ||
|
|
4ac074f3dd | ||
|
|
436249597d | ||
|
|
016a742d90 | ||
|
|
6d863ac29c | ||
|
|
ae981fe57d | ||
|
|
0c6a75b722 | ||
|
|
bfd9b1b7c7 | ||
|
|
12f6b1ca45 | ||
|
|
04264110ee | ||
|
|
e4a112c329 | ||
|
|
ef4dee8886 | ||
|
|
e7ee21ca30 | ||
|
|
23ee480c4f | ||
|
|
7816271302 | ||
|
|
b57814f14a | ||
|
|
b18e86f81c | ||
|
|
7b1b503703 | ||
|
|
32d4febf10 | ||
|
|
814973af58 | ||
|
|
ecee642e10 | ||
|
|
9afc0f6667 | ||
|
|
e9e517533a | ||
|
|
4a531ccea1 | ||
|
|
fb8e0595c2 | ||
|
|
d748d6e400 | ||
|
|
6b99fa1f24 | ||
|
|
ca5abc635c | ||
|
|
35e75be0d0 | ||
|
|
cf401a659d | ||
|
|
bd56968efb | ||
|
|
a78bc686cd | ||
|
|
ad8c962c25 | ||
|
|
be91976498 | ||
|
|
57821b839e | ||
|
|
ad334ed09f | ||
|
|
a955937e02 | ||
|
|
3c42cc17c8 | ||
|
|
deeab036d3 | ||
|
|
a1badcd9a1 | ||
|
|
52762438c6 | ||
|
|
3294079b72 | ||
|
|
1c6bdf20b6 | ||
|
|
fac00be995 | ||
|
|
e7e8e99946 | ||
|
|
9f9749548a | ||
|
|
db1ac85acf | ||
|
|
d5eaeb429a | ||
|
|
4e7595d8d1 | ||
|
|
f25fdcdc3d | ||
|
|
7a4de75e07 | ||
|
|
545b57a57d | ||
|
|
32c3aa7979 | ||
|
|
013f703241 | ||
|
|
c463ad5fd6 | ||
|
|
412b8473fe | ||
|
|
02df2132b4 | ||
|
|
a964d5c93f | ||
|
|
eafc32a915 | ||
|
|
df4b84b4b9 | ||
|
|
6e094eb4fc | ||
|
|
9e7d7bcb4c | ||
|
|
63b3cc8c02 | ||
|
|
7a88786685 | ||
|
|
427889f8ca | ||
|
|
82c9c28439 | ||
|
|
c84c1f2e96 | ||
|
|
a3ee8672ed | ||
|
|
4cfde09016 | ||
|
|
0b8dcbebe9 | ||
|
|
aa12506221 | ||
|
|
39ed9dea01 | ||
|
|
6f095470ad | ||
|
|
2a5d2cc146 | ||
|
|
b5e8218551 | ||
|
|
062cc307fb | ||
|
|
e99ff1be35 | ||
|
|
404a213896 | ||
|
|
0a07f16ef6 | ||
|
|
f9bf8f9901 | ||
|
|
b6ae67bf3e | ||
|
|
191ce0798f | ||
|
|
40362590c8 | ||
|
|
87f6dc7c0b | ||
|
|
2f2c1f263a | ||
|
|
8841cbb3d0 | ||
|
|
a2e20a8092 | ||
|
|
6a7c7a0ab5 | ||
|
|
44a8c8e35d | ||
|
|
1cbfccc4eb | ||
|
|
e12c0b5536 | ||
|
|
b7a8781308 | ||
|
|
73a8fcd35b | ||
|
|
a2ad39f78d | ||
|
|
832635d6f5 | ||
|
|
dacb56bc20 | ||
|
|
e5a9821027 | ||
|
|
bbe666eb73 | ||
|
|
000cb3d80c | ||
|
|
40f85dbf5f | ||
|
|
d6646ebadf | ||
|
|
e02bddc78f | ||
|
|
08e679184b | ||
|
|
5c877e894b | ||
|
|
5918f03cb1 | ||
|
|
78263d716c | ||
|
|
15f4841328 | ||
|
|
ae5d50141b | ||
|
|
8839563ff8 | ||
|
|
6d954b2d5d | ||
|
|
6e125f15a4 | ||
|
|
e344921a06 | ||
|
|
4cbaf0dc70 | ||
|
|
ef7d2f4a82 | ||
|
|
5f7d998b0b | ||
|
|
2c14281168 | ||
|
|
9feab4bc79 | ||
|
|
63237bc112 | ||
|
|
99f4752c89 | ||
|
|
ef1ed5aa8b | ||
|
|
1258270ac4 | ||
|
|
bb7a2f5f6c | ||
|
|
c865d32d95 | ||
|
|
87c3b24488 | ||
|
|
5a5257294b | ||
|
|
a9ca951854 | ||
|
|
2a9353ee70 | ||
|
|
18e134b92a | ||
|
|
6c87e15a52 | ||
|
|
a710821c35 | ||
|
|
de4aeedce5 | ||
|
|
1f71a01453 | ||
|
|
6371d79d33 | ||
|
|
80d2218aa6 | ||
|
|
bc636f109c | ||
|
|
76d58af4d8 | ||
|
|
84e5417a8c | ||
|
|
89188958ec | ||
|
|
b5d24d751d | ||
|
|
3269061db4 | ||
|
|
9f576f43cc | ||
|
|
505c6e0e0e | ||
|
|
9936b49ee0 | ||
|
|
707bc765b9 | ||
|
|
8780c987ea | ||
|
|
7aa01f786d | ||
|
|
340e94d54e | ||
|
|
509b123064 | ||
|
|
8060b1c753 | ||
|
|
33ef3e7d59 | ||
|
|
704e5f7134 | ||
|
|
91bc3ab525 | ||
|
|
2e6bded9d0 | ||
|
|
c6e980ed96 | ||
|
|
32a932ad5c | ||
|
|
f6d2bd04e9 | ||
|
|
b0266b470f | ||
|
|
0ed969fa3f | ||
|
|
90a7b5e0d3 | ||
|
|
c5bf656fe7 | ||
|
|
6bce4533a3 | ||
|
|
8a8aa0016e | ||
|
|
7e4ebd330c | ||
|
|
c304845117 | ||
|
|
ee85f3e824 | ||
|
|
3f6f1dcd78 | ||
|
|
c271a4b2cb | ||
|
|
fa07dfb720 | ||
|
|
6f6b258f22 | ||
|
|
717b246cb6 | ||
|
|
5990e0c2eb | ||
|
|
827df80ec8 | ||
|
|
7117fae2b2 | ||
|
|
15e9462140 | ||
|
|
714f8327ea | ||
|
|
fedb77e304 | ||
|
|
06d2884a88 | ||
|
|
b50556802c | ||
|
|
b18dfdb9ba | ||
|
|
173f83808e | ||
|
|
fbe2d78331 | ||
|
|
e5fd9c6366 | ||
|
|
3623b991ff | ||
|
|
2cabd7879c | ||
|
|
a1a378d6f5 | ||
|
|
b016268fdb | ||
|
|
dfb31b78d9 | ||
|
|
4eed603d36 | ||
|
|
cdc10d6c4b | ||
|
|
cb03501eff | ||
|
|
c2e28ab5a6 | ||
|
|
24a166cb94 | ||
|
|
1d3ac0c9b3 | ||
|
|
ff29b62398 | ||
|
|
d9e016db8b | ||
|
|
6cfd50a7b8 | ||
|
|
6605d3812a | ||
|
|
be71abe580 | ||
|
|
518ff48e97 | ||
|
|
8a4add257f | ||
|
|
7842cd0bc0 | ||
|
|
ffe480ad44 | ||
|
|
e4d3f95257 | ||
|
|
245eabe85f | ||
|
|
22e7eb1ffc | ||
|
|
d663a58d65 | ||
|
|
fa5d5f1bcc | ||
|
|
7178095aef | ||
|
|
4704faa011 | ||
|
|
0f77d9df1f | ||
|
|
e02c3fca8b | ||
|
|
abe0838a63 | ||
|
|
6583e3d0c9 | ||
|
|
2093d68bfb | ||
|
|
d3a55d50c0 | ||
|
|
471733b243 | ||
|
|
47fa717bce | ||
|
|
cfc68e70b6 | ||
|
|
bd9ee62118 | ||
|
|
aaa874b099 | ||
|
|
958709faf2 | ||
|
|
52ab93013c | ||
|
|
9203fa3df2 | ||
|
|
9ef3edabce | ||
|
|
c771d75a00 | ||
|
|
1db27ab0e3 | ||
|
|
cd45f7051c | ||
|
|
588ea7978e | ||
|
|
946f12cf6a | ||
|
|
7e49bfa984 | ||
|
|
7b10d75aeb | ||
|
|
34e4963ccd | ||
|
|
9fc9ed805c | ||
|
|
db4c5bc3a3 | ||
|
|
024faa2561 | ||
|
|
b46459de5f | ||
|
|
ac7f025223 | ||
|
|
e4343650c6 | ||
|
|
80c5259b05 | ||
|
|
004a65d933 | ||
|
|
38289918c2 | ||
|
|
0e46f9f213 | ||
|
|
be03b973e5 | ||
|
|
a0bf2b3d3d | ||
|
|
755ab36e83 | ||
|
|
30975f7360 | ||
|
|
828307fc52 | ||
|
|
e79ca4fa4c | ||
|
|
43288eb5c0 | ||
|
|
9518595e48 | ||
|
|
3c4cd3743f | ||
|
|
b39cbffe14 | ||
|
|
f588d3f35b | ||
|
|
54b06872eb | ||
|
|
0786c608a4 | ||
|
|
c70b2eaa30 | ||
|
|
c417a95e1f | ||
|
|
5ae9be0291 | ||
|
|
e2a6e3ea58 | ||
|
|
8fd5fa185b | ||
|
|
66707661e9 | ||
|
|
4e18ec5951 | ||
|
|
05b77b5042 | ||
|
|
d6cfe11d97 | ||
|
|
dd4d59e4e7 | ||
|
|
7cb8626e16 | ||
|
|
89b5202adb | ||
|
|
40ae0c1449 | ||
|
|
81a8115c56 | ||
|
|
0ddd26bd51 | ||
|
|
69e2133a27 | ||
|
|
b04f85949b | ||
|
|
667dba01ae | ||
|
|
85a8cef628 | ||
|
|
3099acfd00 | ||
|
|
b00fe0b5f8 | ||
|
|
9a64b8bdb6 | ||
|
|
c01e493bd2 | ||
|
|
890236af23 | ||
|
|
ea678d805d | ||
|
|
29699418ff | ||
|
|
87bb36c39f | ||
|
|
7cb85ed73c | ||
|
|
c647771a6d | ||
|
|
6c3d737219 | ||
|
|
a1f38fed7a | ||
|
|
c36bb77286 | ||
|
|
1a1acdc3c9 | ||
|
|
ef8e60c405 | ||
|
|
31c1cc47bf | ||
|
|
351fed7359 | ||
|
|
f49e7cbe57 | ||
|
|
7da8ea5e99 | ||
|
|
8fa6a12a7c | ||
|
|
1070278eaf | ||
|
|
16b1a6b153 | ||
|
|
096a7534e0 | ||
|
|
b0897187d2 | ||
|
|
885d94882d | ||
|
|
11a3341e13 | ||
|
|
231890f78a | ||
|
|
a272feda6a | ||
|
|
bc936a0ca7 | ||
|
|
28030c2d13 | ||
|
|
7fe9176286 | ||
|
|
1105f9b8d6 | ||
|
|
e4653defa8 | ||
|
|
1c62a1e839 | ||
|
|
3a3bbfe201 | ||
|
|
1c38833998 | ||
|
|
38894177ee | ||
|
|
dce8416942 | ||
|
|
14219e9b42 | ||
|
|
7b459e7502 | ||
|
|
31824c0504 | ||
|
|
e203abae85 | ||
|
|
faf83b680b | ||
|
|
67dcbcb842 | ||
|
|
6533a25404 | ||
|
|
4dc760b0e9 | ||
|
|
25933b9043 | ||
|
|
a53aaa456e | ||
|
|
e8a7ea07a5 | ||
|
|
8817dc6b10 | ||
|
|
491ec04b46 | ||
|
|
8a5d4a683b | ||
|
|
dfc7c7357a | ||
|
|
690a2f7d34 | ||
|
|
58f22b24e4 | ||
|
|
3cce9f528b | ||
|
|
20fd5ac8cb | ||
|
|
9e05e086eb | ||
|
|
056e0adddf | ||
|
|
b36388200d | ||
|
|
9793e5741a | ||
|
|
143380c012 | ||
|
|
4b92254945 | ||
|
|
f9c1d8b4a6 | ||
|
|
c0c469339b | ||
|
|
0ca6343ed7 | ||
|
|
3db74c3427 | ||
|
|
48d5cb53bd | ||
|
|
fd7d2dbf53 | ||
|
|
6609697752 | ||
|
|
dcd6e1973e | ||
|
|
3614a6e932 | ||
|
|
931a0210e5 | ||
|
|
f9e7de4b42 | ||
|
|
8e0b79594e | ||
|
|
17122c4360 | ||
|
|
154f7b6a30 | ||
|
|
52e5543d0b | ||
|
|
3c304bd2ae | ||
|
|
26609bb8fd | ||
|
|
de3fa9aaa4 | ||
|
|
788665f84c | ||
|
|
3943782971 | ||
|
|
8f899c40f2 | ||
|
|
a1f582399e | ||
|
|
440b63f662 | ||
|
|
7d2cc3b56b | ||
|
|
5fe3422469 | ||
|
|
6c02cedb1e | ||
|
|
3cc2f1dcad | ||
|
|
773cdc5877 | ||
|
|
361a7329d7 | ||
|
|
29910f1236 | ||
|
|
4a164016f5 | ||
|
|
cebd3e62a4 | ||
|
|
2562a38fa1 | ||
|
|
d46c922bbf | ||
|
|
66b59982f7 | ||
|
|
ad397ccf7f | ||
|
|
bdef80ede7 | ||
|
|
385dcbc75a | ||
|
|
74cf501c8f | ||
|
|
0c200d6748 | ||
|
|
e65a36c517 | ||
|
|
126b54ad40 | ||
|
|
78637751af | ||
|
|
f96526ee3a | ||
|
|
b3c7a91f3d | ||
|
|
b8daeef0c4 | ||
|
|
2b662944cf | ||
|
|
3d516df01e | ||
|
|
26b4a9b15b | ||
|
|
0f8af273ae | ||
|
|
fa29a31da9 | ||
|
|
9e0d2606d8 | ||
|
|
338dedd6e0 | ||
|
|
1f893b1393 | ||
|
|
b783d6f928 | ||
|
|
8ca0d40f05 | ||
|
|
2f40a80434 | ||
|
|
812d8eb5bb | ||
|
|
bf5f548349 | ||
|
|
ebf90e72b9 | ||
|
|
31d7d42edf | ||
|
|
833875b42f | ||
|
|
b901c10f3c | ||
|
|
8ab678bd97 | ||
|
|
55d5072f46 | ||
|
|
a379ffd0f2 | ||
|
|
4501d73134 | ||
|
|
5f15774ec7 | ||
|
|
c9e057599e | ||
|
|
66851f5625 | ||
|
|
b33c235b4d | ||
|
|
d6693b6114 | ||
|
|
36b4d26c78 | ||
|
|
617592d90a | ||
|
|
15a77b8070 | ||
|
|
606eccd22b | ||
|
|
5613450313 | ||
|
|
c59b5564af | ||
|
|
330b086b8b | ||
|
|
9837ef4f36 | ||
|
|
add46b3251 | ||
|
|
e169199107 | ||
|
|
92fe654850 | ||
|
|
b257486404 | ||
|
|
bdf2e33f40 | ||
|
|
224d361923 | ||
|
|
3452fa56df | ||
|
|
cd256235da | ||
|
|
361a164f2a | ||
|
|
0e60d4b198 | ||
|
|
67b47e39b4 | ||
|
|
8f54310f63 | ||
|
|
c7a7494d7e | ||
|
|
af88b3166d | ||
|
|
b7837b2a14 | ||
|
|
950ddc749e | ||
|
|
df081ef0cf | ||
|
|
7b24f90d9f | ||
|
|
f2e4579fd8 | ||
|
|
97cb351827 | ||
|
|
c1ec53fdbb | ||
|
|
98214aa429 | ||
|
|
ce7deac2dd | ||
|
|
612092b867 | ||
|
|
92579d5949 | ||
|
|
9ab07060ae | ||
|
|
0d45125d79 | ||
|
|
9ced152778 | ||
|
|
3685ab2e3e | ||
|
|
be605f11f2 | ||
|
|
8cca8df976 | ||
|
|
990a31e961 | ||
|
|
5db201c342 | ||
|
|
a625e30dd4 | ||
|
|
b236cdd060 | ||
|
|
2db9899184 | ||
|
|
fe5d6db986 | ||
|
|
7c7bf8fecf | ||
|
|
76e3a46378 | ||
|
|
16f3897fec | ||
|
|
045e120854 | ||
|
|
2b7fcce9b2 | ||
|
|
9685931694 | ||
|
|
1dc844435a | ||
|
|
18892379de | ||
|
|
620d61c8dc | ||
|
|
9f91398875 | ||
|
|
a29b1154a9 | ||
|
|
34d19a471a | ||
|
|
2ef6477d7c | ||
|
|
26e6800836 | ||
|
|
9dbbcf3872 | ||
|
|
6b3343e1e4 | ||
|
|
ef48f754a5 | ||
|
|
3be1ede847 | ||
|
|
7bff1b61e8 | ||
|
|
6affd0eb68 | ||
|
|
0a112d15e0 | ||
|
|
4e03f582bb | ||
|
|
8f186c1c5e | ||
|
|
cd1bae9a1f | ||
|
|
60796c26ca | ||
|
|
28927f950d | ||
|
|
95f16ebc8c | ||
|
|
25bca8385d | ||
|
|
965c7f23b4 | ||
|
|
33082af9cc | ||
|
|
1f7f3565b0 | ||
|
|
f784363696 | ||
|
|
37bd51e138 | ||
|
|
c367728c43 | ||
|
|
61f0f5d884 | ||
|
|
5f2ebeead7 | ||
|
|
7646037fc7 | ||
|
|
eac6d285ff | ||
|
|
b921d5e734 | ||
|
|
831d808e63 | ||
|
|
451b88d7e3 | ||
|
|
f916682a71 | ||
|
|
2d76bcf0cf | ||
|
|
8db294efe6 | ||
|
|
5cc5149aed | ||
|
|
7ecb01dc9f | ||
|
|
8bf1a545d9 | ||
|
|
efb2be2f94 | ||
|
|
bd2edda494 | ||
|
|
991172eae4 | ||
|
|
fc7631f9aa | ||
|
|
a21efb7d2f | ||
|
|
03bc844ad0 | ||
|
|
f7bdc35ed6 | ||
|
|
0efdffd857 | ||
|
|
6a16a42d0c | ||
|
|
e9c00c72b1 | ||
|
|
ab22f36b8a | ||
|
|
c57497cd91 | ||
|
|
ba123236e5 | ||
|
|
338b6e4607 | ||
|
|
f88c717560 | ||
|
|
f8ffc92db5 | ||
|
|
98c23c172c | ||
|
|
781c107d8c | ||
|
|
186668c075 | ||
|
|
cf9f785193 | ||
|
|
72d2d3f224 | ||
|
|
087c76b394 | ||
|
|
4f9fb2c8c3 | ||
|
|
334e43e764 | ||
|
|
7843256402 | ||
|
|
0522ba35fe | ||
|
|
24d3b52e0b | ||
|
|
3177110f0f | ||
|
|
e1b8243a67 | ||
|
|
b1c6ce3885 | ||
|
|
0b4b25a11e | ||
|
|
1176fe984a | ||
|
|
6ca768c3ee | ||
|
|
3da1659c8d | ||
|
|
9aa4cd319c | ||
|
|
5af866cdca | ||
|
|
2b421fa447 | ||
|
|
30ec964325 | ||
|
|
714d7d72eb | ||
|
|
687aa0f363 | ||
|
|
8363ab07a7 | ||
|
|
c46a757339 | ||
|
|
557864395b | ||
|
|
3f7a85d80b | ||
|
|
8d18d2ce1f | ||
|
|
7141ba1587 | ||
|
|
44d350a225 | ||
|
|
239b8e72d9 | ||
|
|
279bdb6fb1 | ||
|
|
a0cea819da | ||
|
|
9ab7f60544 | ||
|
|
aaf7191bf3 | ||
|
|
628c9be0c8 | ||
|
|
733052720c | ||
|
|
a10f007194 | ||
|
|
6fa50c58d3 | ||
|
|
c54a58d6e4 | ||
|
|
34cb1ea3fd | ||
|
|
f640b0ca91 | ||
|
|
60e16da42e | ||
|
|
0cdceb95d6 | ||
|
|
70bd22d925 | ||
|
|
82462dd647 | ||
|
|
c0466e943d | ||
|
|
b187b4695d | ||
|
|
b1956d2a37 | ||
|
|
590b622e5f | ||
|
|
3d8174396a | ||
|
|
b8dc6e9bd9 | ||
|
|
8ee99109dc | ||
|
|
902041d4ee | ||
|
|
cc34aef47e | ||
|
|
0afbbe7c7a | ||
|
|
8004553ba7 | ||
|
|
0023b2846a | ||
|
|
34775c1816 | ||
|
|
e0759e704b | ||
|
|
0aa225ca78 | ||
|
|
b43b4ee5c0 | ||
|
|
0cdb8cecbf | ||
|
|
fd6a306742 | ||
|
|
7f3b3d2277 | ||
|
|
8be5b977bf | ||
|
|
d7ddb15f9c | ||
|
|
9a6a1798d0 | ||
|
|
14196fd349 | ||
|
|
941b89a523 | ||
|
|
a5f9e5f8c0 | ||
|
|
80c3356c8f | ||
|
|
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 | ||
|
|
6fe5cb1ffd | ||
|
|
7edd5a7a8e | ||
|
|
c1edc1b99b | ||
|
|
4d1d890f72 | ||
|
|
fe0f82fa2b | ||
|
|
84083a65a8 | ||
|
|
fc91c6bc08 | ||
|
|
09120171ba | ||
|
|
a362f920dc | ||
|
|
9d7729f548 | ||
|
|
ed56e177cf | ||
|
|
9db28bd502 | ||
|
|
aded70eb2e | ||
|
|
dfbad85465 | ||
|
|
52076fe182 | ||
|
|
5575c3cb13 | ||
|
|
637d32efff | ||
|
|
fd54658e53 | ||
|
|
2f39a8d76e | ||
|
|
6a3e793500 | ||
|
|
3b3ffeda6b | ||
|
|
f7d92a3b11 | ||
|
|
d9d9ba8bf1 | ||
|
|
f5d9090183 | ||
|
|
705ecd1ef1 | ||
|
|
08b5266a86 | ||
|
|
ecc4846ba8 | ||
|
|
4aab705d11 | ||
|
|
4615a68bcc | ||
|
|
bf6934e8ac | ||
|
|
af8c304bd4 | ||
|
|
51dac5a5a8 | ||
|
|
56463d9e36 | ||
|
|
a6a339dc59 | ||
|
|
8423304ab5 | ||
|
|
bb7408dbe9 | ||
|
|
7eff4dcf02 | ||
|
|
d7ee3fec3d | ||
|
|
5e026a3e8d | ||
|
|
d5e117b89f | ||
|
|
c87a5501df | ||
|
|
7584ebba0b | ||
|
|
66075e3960 | ||
|
|
193ba781a0 | ||
|
|
3e5dd64acc | ||
|
|
d66ab7d389 | ||
|
|
d2e6b27ecd | ||
|
|
0588541357 | ||
|
|
096ea84af6 | ||
|
|
04d0cfd510 | ||
|
|
7653f969ec | ||
|
|
c4ab6a4a8d | ||
|
|
d1ecd1318f | ||
|
|
8d65b1427d | ||
|
|
e693a6057e | ||
|
|
d0aa490ac3 | ||
|
|
0b6cad7d4f | ||
|
|
14e6c6d9a6 | ||
|
|
b2061347a5 | ||
|
|
79979f0a3b | ||
|
|
48d70b2349 | ||
|
|
b1f6309662 | ||
|
|
1acde76292 | ||
|
|
3b4867d7ab | ||
|
|
5ae965f4d3 | ||
|
|
742427b77b | ||
|
|
3d70a101f1 | ||
|
|
99062a5ea3 | ||
|
|
056d87f7ae | ||
|
|
df5a58772c | ||
|
|
e45db05b8e | ||
|
|
d234f74703 | ||
|
|
3ae2a1be4a | ||
|
|
2b828abd90 | ||
|
|
4b598b1575 |
21
.editorconfig
Normal file
21
.editorconfig
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
# EditorConfig is awesome: https://EditorConfig.org
|
||||||
|
|
||||||
|
# top-most EditorConfig file
|
||||||
|
root = true
|
||||||
|
|
||||||
|
# Unix-style newlines with a newline ending every file
|
||||||
|
[*]
|
||||||
|
end_of_line = lf
|
||||||
|
insert_final_newline = true
|
||||||
|
|
||||||
|
# Matches multiple files with brace expansion notation
|
||||||
|
# Set default charset
|
||||||
|
charset = utf-8
|
||||||
|
|
||||||
|
# 2 space indentation
|
||||||
|
[*.{cjs,mjs,js,jsx,ts,tsx,css,scss,sass,html,json}]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 4
|
||||||
|
|
||||||
|
# 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.
|
||||||
2
.env.framework
Normal file
2
.env.framework
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
VITE_BUILD_TYPE = Production
|
||||||
|
VITE_BUILD_PLATFORM = Framework
|
||||||
2
.env.shell
Normal file
2
.env.shell
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
VITE_BUILD_TYPE = Production
|
||||||
|
VITE_BUILD_PLATFORM = Shell
|
||||||
64
.eslintrc.cjs
Normal file
64
.eslintrc.cjs
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
module.exports = {
|
||||||
|
'env': {
|
||||||
|
'browser': true,
|
||||||
|
'es2021': true,
|
||||||
|
'node': true
|
||||||
|
},
|
||||||
|
'ignorePatterns': ['src/proto/'],
|
||||||
|
'extends': [
|
||||||
|
'eslint:recommended',
|
||||||
|
'plugin:@typescript-eslint/recommended'
|
||||||
|
],
|
||||||
|
'overrides': [
|
||||||
|
{
|
||||||
|
'env': {
|
||||||
|
'node': true
|
||||||
|
},
|
||||||
|
'files': [
|
||||||
|
'.eslintrc.{js,cjs}'
|
||||||
|
],
|
||||||
|
'parserOptions': {
|
||||||
|
'sourceType': 'script'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
'parser': '@typescript-eslint/parser',
|
||||||
|
'parserOptions': {
|
||||||
|
'ecmaVersion': 'latest',
|
||||||
|
'sourceType': 'module'
|
||||||
|
},
|
||||||
|
'plugins': [
|
||||||
|
'@typescript-eslint',
|
||||||
|
'import'
|
||||||
|
],
|
||||||
|
'settings': {
|
||||||
|
'import/parsers': {
|
||||||
|
'@typescript-eslint/parser': ['.ts']
|
||||||
|
},
|
||||||
|
'import/resolver': {
|
||||||
|
'typescript': {
|
||||||
|
'alwaysTryTypes': true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'rules': {
|
||||||
|
'indent': [
|
||||||
|
'error',
|
||||||
|
4
|
||||||
|
],
|
||||||
|
'linebreak-style': [
|
||||||
|
'error',
|
||||||
|
'unix'
|
||||||
|
],
|
||||||
|
'semi': [
|
||||||
|
'error',
|
||||||
|
'always'
|
||||||
|
],
|
||||||
|
'no-unused-vars': 'off',
|
||||||
|
'no-async-promise-executor': 'off',
|
||||||
|
'@typescript-eslint/no-explicit-any': 'off',
|
||||||
|
'@typescript-eslint/no-unused-vars': 'off',
|
||||||
|
'@typescript-eslint/no-var-requires': 'off',
|
||||||
|
'object-curly-spacing': ['error', 'always'],
|
||||||
|
}
|
||||||
|
};
|
||||||
81
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
Normal file
81
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
name: Bug 反馈
|
||||||
|
description: 报告可能的 NapCat 异常行为
|
||||||
|
title: '[BUG] '
|
||||||
|
labels: bug
|
||||||
|
body:
|
||||||
|
- type: markdown
|
||||||
|
attributes:
|
||||||
|
value: |
|
||||||
|
欢迎来到 NapCat 的 Issue Tracker!请填写以下表格来提交 Bug。
|
||||||
|
在提交新的 Bug 反馈前,请确保您:
|
||||||
|
* 已经搜索了现有的 issues,并且没有找到可以解决您问题的方法
|
||||||
|
* 不与现有的某一 issue 重复
|
||||||
|
- type: input
|
||||||
|
id: system-version
|
||||||
|
attributes:
|
||||||
|
label: 系统版本
|
||||||
|
description: 运行 QQNT 的系统版本
|
||||||
|
placeholder: Windows 10 Pro Workstation 22H2
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: input
|
||||||
|
id: qqnt-version
|
||||||
|
attributes:
|
||||||
|
label: QQNT 版本
|
||||||
|
description: 可在 QQNT 的「关于」的设置页中找到
|
||||||
|
placeholder: 9.9.7-21804
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: input
|
||||||
|
id: napcat-version
|
||||||
|
attributes:
|
||||||
|
label: NapCat 版本
|
||||||
|
description: 可在 LiteLoaderQQNT 的设置页或是 QQNT 的设置页侧栏中找到
|
||||||
|
placeholder: 1.0.0
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: input
|
||||||
|
id: onebot-client-version
|
||||||
|
attributes:
|
||||||
|
label: OneBot 客户端
|
||||||
|
description: 连接至 NapCat 的客户端版本信息
|
||||||
|
placeholder: Overflow 2.16.0-2cf7991-SNAPSHOT
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: what-happened
|
||||||
|
attributes:
|
||||||
|
label: 发生了什么?
|
||||||
|
description: 填写你认为的 NapCat 的不正常行为
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: how-reproduce
|
||||||
|
attributes:
|
||||||
|
label: 如何复现
|
||||||
|
description: 填写应当如何操作才能触发这个不正常行为
|
||||||
|
placeholder: |
|
||||||
|
1. xxx
|
||||||
|
2. xxx
|
||||||
|
3. xxx
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: what-expected
|
||||||
|
attributes:
|
||||||
|
label: 期望的结果?
|
||||||
|
description: 填写你认为 NapCat 应当执行的正常行为
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: napcat-log
|
||||||
|
attributes:
|
||||||
|
label: NapCat 运行日志
|
||||||
|
description: 粘贴相关日志内容到此处
|
||||||
|
render: shell
|
||||||
|
- type: textarea
|
||||||
|
id: onebot-client-log
|
||||||
|
attributes:
|
||||||
|
label: OneBot 客户端运行日志
|
||||||
|
description: 粘贴 OneBot 客户端的相关日志内容到此处
|
||||||
|
render: shell
|
||||||
11
.github/dependabot.yml
vendored
Normal file
11
.github/dependabot.yml
vendored
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
# To get started with Dependabot version updates, you'll need to specify which
|
||||||
|
# package ecosystems to update and where the package manifests are located.
|
||||||
|
# Please see the documentation for all configuration options:
|
||||||
|
# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
|
||||||
|
|
||||||
|
version: 2
|
||||||
|
updates:
|
||||||
|
- package-ecosystem: "npm" # See documentation for possible values
|
||||||
|
directory: "/" # Location of package manifests
|
||||||
|
schedule:
|
||||||
|
interval: "daily"
|
||||||
66
.github/workflows/build.yml
vendored
Normal file
66
.github/workflows/build.yml
vendored
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
name: "Build Action"
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
|
||||||
|
permissions: write-all
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
Build-LiteLoader:
|
||||||
|
if: ${{ startsWith(github.event.head_commit.message, 'build:') }}
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
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 Framework
|
||||||
|
run: |
|
||||||
|
npm i
|
||||||
|
npm run build:framework
|
||||||
|
cd dist
|
||||||
|
npm i --omit=dev
|
||||||
|
rm package-lock.json
|
||||||
|
cd ..
|
||||||
|
- name: Upload Artifact
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: NapCat.Framework
|
||||||
|
path: dist
|
||||||
|
Build-Shell:
|
||||||
|
if: ${{ startsWith(github.event.head_commit.message, 'build:') }}
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
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 LiteLoader
|
||||||
|
run: |
|
||||||
|
npm i
|
||||||
|
npm run build:shell
|
||||||
|
cd dist
|
||||||
|
npm i --omit=dev
|
||||||
|
rm package-lock.json
|
||||||
|
cd ..
|
||||||
|
- name: Upload Artifact
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: NapCat.Shell
|
||||||
|
path: dist
|
||||||
123
.github/workflows/release.yml
vendored
Normal file
123
.github/workflows/release.yml
vendored
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
name: "Build Release"
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- "v*"
|
||||||
|
|
||||||
|
permissions: write-all
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
check-version:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Clone Repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
ref: main
|
||||||
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Extract version from tag
|
||||||
|
run: echo "VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Use Node.js 20.X
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: 20.x
|
||||||
|
|
||||||
|
- name: Check Version
|
||||||
|
run: |
|
||||||
|
ls
|
||||||
|
node ./script/checkVersion.cjs
|
||||||
|
sh ./checkVersion.sh
|
||||||
|
Build-LiteLoader:
|
||||||
|
needs: [check-version]
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
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 Framework
|
||||||
|
run: |
|
||||||
|
npm i
|
||||||
|
npm run build:framework
|
||||||
|
cd dist
|
||||||
|
npm i --omit=dev
|
||||||
|
cd ..
|
||||||
|
- name: Upload Artifact
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: NapCat.Framework
|
||||||
|
path: dist
|
||||||
|
Build-Shell:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: [check-version]
|
||||||
|
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 Shell
|
||||||
|
run: |
|
||||||
|
npm i
|
||||||
|
npm run build:shell
|
||||||
|
cd dist
|
||||||
|
npm i --omit=dev
|
||||||
|
cd ..
|
||||||
|
|
||||||
|
- name: Upload Artifact
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: NapCat.Shell
|
||||||
|
path: dist
|
||||||
|
|
||||||
|
release-napcat:
|
||||||
|
needs: [Build-LiteLoader,Build-Shell]
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Download All Artifact
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
|
||||||
|
- name: Compress subdirectories
|
||||||
|
run: |
|
||||||
|
for dir in */; do
|
||||||
|
base=$(basename "$dir")
|
||||||
|
zip -r "${base}.zip" "$dir"
|
||||||
|
done
|
||||||
|
|
||||||
|
- name: Extract version from tag
|
||||||
|
run: echo "VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Clone Changes Log
|
||||||
|
run: curl -o CHANGELOG.md https://fastly.jsdelivr.net/gh/NapNeko/NapCatQQ@main/docs/changelogs/CHANGELOG.v${{ env.VERSION }}.md
|
||||||
|
|
||||||
|
- name: Create Release Draft and Upload Artifacts
|
||||||
|
uses: softprops/action-gh-release@v1
|
||||||
|
with:
|
||||||
|
name: NapCat V${{ env.VERSION }}
|
||||||
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
body_path: CHANGELOG.md
|
||||||
|
files: |
|
||||||
|
NapCat.Framework.zip
|
||||||
|
NapCat.Shell.zip
|
||||||
|
# NapCat.darwin.x64.zip
|
||||||
|
# NapCat.darwin.arm64.zip
|
||||||
|
draft: true
|
||||||
18
.gitignore
vendored
Normal file
18
.gitignore
vendored
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
# Develop
|
||||||
|
node_modules/
|
||||||
|
package-lock.json
|
||||||
|
pnpm-lock.yaml
|
||||||
|
out/
|
||||||
|
dist/
|
||||||
|
/src/core.lib/common/
|
||||||
|
/localdebug/
|
||||||
|
|
||||||
|
# Editor
|
||||||
|
.vscode/*
|
||||||
|
!.vscode/extensions.json
|
||||||
|
.idea/*
|
||||||
|
|
||||||
|
# Build
|
||||||
|
*.db
|
||||||
|
checkVersion.sh
|
||||||
|
bun.lockb
|
||||||
10
.prettierrc.json
Normal file
10
.prettierrc.json
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"trailingComma": "es5",
|
||||||
|
"tabWidth": 4,
|
||||||
|
"semi": true,
|
||||||
|
"singleQuote": true,
|
||||||
|
"bracketSpacing": true,
|
||||||
|
"arrowParens": "always",
|
||||||
|
"printWidth": 120,
|
||||||
|
"endOfLine": "auto"
|
||||||
|
}
|
||||||
339
LICENSE
Normal file
339
LICENSE
Normal file
@@ -0,0 +1,339 @@
|
|||||||
|
GNU GENERAL PUBLIC LICENSE
|
||||||
|
Version 2, June 1991
|
||||||
|
|
||||||
|
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
|
||||||
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
Everyone is permitted to copy and distribute verbatim copies
|
||||||
|
of this license document, but changing it is not allowed.
|
||||||
|
|
||||||
|
Preamble
|
||||||
|
|
||||||
|
The licenses for most software are designed to take away your
|
||||||
|
freedom to share and change it. By contrast, the GNU General Public
|
||||||
|
License is intended to guarantee your freedom to share and change free
|
||||||
|
software--to make sure the software is free for all its users. This
|
||||||
|
General Public License applies to most of the Free Software
|
||||||
|
Foundation's software and to any other program whose authors commit to
|
||||||
|
using it. (Some other Free Software Foundation software is covered by
|
||||||
|
the GNU Lesser General Public License instead.) You can apply it to
|
||||||
|
your programs, too.
|
||||||
|
|
||||||
|
When we speak of free software, we are referring to freedom, not
|
||||||
|
price. Our General Public Licenses are designed to make sure that you
|
||||||
|
have the freedom to distribute copies of free software (and charge for
|
||||||
|
this service if you wish), that you receive source code or can get it
|
||||||
|
if you want it, that you can change the software or use pieces of it
|
||||||
|
in new free programs; and that you know you can do these things.
|
||||||
|
|
||||||
|
To protect your rights, we need to make restrictions that forbid
|
||||||
|
anyone to deny you these rights or to ask you to surrender the rights.
|
||||||
|
These restrictions translate to certain responsibilities for you if you
|
||||||
|
distribute copies of the software, or if you modify it.
|
||||||
|
|
||||||
|
For example, if you distribute copies of such a program, whether
|
||||||
|
gratis or for a fee, you must give the recipients all the rights that
|
||||||
|
you have. You must make sure that they, too, receive or can get the
|
||||||
|
source code. And you must show them these terms so they know their
|
||||||
|
rights.
|
||||||
|
|
||||||
|
We protect your rights with two steps: (1) copyright the software, and
|
||||||
|
(2) offer you this license which gives you legal permission to copy,
|
||||||
|
distribute and/or modify the software.
|
||||||
|
|
||||||
|
Also, for each author's protection and ours, we want to make certain
|
||||||
|
that everyone understands that there is no warranty for this free
|
||||||
|
software. If the software is modified by someone else and passed on, we
|
||||||
|
want its recipients to know that what they have is not the original, so
|
||||||
|
that any problems introduced by others will not reflect on the original
|
||||||
|
authors' reputations.
|
||||||
|
|
||||||
|
Finally, any free program is threatened constantly by software
|
||||||
|
patents. We wish to avoid the danger that redistributors of a free
|
||||||
|
program will individually obtain patent licenses, in effect making the
|
||||||
|
program proprietary. To prevent this, we have made it clear that any
|
||||||
|
patent must be licensed for everyone's free use or not licensed at all.
|
||||||
|
|
||||||
|
The precise terms and conditions for copying, distribution and
|
||||||
|
modification follow.
|
||||||
|
|
||||||
|
GNU GENERAL PUBLIC LICENSE
|
||||||
|
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||||
|
|
||||||
|
0. This License applies to any program or other work which contains
|
||||||
|
a notice placed by the copyright holder saying it may be distributed
|
||||||
|
under the terms of this General Public License. The "Program", below,
|
||||||
|
refers to any such program or work, and a "work based on the Program"
|
||||||
|
means either the Program or any derivative work under copyright law:
|
||||||
|
that is to say, a work containing the Program or a portion of it,
|
||||||
|
either verbatim or with modifications and/or translated into another
|
||||||
|
language. (Hereinafter, translation is included without limitation in
|
||||||
|
the term "modification".) Each licensee is addressed as "you".
|
||||||
|
|
||||||
|
Activities other than copying, distribution and modification are not
|
||||||
|
covered by this License; they are outside its scope. The act of
|
||||||
|
running the Program is not restricted, and the output from the Program
|
||||||
|
is covered only if its contents constitute a work based on the
|
||||||
|
Program (independent of having been made by running the Program).
|
||||||
|
Whether that is true depends on what the Program does.
|
||||||
|
|
||||||
|
1. You may copy and distribute verbatim copies of the Program's
|
||||||
|
source code as you receive it, in any medium, provided that you
|
||||||
|
conspicuously and appropriately publish on each copy an appropriate
|
||||||
|
copyright notice and disclaimer of warranty; keep intact all the
|
||||||
|
notices that refer to this License and to the absence of any warranty;
|
||||||
|
and give any other recipients of the Program a copy of this License
|
||||||
|
along with the Program.
|
||||||
|
|
||||||
|
You may charge a fee for the physical act of transferring a copy, and
|
||||||
|
you may at your option offer warranty protection in exchange for a fee.
|
||||||
|
|
||||||
|
2. You may modify your copy or copies of the Program or any portion
|
||||||
|
of it, thus forming a work based on the Program, and copy and
|
||||||
|
distribute such modifications or work under the terms of Section 1
|
||||||
|
above, provided that you also meet all of these conditions:
|
||||||
|
|
||||||
|
a) You must cause the modified files to carry prominent notices
|
||||||
|
stating that you changed the files and the date of any change.
|
||||||
|
|
||||||
|
b) You must cause any work that you distribute or publish, that in
|
||||||
|
whole or in part contains or is derived from the Program or any
|
||||||
|
part thereof, to be licensed as a whole at no charge to all third
|
||||||
|
parties under the terms of this License.
|
||||||
|
|
||||||
|
c) If the modified program normally reads commands interactively
|
||||||
|
when run, you must cause it, when started running for such
|
||||||
|
interactive use in the most ordinary way, to print or display an
|
||||||
|
announcement including an appropriate copyright notice and a
|
||||||
|
notice that there is no warranty (or else, saying that you provide
|
||||||
|
a warranty) and that users may redistribute the program under
|
||||||
|
these conditions, and telling the user how to view a copy of this
|
||||||
|
License. (Exception: if the Program itself is interactive but
|
||||||
|
does not normally print such an announcement, your work based on
|
||||||
|
the Program is not required to print an announcement.)
|
||||||
|
|
||||||
|
These requirements apply to the modified work as a whole. If
|
||||||
|
identifiable sections of that work are not derived from the Program,
|
||||||
|
and can be reasonably considered independent and separate works in
|
||||||
|
themselves, then this License, and its terms, do not apply to those
|
||||||
|
sections when you distribute them as separate works. But when you
|
||||||
|
distribute the same sections as part of a whole which is a work based
|
||||||
|
on the Program, the distribution of the whole must be on the terms of
|
||||||
|
this License, whose permissions for other licensees extend to the
|
||||||
|
entire whole, and thus to each and every part regardless of who wrote it.
|
||||||
|
|
||||||
|
Thus, it is not the intent of this section to claim rights or contest
|
||||||
|
your rights to work written entirely by you; rather, the intent is to
|
||||||
|
exercise the right to control the distribution of derivative or
|
||||||
|
collective works based on the Program.
|
||||||
|
|
||||||
|
In addition, mere aggregation of another work not based on the Program
|
||||||
|
with the Program (or with a work based on the Program) on a volume of
|
||||||
|
a storage or distribution medium does not bring the other work under
|
||||||
|
the scope of this License.
|
||||||
|
|
||||||
|
3. You may copy and distribute the Program (or a work based on it,
|
||||||
|
under Section 2) in object code or executable form under the terms of
|
||||||
|
Sections 1 and 2 above provided that you also do one of the following:
|
||||||
|
|
||||||
|
a) Accompany it with the complete corresponding machine-readable
|
||||||
|
source code, which must be distributed under the terms of Sections
|
||||||
|
1 and 2 above on a medium customarily used for software interchange; or,
|
||||||
|
|
||||||
|
b) Accompany it with a written offer, valid for at least three
|
||||||
|
years, to give any third party, for a charge no more than your
|
||||||
|
cost of physically performing source distribution, a complete
|
||||||
|
machine-readable copy of the corresponding source code, to be
|
||||||
|
distributed under the terms of Sections 1 and 2 above on a medium
|
||||||
|
customarily used for software interchange; or,
|
||||||
|
|
||||||
|
c) Accompany it with the information you received as to the offer
|
||||||
|
to distribute corresponding source code. (This alternative is
|
||||||
|
allowed only for noncommercial distribution and only if you
|
||||||
|
received the program in object code or executable form with such
|
||||||
|
an offer, in accord with Subsection b above.)
|
||||||
|
|
||||||
|
The source code for a work means the preferred form of the work for
|
||||||
|
making modifications to it. For an executable work, complete source
|
||||||
|
code means all the source code for all modules it contains, plus any
|
||||||
|
associated interface definition files, plus the scripts used to
|
||||||
|
control compilation and installation of the executable. However, as a
|
||||||
|
special exception, the source code distributed need not include
|
||||||
|
anything that is normally distributed (in either source or binary
|
||||||
|
form) with the major components (compiler, kernel, and so on) of the
|
||||||
|
operating system on which the executable runs, unless that component
|
||||||
|
itself accompanies the executable.
|
||||||
|
|
||||||
|
If distribution of executable or object code is made by offering
|
||||||
|
access to copy from a designated place, then offering equivalent
|
||||||
|
access to copy the source code from the same place counts as
|
||||||
|
distribution of the source code, even though third parties are not
|
||||||
|
compelled to copy the source along with the object code.
|
||||||
|
|
||||||
|
4. You may not copy, modify, sublicense, or distribute the Program
|
||||||
|
except as expressly provided under this License. Any attempt
|
||||||
|
otherwise to copy, modify, sublicense or distribute the Program is
|
||||||
|
void, and will automatically terminate your rights under this License.
|
||||||
|
However, parties who have received copies, or rights, from you under
|
||||||
|
this License will not have their licenses terminated so long as such
|
||||||
|
parties remain in full compliance.
|
||||||
|
|
||||||
|
5. You are not required to accept this License, since you have not
|
||||||
|
signed it. However, nothing else grants you permission to modify or
|
||||||
|
distribute the Program or its derivative works. These actions are
|
||||||
|
prohibited by law if you do not accept this License. Therefore, by
|
||||||
|
modifying or distributing the Program (or any work based on the
|
||||||
|
Program), you indicate your acceptance of this License to do so, and
|
||||||
|
all its terms and conditions for copying, distributing or modifying
|
||||||
|
the Program or works based on it.
|
||||||
|
|
||||||
|
6. Each time you redistribute the Program (or any work based on the
|
||||||
|
Program), the recipient automatically receives a license from the
|
||||||
|
original licensor to copy, distribute or modify the Program subject to
|
||||||
|
these terms and conditions. You may not impose any further
|
||||||
|
restrictions on the recipients' exercise of the rights granted herein.
|
||||||
|
You are not responsible for enforcing compliance by third parties to
|
||||||
|
this License.
|
||||||
|
|
||||||
|
7. If, as a consequence of a court judgment or allegation of patent
|
||||||
|
infringement or for any other reason (not limited to patent issues),
|
||||||
|
conditions are imposed on you (whether by court order, agreement or
|
||||||
|
otherwise) that contradict the conditions of this License, they do not
|
||||||
|
excuse you from the conditions of this License. If you cannot
|
||||||
|
distribute so as to satisfy simultaneously your obligations under this
|
||||||
|
License and any other pertinent obligations, then as a consequence you
|
||||||
|
may not distribute the Program at all. For example, if a patent
|
||||||
|
license would not permit royalty-free redistribution of the Program by
|
||||||
|
all those who receive copies directly or indirectly through you, then
|
||||||
|
the only way you could satisfy both it and this License would be to
|
||||||
|
refrain entirely from distribution of the Program.
|
||||||
|
|
||||||
|
If any portion of this section is held invalid or unenforceable under
|
||||||
|
any particular circumstance, the balance of the section is intended to
|
||||||
|
apply and the section as a whole is intended to apply in other
|
||||||
|
circumstances.
|
||||||
|
|
||||||
|
It is not the purpose of this section to induce you to infringe any
|
||||||
|
patents or other property right claims or to contest validity of any
|
||||||
|
such claims; this section has the sole purpose of protecting the
|
||||||
|
integrity of the free software distribution system, which is
|
||||||
|
implemented by public license practices. Many people have made
|
||||||
|
generous contributions to the wide range of software distributed
|
||||||
|
through that system in reliance on consistent application of that
|
||||||
|
system; it is up to the author/donor to decide if he or she is willing
|
||||||
|
to distribute software through any other system and a licensee cannot
|
||||||
|
impose that choice.
|
||||||
|
|
||||||
|
This section is intended to make thoroughly clear what is believed to
|
||||||
|
be a consequence of the rest of this License.
|
||||||
|
|
||||||
|
8. If the distribution and/or use of the Program is restricted in
|
||||||
|
certain countries either by patents or by copyrighted interfaces, the
|
||||||
|
original copyright holder who places the Program under this License
|
||||||
|
may add an explicit geographical distribution limitation excluding
|
||||||
|
those countries, so that distribution is permitted only in or among
|
||||||
|
countries not thus excluded. In such case, this License incorporates
|
||||||
|
the limitation as if written in the body of this License.
|
||||||
|
|
||||||
|
9. The Free Software Foundation may publish revised and/or new versions
|
||||||
|
of the General Public License from time to time. Such new versions will
|
||||||
|
be similar in spirit to the present version, but may differ in detail to
|
||||||
|
address new problems or concerns.
|
||||||
|
|
||||||
|
Each version is given a distinguishing version number. If the Program
|
||||||
|
specifies a version number of this License which applies to it and "any
|
||||||
|
later version", you have the option of following the terms and conditions
|
||||||
|
either of that version or of any later version published by the Free
|
||||||
|
Software Foundation. If the Program does not specify a version number of
|
||||||
|
this License, you may choose any version ever published by the Free Software
|
||||||
|
Foundation.
|
||||||
|
|
||||||
|
10. If you wish to incorporate parts of the Program into other free
|
||||||
|
programs whose distribution conditions are different, write to the author
|
||||||
|
to ask for permission. For software which is copyrighted by the Free
|
||||||
|
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||||
|
make exceptions for this. Our decision will be guided by the two goals
|
||||||
|
of preserving the free status of all derivatives of our free software and
|
||||||
|
of promoting the sharing and reuse of software generally.
|
||||||
|
|
||||||
|
NO WARRANTY
|
||||||
|
|
||||||
|
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||||
|
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||||
|
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||||
|
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||||
|
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||||
|
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||||
|
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||||
|
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||||
|
REPAIR OR CORRECTION.
|
||||||
|
|
||||||
|
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||||
|
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||||
|
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||||
|
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||||
|
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||||
|
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||||
|
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||||
|
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||||
|
POSSIBILITY OF SUCH DAMAGES.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
How to Apply These Terms to Your New Programs
|
||||||
|
|
||||||
|
If you develop a new program, and you want it to be of the greatest
|
||||||
|
possible use to the public, the best way to achieve this is to make it
|
||||||
|
free software which everyone can redistribute and change under these terms.
|
||||||
|
|
||||||
|
To do so, attach the following notices to the program. It is safest
|
||||||
|
to attach them to the start of each source file to most effectively
|
||||||
|
convey the exclusion of warranty; and each file should have at least
|
||||||
|
the "copyright" line and a pointer to where the full notice is found.
|
||||||
|
|
||||||
|
<one line to give the program's name and a brief idea of what it does.>
|
||||||
|
Copyright (C) <year> <name of author>
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along
|
||||||
|
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
|
||||||
|
Also add information on how to contact you by electronic and paper mail.
|
||||||
|
|
||||||
|
If the program is interactive, make it output a short notice like this
|
||||||
|
when it starts in an interactive mode:
|
||||||
|
|
||||||
|
Gnomovision version 69, Copyright (C) year name of author
|
||||||
|
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||||
|
This is free software, and you are welcome to redistribute it
|
||||||
|
under certain conditions; type `show c' for details.
|
||||||
|
|
||||||
|
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||||
|
parts of the General Public License. Of course, the commands you use may
|
||||||
|
be called something other than `show w' and `show c'; they could even be
|
||||||
|
mouse-clicks or menu items--whatever suits your program.
|
||||||
|
|
||||||
|
You should also get your employer (if you work as a programmer) or your
|
||||||
|
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||||
|
necessary. Here is a sample; alter the names:
|
||||||
|
|
||||||
|
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||||
|
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||||
|
|
||||||
|
<signature of Ty Coon>, 1 April 1989
|
||||||
|
Ty Coon, President of Vice
|
||||||
|
|
||||||
|
This General Public License does not permit incorporating your program into
|
||||||
|
proprietary programs. If your program is a subroutine library, you may
|
||||||
|
consider it more useful to permit linking proprietary applications with the
|
||||||
|
library. If this is what you want to do, use the GNU Lesser General
|
||||||
|
Public License instead of this License.
|
||||||
40
README.md
40
README.md
@@ -1,14 +1,34 @@
|
|||||||
# NapCatQQ
|
<div align="center">
|
||||||
|
<img src="https://socialify.git.ci/NapNeko/NapCatQQ/image?description=1&language=1&logo=https%3A%2F%2Fraw.githubusercontent.com%2FNapNeko%2FNapCatQQ%2Fmain%2Flogo.png&name=1&stargazers=1&theme=Auto" alt="NapCatQQ" width="640" height="320" />
|
||||||
|
</div>
|
||||||
|
|
||||||
## 介绍
|
---
|
||||||
无
|
|
||||||
|
|
||||||
## 下载与安装
|
## 项目介绍
|
||||||
前往release获取
|
NapCatQQ 是现代化的基于 NTQQ 的 Bot 协议端实现。
|
||||||
|
|
||||||
## 使用与配置
|
## 项目优势
|
||||||
参考文档
|
- [x] **多种启动方式**:支持以无头、LiteLoader 插件、仅 QQ GUI 三种方式启动
|
||||||
|
- [x] **低占用**:无头模式占用资源极低,适合在服务器上运行
|
||||||
|
- [x] **WebUI**:自带 WebUI 支持,远程管理更加便捷
|
||||||
|
|
||||||
## 开源与安全
|
## 如何使用
|
||||||
为了防止过于扩散与违规使用,未来 NapCat 发版都会不公布源码,在未来形势有所转变下可能会发布源码。
|
|
||||||
代码将进行混淆与插桩,请不要违法使用与宣传本项目。
|
可前往 [Release](https://github.com/NapNeko/NapCatQQ/releases/) 页面下载最新版本
|
||||||
|
|
||||||
|
**首次使用**请务必前往[官方文档](https://napneko.github.io/)查看使用教程。
|
||||||
|
|
||||||
|
## 相关链接
|
||||||
|
|
||||||
|
[Telegram Link](https://t.me/+nLZEnpne-pQ1OWFl)
|
||||||
|
|
||||||
|
## 附加协议
|
||||||
|
禁止未授权任何项目使用Core部分代码用于二次开发与分发
|
||||||
|
|
||||||
|
## 鸣谢名单
|
||||||
|
感谢 [LLOneBot](https://github.com/LLOneBot/LLOneBot) 提供初始版本基础
|
||||||
|
|
||||||
|
感谢 [Lagrange](https://github.com/LagrangeDev/Lagrange.Core) 对本项目的大力支持
|
||||||
|
|
||||||
|
---
|
||||||
|
**任何使用本仓库代码的地方,都应当严格遵守[本仓库开源许可](./LICENSE)。**
|
||||||
33
manifest.json
Normal file
33
manifest.json
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
{
|
||||||
|
"manifest_version": 4,
|
||||||
|
"type": "extension",
|
||||||
|
"name": "NapCat",
|
||||||
|
"slug": "NapCat",
|
||||||
|
"description": "OneBot v11 protocol implementation with NapCat logic",
|
||||||
|
"version": "2.0.2",
|
||||||
|
"icon": "./logo.png",
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "MliKiowa",
|
||||||
|
"link": "https://github.com/MliKiowa"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Young",
|
||||||
|
"link": "https://github.com/Wesley-Young"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"repository": {
|
||||||
|
"repo": "NapNeko/NapCatQQ",
|
||||||
|
"branch": "main"
|
||||||
|
},
|
||||||
|
"platform": [
|
||||||
|
"win32",
|
||||||
|
"linux",
|
||||||
|
"darwin"
|
||||||
|
],
|
||||||
|
"injects": {
|
||||||
|
"renderer": "./renderer.js",
|
||||||
|
"main": "./liteloader.cjs",
|
||||||
|
"preload": "./preload.cjs"
|
||||||
|
}
|
||||||
|
}
|
||||||
65
package.json
Normal file
65
package.json
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
{
|
||||||
|
"name": "napcat",
|
||||||
|
"private": true,
|
||||||
|
"type": "module",
|
||||||
|
"version": "2.0.2",
|
||||||
|
"scripts": {
|
||||||
|
"build:framework": "vite build --mode framework",
|
||||||
|
"build:shell": "vite build --mode shell",
|
||||||
|
"build:webui": "cd ./src/webui && vite build",
|
||||||
|
"lint": "eslint --fix src/**/*.{js,ts}",
|
||||||
|
"depend": "cd dist && npm install --omit=dev"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@babel/core": "^7.24.7",
|
||||||
|
"@babel/plugin-proposal-class-properties": "^7.18.6",
|
||||||
|
"@babel/plugin-proposal-decorators": "^7.24.7",
|
||||||
|
"@babel/preset-typescript": "^7.24.7",
|
||||||
|
"@log4js-node/log4js-api": "^1.0.2",
|
||||||
|
"@protobuf-ts/plugin": "^2.9.4",
|
||||||
|
"@rollup/plugin-node-resolve": "^15.2.3",
|
||||||
|
"@rollup/plugin-typescript": "^11.1.6",
|
||||||
|
"@types/cors": "^2.8.17",
|
||||||
|
"@types/express": "^4.17.21",
|
||||||
|
"@types/figlet": "^1.5.8",
|
||||||
|
"@types/fluent-ffmpeg": "^2.1.24",
|
||||||
|
"@types/jest": "^29.5.12",
|
||||||
|
"@types/node": "^22.0.1",
|
||||||
|
"@types/qrcode-terminal": "^0.12.2",
|
||||||
|
"@types/ws": "^8.5.12",
|
||||||
|
"@typescript-eslint/eslint-plugin": "^7.4.0",
|
||||||
|
"@typescript-eslint/parser": "^7.4.0",
|
||||||
|
"eslint": "^8.57.0",
|
||||||
|
"eslint-import-resolver-typescript": "^3.6.1",
|
||||||
|
"eslint-plugin-import": "^2.29.1",
|
||||||
|
"i": "^0.3.7",
|
||||||
|
"javascript-obfuscator": "^4.1.0",
|
||||||
|
"rollup": "^4.13.2",
|
||||||
|
"rollup-plugin-dts": "^6.1.0",
|
||||||
|
"rollup-plugin-obfuscator": "^1.1.0",
|
||||||
|
"typescript": "^5.3.3",
|
||||||
|
"vite": "^5.2.6",
|
||||||
|
"vite-plugin-babel": "^1.2.0",
|
||||||
|
"vite-plugin-cp": "^4.0.8",
|
||||||
|
"vite-plugin-dts": "^3.8.2",
|
||||||
|
"vite-tsconfig-paths": "^4.3.2"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"ajv": "^8.13.0",
|
||||||
|
"async-mutex": "^0.5.0",
|
||||||
|
"chalk": "^5.3.0",
|
||||||
|
"commander": "^12.1.0",
|
||||||
|
"cors": "^2.8.5",
|
||||||
|
"express": "^5.0.0-beta.2",
|
||||||
|
"fast-xml-parser": "^4.3.6",
|
||||||
|
"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",
|
||||||
|
"strtok3": "8.0.1",
|
||||||
|
"ws": "^8.18.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 }"
|
||||||
90
script/BootWay05.bat
Normal file
90
script/BootWay05.bat
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
@echo off
|
||||||
|
REM 检查当前会话是否具有管理员权限
|
||||||
|
openfiles >nul 2>&1
|
||||||
|
if %errorlevel% neq 0 (
|
||||||
|
REM 如果不是管理员,则重新启动脚本以管理员模式运行
|
||||||
|
echo 请求管理员权限...
|
||||||
|
powershell -Command "Start-Process cmd -ArgumentList '/c %~f0 %*' -Verb RunAs"
|
||||||
|
exit /b
|
||||||
|
)
|
||||||
|
|
||||||
|
REM 设置当前工作目录
|
||||||
|
cd /d %~dp0
|
||||||
|
|
||||||
|
REM 获取当前目录路径
|
||||||
|
set currentPath=%cd%
|
||||||
|
set currentPath=%currentPath:\=/%
|
||||||
|
|
||||||
|
REM 生成JavaScript代码
|
||||||
|
set "jsCode=(async () =^>await import('file:///%currentPath%/napcat.mjs'))();"
|
||||||
|
|
||||||
|
REM 将JavaScript代码保存到文件中
|
||||||
|
echo %jsCode% > loadScript.js
|
||||||
|
echo JavaScript code has been generated and saved to loadScript.js
|
||||||
|
|
||||||
|
REM 设置NAPCAT_PATH环境变量为 当前目录的loadScript.js地址
|
||||||
|
set NAPCAT_PATH=%cd%\loadScript.js
|
||||||
|
|
||||||
|
REM 获取QQ路径
|
||||||
|
|
||||||
|
|
||||||
|
: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
|
||||||
|
|
||||||
|
REM 拿不到QQ路径则退出
|
||||||
|
if not exist "%QQpath%" (
|
||||||
|
echo provided QQ path is invalid: %QQpath%
|
||||||
|
pause
|
||||||
|
exit /b
|
||||||
|
)
|
||||||
|
|
||||||
|
REM 收集dbghelp.dll路径和HASH信息
|
||||||
|
set QQdir=%~dp0
|
||||||
|
set oldDllPath=%QQdir%dbghelp.dll
|
||||||
|
set newDllPath=%currentPath%\dbghelp.dll
|
||||||
|
|
||||||
|
for /f "tokens=*" %%A in ('certutil -hashfile "%oldDllPath%" MD5') do (
|
||||||
|
if not defined oldDllHash set oldDllHash=%%A
|
||||||
|
)
|
||||||
|
for /f "tokens=*" %%A in ('certutil -hashfile "%newDllPath%" MD5') do (
|
||||||
|
if not defined newDllHash set newDllHash=%%A
|
||||||
|
)
|
||||||
|
|
||||||
|
REM 如果文件一致则跳过
|
||||||
|
if "%oldDllHash%" neq "%newDllHash%" (
|
||||||
|
tasklist /fi "imagename eq QQ.exe" 2>nul | find /i "QQ.exe" >nul
|
||||||
|
if %errorlevel% equ 0 (
|
||||||
|
REM 文件占用则退出
|
||||||
|
echo dbghelp.dll is in use, cannot continue.
|
||||||
|
) else (
|
||||||
|
REM 文件未占用则尝试覆盖
|
||||||
|
copy /y "%newDllPath%" "%oldDllPath%"
|
||||||
|
if %errorlevel% neq 0 (
|
||||||
|
echo Failed to copy dbghelp.dll
|
||||||
|
pause
|
||||||
|
exit /b
|
||||||
|
) else (
|
||||||
|
echo dbghelp.dll has been copied to %QQdir%
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
REM 带参数启动QQ
|
||||||
|
REM 判断wt是否存在,存在则通过wt启动,不存在则通过cmd启动
|
||||||
|
REM %QQPath% --enable-logging %*
|
||||||
|
where wt >nul 2>nul
|
||||||
|
if %errorlevel% equ 0 (
|
||||||
|
wt "cmd" /c "%QQPath%" --enable-logging %*
|
||||||
|
) else (
|
||||||
|
"%QQPath%" --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..."
|
||||||
|
}
|
||||||
93
script/BootWay05.utf8.bat
Normal file
93
script/BootWay05.utf8.bat
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
@echo off
|
||||||
|
REM 检查当前会话是否具有管理员权限
|
||||||
|
openfiles >nul 2>&1
|
||||||
|
if %errorlevel% neq 0 (
|
||||||
|
REM 如果不是管理员,则重新启动脚本以管理员模式运行
|
||||||
|
echo 请求管理员权限...
|
||||||
|
where wt >nul 2>nul
|
||||||
|
if %errorlevel% equ 0 (
|
||||||
|
powershell -Command "Start-Process cmd -ArgumentList ' /c %~f0 %*' -Verb RunAs"
|
||||||
|
) else (
|
||||||
|
powershell -Command "Start-Process wt -ArgumentList 'cmd /c %~f0 %*' -Verb RunAs"
|
||||||
|
)
|
||||||
|
|
||||||
|
REM wt "cmd" /c "%~f0 %*"
|
||||||
|
exit /b
|
||||||
|
)
|
||||||
|
|
||||||
|
REM 设置当前工作目录
|
||||||
|
cd /d %~dp0
|
||||||
|
|
||||||
|
REM 获取当前目录路径
|
||||||
|
set currentPath=%cd%
|
||||||
|
set currentPath=%currentPath:\=/%
|
||||||
|
|
||||||
|
REM 生成JavaScript代码
|
||||||
|
set "jsCode=(async () =^>await import('file:///%currentPath%/napcat.mjs'))();"
|
||||||
|
|
||||||
|
REM 将JavaScript代码保存到文件中
|
||||||
|
echo %jsCode% > loadScript.js
|
||||||
|
echo JavaScript code has been generated and saved to loadScript.js
|
||||||
|
|
||||||
|
REM 设置NAPCAT_PATH环境变量为 当前目录的loadScript.js地址
|
||||||
|
set NAPCAT_PATH=%cd%\loadScript.js
|
||||||
|
|
||||||
|
REM 获取QQ路径
|
||||||
|
|
||||||
|
|
||||||
|
: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
|
||||||
|
|
||||||
|
REM 拿不到QQ路径则退出
|
||||||
|
if not exist "%QQpath%" (
|
||||||
|
echo provided QQ path is invalid: %QQpath%
|
||||||
|
pause
|
||||||
|
exit /b
|
||||||
|
)
|
||||||
|
|
||||||
|
REM 收集dbghelp.dll路径和HASH信息
|
||||||
|
set QQdir=%~dp0
|
||||||
|
set oldDllPath=%QQdir%dbghelp.dll
|
||||||
|
set newDllPath=%currentPath%\dbghelp.dll
|
||||||
|
|
||||||
|
for /f "tokens=*" %%A in ('certutil -hashfile "%oldDllPath%" MD5') do (
|
||||||
|
if not defined oldDllHash set oldDllHash=%%A
|
||||||
|
)
|
||||||
|
for /f "tokens=*" %%A in ('certutil -hashfile "%newDllPath%" MD5') do (
|
||||||
|
if not defined newDllHash set newDllHash=%%A
|
||||||
|
)
|
||||||
|
|
||||||
|
REM 如果文件一致则跳过
|
||||||
|
if "%oldDllHash%" neq "%newDllHash%" (
|
||||||
|
tasklist /fi "imagename eq QQ.exe" 2>nul | find /i "QQ.exe" >nul
|
||||||
|
if %errorlevel% equ 0 (
|
||||||
|
REM 文件占用则退出
|
||||||
|
echo dbghelp.dll is in use, cannot continue.
|
||||||
|
) else (
|
||||||
|
REM 文件未占用则尝试覆盖
|
||||||
|
copy /y "%newDllPath%" "%oldDllPath%"
|
||||||
|
if %errorlevel% neq 0 (
|
||||||
|
echo Failed to copy dbghelp.dll
|
||||||
|
pause
|
||||||
|
exit /b
|
||||||
|
) else (
|
||||||
|
echo dbghelp.dll has been copied to %QQdir%
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
REM 带参数启动QQ
|
||||||
|
REM 判断wt是否存在,存在则通过wt启动,不存在则通过cmd启动
|
||||||
|
REM %QQPath% --enable-logging %*
|
||||||
|
chcp 65001
|
||||||
|
"%QQPath%" --enable-logging %*
|
||||||
42
script/checkVersion.cjs
Normal file
42
script/checkVersion.cjs
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
const fs = require("fs");
|
||||||
|
const process = require("process");
|
||||||
|
|
||||||
|
console.log("[NapCat] [CheckVersion] 开始检测当前仓库版本...");
|
||||||
|
try {
|
||||||
|
const packageJson = require("../package.json");
|
||||||
|
const currentVersion = packageJson.version;
|
||||||
|
const targetVersion = process.env.VERSION;
|
||||||
|
|
||||||
|
console.log("[NapCat] [CheckVersion] currentVersion:", currentVersion, "targetVersion:", targetVersion);
|
||||||
|
|
||||||
|
// 验证 targetVersion 格式
|
||||||
|
if (!targetVersion || typeof targetVersion !== 'string') {
|
||||||
|
console.log("[NapCat] [CheckVersion] 目标版本格式不正确或未设置!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 写入脚本文件的统一函数
|
||||||
|
const writeScriptToFile = (content) => {
|
||||||
|
fs.writeFileSync("./checkVersion.sh", content, { flag: 'w' });
|
||||||
|
console.log("[NapCat] [CheckVersion] checkVersion.sh 文件已更新。");
|
||||||
|
};
|
||||||
|
|
||||||
|
if (currentVersion === targetVersion) {
|
||||||
|
// 不需要更新版本,写入一个简单的脚本
|
||||||
|
const simpleScript = "#!/bin/bash\necho \"CheckVersion Is Done\"";
|
||||||
|
writeScriptToFile(simpleScript);
|
||||||
|
} else {
|
||||||
|
// 更新版本,构建安全的sed命令
|
||||||
|
const safeScriptContent = `
|
||||||
|
#!/bin/bash
|
||||||
|
git config --global user.email "bot@test.wumiao.wang"
|
||||||
|
git config --global user.name "Version"
|
||||||
|
sed -i "s/\\\"version\\\": \\\"${currentVersion}\\\"/\\\"version\\\": \\\"${targetVersion}\\\"/g" package.json
|
||||||
|
git add .
|
||||||
|
git commit -m "chore:version change"
|
||||||
|
git push -u origin main`;
|
||||||
|
writeScriptToFile(safeScriptContent);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log("[NapCat] [CheckVersion] 检测过程中发生错误:", error);
|
||||||
|
}
|
||||||
BIN
script/dbghelp.dll
Normal file
BIN
script/dbghelp.dll
Normal file
Binary file not shown.
263
src/common/framework/event-legacy.ts
Normal file
263
src/common/framework/event-legacy.ts
Normal file
@@ -0,0 +1,263 @@
|
|||||||
|
import { NodeIQQNTWrapperSession } from '@/core/wrapper/wrapper';
|
||||||
|
import { randomUUID } from 'crypto';
|
||||||
|
|
||||||
|
interface Internal_MapKey {
|
||||||
|
timeout: number;
|
||||||
|
createtime: number;
|
||||||
|
func: (...arg: any[]) => any;
|
||||||
|
checker: ((...args: any[]) => boolean) | undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ListenerClassBase = Record<string, string>;
|
||||||
|
|
||||||
|
export interface ListenerIBase {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-misused-new
|
||||||
|
new(listener: any): ListenerClassBase;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class LegacyNTEventWrapper {
|
||||||
|
private listenerMapping: Record<string, ListenerIBase>; //ListenerName-Unique -> Listener构造函数
|
||||||
|
private WrapperSession: NodeIQQNTWrapperSession | undefined; //WrapperSession
|
||||||
|
private listenerManager: 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(
|
||||||
|
listenerMapping: Record<string, ListenerIBase>,
|
||||||
|
wrapperSession: NodeIQQNTWrapperSession,
|
||||||
|
) {
|
||||||
|
this.listenerMapping = listenerMapping;
|
||||||
|
this.WrapperSession = wrapperSession;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
createEventFunction<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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
createListenerFunction<T>(listenerMainName: string, uniqueCode: string = ''): T {
|
||||||
|
const ListenerType = this.listenerMapping![listenerMainName];
|
||||||
|
let Listener = this.listenerManager.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.createEventFunction<(listener: T) => number>(Service);
|
||||||
|
addfunc!(Listener as T);
|
||||||
|
//console.log(addfunc!(Listener as T));
|
||||||
|
this.listenerManager.set(listenerMainName + uniqueCode, Listener);
|
||||||
|
}
|
||||||
|
return Listener as T;
|
||||||
|
}
|
||||||
|
|
||||||
|
//统一回调清理事件
|
||||||
|
async dispatcherListener(ListenerMainName: string, ListenerSubName: string, ...args: any[]) {
|
||||||
|
//console.log("[EventDispatcher]",ListenerMainName, ListenerSubName, ...args);
|
||||||
|
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.createEventFunction<EventType>(EventName);
|
||||||
|
let complete = false;
|
||||||
|
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 timeoutRef = setTimeout(databack, timeout);
|
||||||
|
const eventCallbak = {
|
||||||
|
timeout: timeout,
|
||||||
|
createtime: Date.now(),
|
||||||
|
checker: checker,
|
||||||
|
func: (...args: Parameters<ListenerType>) => {
|
||||||
|
complete++;
|
||||||
|
retData = args;
|
||||||
|
if (complete >= waitTimes) {
|
||||||
|
clearTimeout(timeoutRef);
|
||||||
|
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.createListenerFunction(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.createListenerFunction(ListenerMainName);
|
||||||
|
const EventFunc = this.createEventFunction<EventType>(EventName);
|
||||||
|
retEvent = await EventFunc!(...(args as any[]));
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 示例代码 快速创建事件
|
||||||
|
// 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) });
|
||||||
140
src/common/framework/event.ts
Normal file
140
src/common/framework/event.ts
Normal file
@@ -0,0 +1,140 @@
|
|||||||
|
import type { NodeIQQNTWrapperSession, WrapperNodeApi } from '@/core/wrapper/wrapper';
|
||||||
|
import EventEmitter from 'node:events';
|
||||||
|
|
||||||
|
export type ListenerClassBase = Record<string, string>;
|
||||||
|
|
||||||
|
export interface ListenerIBase {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-misused-new
|
||||||
|
new(listener: any): ListenerClassBase;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class NTEventChannel extends EventEmitter {
|
||||||
|
private wrapperApi: WrapperNodeApi;
|
||||||
|
private wrapperSession: NodeIQQNTWrapperSession;
|
||||||
|
private listenerRefStorage = new Map<string, ListenerIBase>();
|
||||||
|
|
||||||
|
constructor(WrapperApi: WrapperNodeApi, WrapperSession: NodeIQQNTWrapperSession) {
|
||||||
|
super();
|
||||||
|
this.on('error', () => {
|
||||||
|
});
|
||||||
|
this.wrapperApi = WrapperApi;
|
||||||
|
this.wrapperSession = WrapperSession;
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatcherListener(ListenerEvent: string, ...args: any[]) {
|
||||||
|
this.emit(ListenerEvent, ...args);
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
return (...args: any[]) => {
|
||||||
|
current.dispatcherListener.apply(current, [ListenerMainName + '/' + prop, ...args]);
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async getOrInitListener<T>(listenerMainName: string): Promise<T> {
|
||||||
|
const ListenerType = this.wrapperApi[listenerMainName];
|
||||||
|
//获取NTQQ 外部 Listener包装
|
||||||
|
if (!ListenerType) throw new Error('Init Listener not found');
|
||||||
|
let Listener = this.listenerRefStorage.get(listenerMainName);
|
||||||
|
//判断是否已创建 创建则跳过
|
||||||
|
if (!Listener && ListenerType) {
|
||||||
|
Listener = new ListenerType(this.createProxyDispatch(listenerMainName));
|
||||||
|
if (!Listener) throw new Error('Init Listener failed');
|
||||||
|
//实例化NTQQ Listener外包装
|
||||||
|
const ServiceSubName = listenerMainName.match(/^NodeIKernel(.*?)Listener$/)![1];
|
||||||
|
const Service = 'NodeIKernel' + ServiceSubName + 'Service/addKernel' + ServiceSubName + 'Listener';
|
||||||
|
const addfunc = this.createEventFunction<(listener: T) => number>(Service);
|
||||||
|
//添加Listener到NTQQ
|
||||||
|
addfunc!(Listener as T);
|
||||||
|
this.listenerRefStorage.set(listenerMainName, Listener);
|
||||||
|
//保存Listener实例
|
||||||
|
}
|
||||||
|
return Listener as T;
|
||||||
|
}
|
||||||
|
|
||||||
|
async createEventWithListener<EventType extends (...args: any) => any, ListenerType extends (...args: any) => any>
|
||||||
|
(
|
||||||
|
eventName: string,
|
||||||
|
listenerName: string,
|
||||||
|
waitTimes = 1,
|
||||||
|
timeout: number = 3000,
|
||||||
|
checker: (...args: Parameters<ListenerType>) => boolean,
|
||||||
|
...eventArg: Parameters<EventType>
|
||||||
|
) {
|
||||||
|
return new Promise<[EventRet: Awaited<ReturnType<EventType>>, ...Parameters<ListenerType>]>(async (resolve, reject) => {
|
||||||
|
const ListenerNameList = listenerName.split('/');
|
||||||
|
const ListenerMainName = ListenerNameList[0];
|
||||||
|
//const ListenerSubName = ListenerNameList[1];
|
||||||
|
this.getOrInitListener<ListenerType>(ListenerMainName);
|
||||||
|
let complete = 0;
|
||||||
|
const 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 Timeouter = setTimeout(databack, timeout);
|
||||||
|
const callback = (...args: Parameters<ListenerType>) => {
|
||||||
|
if (checker(...args)) {
|
||||||
|
complete++;
|
||||||
|
if (complete >= waitTimes) {
|
||||||
|
clearTimeout(Timeouter);
|
||||||
|
this.removeListener(listenerName, callback);
|
||||||
|
databack();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
this.on(listenerName, callback);
|
||||||
|
const EventFunc = this.createEventFunction<EventType>(eventName);
|
||||||
|
retEvent = await EventFunc!(...(eventArg as any[]));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private createEventFunction<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]();
|
||||||
|
const event = services[eventName]
|
||||||
|
//重新绑定this
|
||||||
|
.bind(services);
|
||||||
|
if (event) {
|
||||||
|
return event as T;
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async callEvent<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.createEventFunction<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);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//NTEvent2.0
|
||||||
30
src/common/framework/napcat.ts
Normal file
30
src/common/framework/napcat.ts
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
import path, { dirname } from 'path';
|
||||||
|
import { fileURLToPath } from 'url';
|
||||||
|
import fs from 'fs';
|
||||||
|
|
||||||
|
export const napcat_version = '2.0.2';
|
||||||
|
|
||||||
|
export class NapCatPathWrapper {
|
||||||
|
binaryPath: string;
|
||||||
|
logsPath: string;
|
||||||
|
configPath: string;
|
||||||
|
cachePath: string;
|
||||||
|
staticPath: string;
|
||||||
|
|
||||||
|
constructor(mainPath: string = dirname(fileURLToPath(import.meta.url))) {
|
||||||
|
this.binaryPath = mainPath;
|
||||||
|
this.logsPath = path.join(this.binaryPath, 'logs');
|
||||||
|
this.configPath = path.join(this.binaryPath, 'config');
|
||||||
|
this.cachePath = path.join(this.binaryPath, 'cache');
|
||||||
|
this.staticPath = path.join(this.binaryPath, 'static');
|
||||||
|
if (fs.existsSync(this.logsPath)) {
|
||||||
|
fs.mkdirSync(this.logsPath, { recursive: true });
|
||||||
|
}
|
||||||
|
if (fs.existsSync(this.configPath)) {
|
||||||
|
fs.mkdirSync(this.configPath, { recursive: true });
|
||||||
|
}
|
||||||
|
if (fs.existsSync(this.cachePath)) {
|
||||||
|
fs.mkdirSync(this.cachePath, { recursive: true });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
67
src/common/utils/ConfigBase.ts
Normal file
67
src/common/utils/ConfigBase.ts
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
import path from 'node:path';
|
||||||
|
import fs from 'node:fs';
|
||||||
|
import type { NapCatCore } from '@/core';
|
||||||
|
|
||||||
|
export abstract class ConfigBase<T> {
|
||||||
|
name: string;
|
||||||
|
coreContext: NapCatCore;
|
||||||
|
configPath: string;
|
||||||
|
configData: T = {} as T;
|
||||||
|
|
||||||
|
protected constructor(name: string, coreContext: NapCatCore, configPath: string) {
|
||||||
|
this.name = name;
|
||||||
|
this.coreContext = coreContext;
|
||||||
|
this.configPath = configPath;
|
||||||
|
fs.mkdirSync(this.configPath, { recursive: true });
|
||||||
|
this.read();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected getKeys(): string[] | null {
|
||||||
|
// 决定 key 在json配置文件中的顺序
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
getConfigPath(pathName: string | undefined): string {
|
||||||
|
const suffix = pathName ? `_${pathName}` : '';
|
||||||
|
const filename = `${this.name}${suffix}.json`;
|
||||||
|
return path.join(this.configPath, filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
read(): T {
|
||||||
|
const logger = this.coreContext.context.logger;
|
||||||
|
const configPath = this.getConfigPath(this.coreContext.selfInfo.uin);
|
||||||
|
if (!fs.existsSync(configPath)) {
|
||||||
|
try {
|
||||||
|
fs.writeFileSync(configPath, fs.readFileSync(this.getConfigPath(undefined), 'utf-8'));
|
||||||
|
logger.log(`[Core] [Config] 配置文件创建成功!\n`);
|
||||||
|
} catch (e: any) {
|
||||||
|
logger.logError(`[Core] [Config] 创建配置文件时发生错误:`, e.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
this.configData = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
|
||||||
|
logger.logDebug(`[Core] [Config] 配置文件${configPath}加载`, this.configData);
|
||||||
|
return this.configData;
|
||||||
|
} catch (e: any) {
|
||||||
|
if (e instanceof SyntaxError) {
|
||||||
|
logger.logError(`[Core] [Config] 配置文件格式错误,请检查配置文件:`, e.message);
|
||||||
|
} else {
|
||||||
|
logger.logError(`[Core] [Config] 读取配置文件时发生错误:`, e.message);
|
||||||
|
}
|
||||||
|
return {} as T;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
save(newConfigData: T = this.configData as T) {
|
||||||
|
const logger = this.coreContext.context.logger;
|
||||||
|
const selfInfo = this.coreContext.selfInfo;
|
||||||
|
this.configData = newConfigData;
|
||||||
|
const configPath = this.getConfigPath(selfInfo.uin);
|
||||||
|
try {
|
||||||
|
fs.writeFileSync(configPath, JSON.stringify(newConfigData, this.getKeys(), 2));
|
||||||
|
} catch (e: any) {
|
||||||
|
logger.logError(`保存配置文件 ${configPath} 时发生错误:`, e.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
31
src/common/utils/LRU.ts
Normal file
31
src/common/utils/LRU.ts
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
export class LRUCache<K, V> {
|
||||||
|
private capacity: number;
|
||||||
|
private cache: Map<K, V>;
|
||||||
|
|
||||||
|
constructor(capacity: number) {
|
||||||
|
this.capacity = capacity;
|
||||||
|
this.cache = new Map<K, V>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public get(key: K): V | undefined {
|
||||||
|
const value = this.cache.get(key);
|
||||||
|
if (value !== undefined) {
|
||||||
|
// Move the accessed key to the end to mark it as most recently used
|
||||||
|
this.cache.delete(key);
|
||||||
|
this.cache.set(key, value);
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public put(key: K, value: V): void {
|
||||||
|
if (this.cache.has(key)) {
|
||||||
|
// If the key already exists, move it to the end to mark it as most recently used
|
||||||
|
this.cache.delete(key);
|
||||||
|
} else if (this.cache.size >= this.capacity) {
|
||||||
|
// If the cache is full, remove the least recently used key (the first one in the map)
|
||||||
|
const firstKey = this.cache.keys().next().value;
|
||||||
|
this.cache.delete(firstKey);
|
||||||
|
}
|
||||||
|
this.cache.set(key, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
150
src/common/utils/MessageUnique.ts
Normal file
150
src/common/utils/MessageUnique.ts
Normal file
@@ -0,0 +1,150 @@
|
|||||||
|
import { Peer } from '@/core';
|
||||||
|
import crypto from 'crypto';
|
||||||
|
|
||||||
|
export 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).digest();
|
||||||
|
//设置第一个bit为0 保证shortId为正数
|
||||||
|
hash[0] &= 0x7f;
|
||||||
|
const shortId = hash.readInt32BE(0);
|
||||||
|
//减少性能损耗
|
||||||
|
// const isExist = this.msgIdMap.getKey(shortId);
|
||||||
|
// if (isExist && isExist === msgId) {
|
||||||
|
// return shortId;
|
||||||
|
// }
|
||||||
|
this.msgIdMap.set(msgId, shortId);
|
||||||
|
this.msgDataMap.set(key, shortId);
|
||||||
|
return shortId;
|
||||||
|
}
|
||||||
|
|
||||||
|
getMsgIdAndPeerByShortId(shortId: number): { MsgId: string; Peer: Peer } | undefined {
|
||||||
|
const data = this.msgDataMap.getKey(shortId);
|
||||||
|
if (data) {
|
||||||
|
const [msgId, chatTypeStr, peerUid] = data.split('|');
|
||||||
|
const peer: Peer = {
|
||||||
|
chatType: parseInt(chatTypeStr),
|
||||||
|
peerUid,
|
||||||
|
guildId: '',
|
||||||
|
};
|
||||||
|
return { MsgId: msgId, Peer: peer };
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
getShortIdByMsgId(msgId: string): number | undefined {
|
||||||
|
return this.msgIdMap.getValue(msgId);
|
||||||
|
}
|
||||||
|
|
||||||
|
getPeerByMsgId(msgId: string) {
|
||||||
|
const shortId = this.msgIdMap.getValue(msgId);
|
||||||
|
if (!shortId) return undefined;
|
||||||
|
return this.getMsgIdAndPeerByShortId(shortId);
|
||||||
|
}
|
||||||
|
|
||||||
|
resize(maxSize: number): void {
|
||||||
|
this.msgIdMap.resize(maxSize);
|
||||||
|
this.msgDataMap.resize(maxSize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const MessageUnique: MessageUniqueWrapper = new MessageUniqueWrapper();
|
||||||
81
src/common/utils/QQBasicInfo.ts
Normal file
81
src/common/utils/QQBasicInfo.ts
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
import path from 'node:path';
|
||||||
|
import fs from 'node:fs';
|
||||||
|
import { systemPlatform } from '@/common/utils/system';
|
||||||
|
import { getDefaultQQVersionConfigInfo, getQQVersionConfigPath } from './helper';
|
||||||
|
import AppidTable from '@/core/external/appid.json';
|
||||||
|
import { LogWrapper } from './log';
|
||||||
|
|
||||||
|
export class QQBasicInfoWrapper {
|
||||||
|
QQMainPath: string | undefined;
|
||||||
|
QQPackageInfoPath: string | undefined;
|
||||||
|
QQVersionConfigPath: string | undefined;
|
||||||
|
isQuickUpdate: boolean | undefined;
|
||||||
|
QQVersionConfig: QQVersionConfigType | undefined;
|
||||||
|
QQPackageInfo: QQPackageInfoType | undefined;
|
||||||
|
QQVersionAppid: string | undefined;
|
||||||
|
QQVersionQua: string | undefined;
|
||||||
|
context: { logger: LogWrapper };
|
||||||
|
|
||||||
|
constructor(context: { logger: LogWrapper }) {
|
||||||
|
//基础目录获取
|
||||||
|
this.context = context;
|
||||||
|
this.QQMainPath = process.execPath;
|
||||||
|
this.QQPackageInfoPath = path.join(path.dirname(this.QQMainPath), 'resources', 'app', 'package.json');
|
||||||
|
this.QQVersionConfigPath = getQQVersionConfigPath(this.QQMainPath);
|
||||||
|
|
||||||
|
//基础信息获取 无快更则启用默认模板填充
|
||||||
|
this.isQuickUpdate = !!this.QQVersionConfigPath;
|
||||||
|
this.QQVersionConfig = this.isQuickUpdate
|
||||||
|
? JSON.parse(fs.readFileSync(this.QQVersionConfigPath!).toString())
|
||||||
|
: getDefaultQQVersionConfigInfo();
|
||||||
|
this.QQPackageInfo = JSON.parse(fs.readFileSync(this.QQPackageInfoPath).toString());
|
||||||
|
const { appid: IQQVersionAppid, qua: IQQVersionQua } = this.getAppidV2();
|
||||||
|
this.QQVersionAppid = IQQVersionAppid;
|
||||||
|
this.QQVersionQua = IQQVersionQua;
|
||||||
|
}
|
||||||
|
|
||||||
|
//基础函数
|
||||||
|
getQQBuildStr() {
|
||||||
|
return this.isQuickUpdate ? this.QQVersionConfig?.buildId : this.QQPackageInfo?.buildVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
getFullQQVesion() {
|
||||||
|
const version = this.isQuickUpdate ? this.QQVersionConfig?.curVersion : this.QQPackageInfo?.version;
|
||||||
|
if (!version) throw new Error('QQ版本获取失败');
|
||||||
|
return version;
|
||||||
|
}
|
||||||
|
|
||||||
|
requireMinNTQQBuild(buildStr: string) {
|
||||||
|
const currentBuild = parseInt(this.getQQBuildStr() || '0');
|
||||||
|
if (currentBuild == 0) throw new Error('QQBuildStr获取失败');
|
||||||
|
return currentBuild >= parseInt(buildStr);
|
||||||
|
}
|
||||||
|
|
||||||
|
//此方法不要直接使用
|
||||||
|
getQUAInternal() {
|
||||||
|
return systemPlatform === 'linux'
|
||||||
|
? `V1_LNX_NQ_${this.getFullQQVesion()}_${this.getQQBuildStr()}_GW_B`
|
||||||
|
: `V1_WIN_NQ_${this.getFullQQVesion()}_${this.getQQBuildStr()}_GW_B`;
|
||||||
|
}
|
||||||
|
|
||||||
|
getAppidV2(): { appid: string; qua: string } {
|
||||||
|
const appidTbale = AppidTable as unknown as QQAppidTableType;
|
||||||
|
try {
|
||||||
|
const fullVersion = this.getFullQQVesion();
|
||||||
|
if (!fullVersion) throw new Error('QQ版本获取失败');
|
||||||
|
const data = appidTbale[fullVersion];
|
||||||
|
if (data) {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
this.context.logger.log(`[QQ版本兼容性检测] 获取Appid异常 请检测NapCat/QQNT是否正常`);
|
||||||
|
}
|
||||||
|
// 以下是兜底措施
|
||||||
|
this.context.logger.log(
|
||||||
|
`[QQ版本兼容性检测] ${this.getFullQQVesion()} 版本兼容性不佳,可能会导致一些功能无法正常使用`,
|
||||||
|
);
|
||||||
|
return { appid: systemPlatform === 'linux' ? '537237950' : '537237765', qua: this.getQUAInternal() };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export let QQBasicInfo: QQBasicInfoWrapper | undefined;
|
||||||
90
src/common/utils/audio.ts
Normal file
90
src/common/utils/audio.ts
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
import fs from 'fs';
|
||||||
|
import { encode, getDuration, getWavFileInfo, isSilk, isWav } from 'silk-wasm';
|
||||||
|
import fsPromise from 'fs/promises';
|
||||||
|
import path from 'node:path';
|
||||||
|
import { randomUUID } from 'crypto';
|
||||||
|
import { spawn } from 'node:child_process';
|
||||||
|
import { LogWrapper } from './log';
|
||||||
|
|
||||||
|
export async function encodeSilk(filePath: string, TEMP_DIR: string, logger: LogWrapper) {
|
||||||
|
async function guessDuration(pttPath: string) {
|
||||||
|
const pttFileInfo = await fsPromise.stat(pttPath);
|
||||||
|
let duration = pttFileInfo.size / 1024 / 3; // 3kb/s
|
||||||
|
duration = Math.floor(duration);
|
||||||
|
duration = Math.max(1, duration);
|
||||||
|
logger.log('通过文件大小估算语音的时长:', duration);
|
||||||
|
return duration;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const file = await fsPromise.readFile(filePath);
|
||||||
|
const pttPath = path.join(TEMP_DIR, randomUUID());
|
||||||
|
if (!isSilk(file)) {
|
||||||
|
logger.log(`语音文件${filePath}需要转换成silk`);
|
||||||
|
const _isWav = isWav(file);
|
||||||
|
const pcmPath = pttPath + '.pcm';
|
||||||
|
let sampleRate = 0;
|
||||||
|
const convert = () => {
|
||||||
|
return new Promise<Buffer>((resolve, reject) => {
|
||||||
|
// todo: 通过配置文件获取ffmpeg路径
|
||||||
|
const ffmpegPath = process.env.FFMPEG_PATH || 'ffmpeg';
|
||||||
|
const cp = spawn(ffmpegPath, ['-y', '-i', filePath, '-ar', '24000', '-ac', '1', '-f', 's16le', pcmPath]);
|
||||||
|
cp.on('error', err => {
|
||||||
|
logger.log('FFmpeg处理转换出错: ', err.message);
|
||||||
|
return reject(err);
|
||||||
|
});
|
||||||
|
cp.on('exit', (code, signal) => {
|
||||||
|
const EXIT_CODES = [0, 255];
|
||||||
|
if (code == null || EXIT_CODES.includes(code)) {
|
||||||
|
sampleRate = 24000;
|
||||||
|
const data = fs.readFileSync(pcmPath);
|
||||||
|
fs.unlink(pcmPath, (err) => {
|
||||||
|
});
|
||||||
|
return resolve(data);
|
||||||
|
}
|
||||||
|
logger.log(`FFmpeg exit: code=${code ?? 'unknown'} sig=${signal ?? 'unknown'}`);
|
||||||
|
reject(Error('FFmpeg处理转换失败'));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
let input: Buffer;
|
||||||
|
if (!_isWav) {
|
||||||
|
input = await convert();
|
||||||
|
} else {
|
||||||
|
input = file;
|
||||||
|
const allowSampleRate = [8000, 12000, 16000, 24000, 32000, 44100, 48000];
|
||||||
|
const { fmt } = getWavFileInfo(input);
|
||||||
|
// log(`wav文件信息`, fmt)
|
||||||
|
if (!allowSampleRate.includes(fmt.sampleRate)) {
|
||||||
|
input = await convert();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const silk = await encode(input, sampleRate);
|
||||||
|
fs.writeFileSync(pttPath, silk.data);
|
||||||
|
logger.log(`语音文件${filePath}转换成功!`, pttPath, '时长:', silk.duration);
|
||||||
|
return {
|
||||||
|
converted: true,
|
||||||
|
path: pttPath,
|
||||||
|
duration: silk.duration / 1000,
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
const silk = file;
|
||||||
|
let duration = 0;
|
||||||
|
try {
|
||||||
|
duration = getDuration(silk) / 1000;
|
||||||
|
} catch (e: any) {
|
||||||
|
logger.log('获取语音文件时长失败, 使用文件大小推测时长', filePath, e.stack);
|
||||||
|
duration = await guessDuration(filePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
converted: false,
|
||||||
|
path: filePath,
|
||||||
|
duration,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
} catch (error: any) {
|
||||||
|
logger.logError('convert silk failed', error.stack);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
301
src/common/utils/file.ts
Normal file
301
src/common/utils/file.ts
Normal file
@@ -0,0 +1,301 @@
|
|||||||
|
import fs from 'fs';
|
||||||
|
import fsPromise, { stat } from 'fs/promises';
|
||||||
|
import crypto, { randomUUID } from 'crypto';
|
||||||
|
import util from 'util';
|
||||||
|
import path from 'node:path';
|
||||||
|
import * as fileType from 'file-type';
|
||||||
|
import { LogWrapper } from './log';
|
||||||
|
|
||||||
|
export function isGIF(path: string) {
|
||||||
|
const buffer = Buffer.alloc(4);
|
||||||
|
const fd = fs.openSync(path, 'r');
|
||||||
|
fs.readSync(fd, buffer, 0, 4, 0);
|
||||||
|
fs.closeSync(fd);
|
||||||
|
return buffer.toString() === 'GIF8';
|
||||||
|
}
|
||||||
|
|
||||||
|
// 定义一个异步函数来检查文件是否存在
|
||||||
|
export function checkFileReceived(path: string, timeout: number = 3000): Promise<void> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const startTime = Date.now();
|
||||||
|
|
||||||
|
function check() {
|
||||||
|
if (fs.existsSync(path)) {
|
||||||
|
resolve();
|
||||||
|
} else if (Date.now() - startTime > timeout) {
|
||||||
|
reject(new Error(`文件不存在: ${path}`));
|
||||||
|
} else {
|
||||||
|
setTimeout(check, 100);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
const readFile = util.promisify(fs.readFile);
|
||||||
|
const result = {
|
||||||
|
err: '',
|
||||||
|
data: '',
|
||||||
|
};
|
||||||
|
try {
|
||||||
|
// 读取文件内容
|
||||||
|
// if (!fs.existsSync(path)){
|
||||||
|
// path = path.replace("\\Ori\\", "\\Thumb\\");
|
||||||
|
// }
|
||||||
|
try {
|
||||||
|
await checkFileReceived(path, 5000);
|
||||||
|
} catch (e: any) {
|
||||||
|
result.err = e.toString();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
const data = await readFile(path);
|
||||||
|
// 转换为Base64编码
|
||||||
|
result.data = data.toString('base64');
|
||||||
|
} catch (err: any) {
|
||||||
|
result.err = err.toString();
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export function calculateFileMD5(filePath: string): Promise<string> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
// 创建一个流式读取器
|
||||||
|
const stream = fs.createReadStream(filePath);
|
||||||
|
const hash = crypto.createHash('md5');
|
||||||
|
|
||||||
|
stream.on('data', (data: Buffer) => {
|
||||||
|
// 当读取到数据时,更新哈希对象的状态
|
||||||
|
hash.update(data);
|
||||||
|
});
|
||||||
|
|
||||||
|
stream.on('end', () => {
|
||||||
|
// 文件读取完成,计算哈希
|
||||||
|
const md5 = hash.digest('hex');
|
||||||
|
resolve(md5);
|
||||||
|
});
|
||||||
|
|
||||||
|
stream.on('error', (err: Error) => {
|
||||||
|
// 处理可能的读取错误
|
||||||
|
reject(err);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface HttpDownloadOptions {
|
||||||
|
url: string;
|
||||||
|
headers?: Record<string, string> | string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function httpDownload(options: string | HttpDownloadOptions): Promise<Buffer> {
|
||||||
|
const chunks: Buffer[] = [];
|
||||||
|
let url: string;
|
||||||
|
let headers: Record<string, string> = {
|
||||||
|
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.71 Safari/537.36',
|
||||||
|
};
|
||||||
|
if (typeof options === 'string') {
|
||||||
|
url = options;
|
||||||
|
const host = new URL(url).hostname;
|
||||||
|
headers['Host'] = host;
|
||||||
|
} else {
|
||||||
|
url = options.url;
|
||||||
|
if (options.headers) {
|
||||||
|
if (typeof options.headers === 'string') {
|
||||||
|
headers = JSON.parse(options.headers);
|
||||||
|
} else {
|
||||||
|
headers = options.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}`);
|
||||||
|
|
||||||
|
const blob = await fetchRes.blob();
|
||||||
|
const buffer = await blob.arrayBuffer();
|
||||||
|
return Buffer.from(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
type Uri2LocalRes = {
|
||||||
|
success: boolean,
|
||||||
|
errMsg: string,
|
||||||
|
fileName: string,
|
||||||
|
ext: string,
|
||||||
|
path: string,
|
||||||
|
isLocal: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function uri2local(TempDir: string, UriOrPath: string, fileName: string | null = null): Promise<Uri2LocalRes> {
|
||||||
|
const res = {
|
||||||
|
success: false,
|
||||||
|
errMsg: '',
|
||||||
|
fileName: '',
|
||||||
|
ext: '',
|
||||||
|
path: '',
|
||||||
|
isLocal: false,
|
||||||
|
};
|
||||||
|
if (!fileName) fileName = randomUUID();
|
||||||
|
let filePath = path.join(TempDir, fileName);//临时目录
|
||||||
|
let url = null;
|
||||||
|
//区分path和uri
|
||||||
|
try {
|
||||||
|
if (fs.existsSync(UriOrPath)) url = new URL('file://' + UriOrPath);
|
||||||
|
} catch (error: any) {
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
url = new URL(UriOrPath);
|
||||||
|
} catch (error: any) {
|
||||||
|
}
|
||||||
|
|
||||||
|
//验证url
|
||||||
|
if (!url) {
|
||||||
|
res.errMsg = `UriOrPath ${UriOrPath} 解析失败,可能${UriOrPath}不存在`;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (url.protocol == 'base64:') {
|
||||||
|
// base64转成文件
|
||||||
|
const base64Data = UriOrPath.split('base64://')[1];
|
||||||
|
try {
|
||||||
|
const buffer = Buffer.from(base64Data, 'base64');
|
||||||
|
fs.writeFileSync(filePath, buffer);
|
||||||
|
} catch (e: any) {
|
||||||
|
res.errMsg = 'base64文件下载失败,' + e.toString();
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
} else if (url.protocol == 'http:' || url.protocol == 'https:') {
|
||||||
|
// 下载文件
|
||||||
|
let buffer: Buffer | null = null;
|
||||||
|
try {
|
||||||
|
buffer = await httpDownload(UriOrPath);
|
||||||
|
} catch (e: any) {
|
||||||
|
res.errMsg = `${url}下载失败,` + e.toString();
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const pathInfo = path.parse(decodeURIComponent(url.pathname));
|
||||||
|
if (pathInfo.name) {
|
||||||
|
fileName = pathInfo.name;
|
||||||
|
if (pathInfo.ext) {
|
||||||
|
fileName += pathInfo.ext;
|
||||||
|
// res.ext = pathInfo.ext
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fileName = fileName.replace(/[/\\:*?"<>|]/g, '_');
|
||||||
|
res.fileName = fileName;
|
||||||
|
filePath = path.join(TempDir, randomUUID() + fileName);
|
||||||
|
fs.writeFileSync(filePath, buffer);
|
||||||
|
} catch (e: any) {
|
||||||
|
res.errMsg = `${url}下载失败,` + e.toString();
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let pathname: string;
|
||||||
|
if (url.protocol === 'file:') {
|
||||||
|
// await fs.copyFile(url.pathname, filePath);
|
||||||
|
pathname = decodeURIComponent(url.pathname);
|
||||||
|
if (process.platform === 'win32') {
|
||||||
|
filePath = pathname.slice(1);
|
||||||
|
} else {
|
||||||
|
filePath = pathname;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 26702执行forword file文件操作 不应该在这里乱来
|
||||||
|
// const cache = await dbUtil.getFileCacheByName(uri);
|
||||||
|
// if (cache) {
|
||||||
|
// filePath = cache.path;
|
||||||
|
// } else {
|
||||||
|
// filePath = uri;
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
res.isLocal = true;
|
||||||
|
}
|
||||||
|
// else{
|
||||||
|
// res.errMsg = `不支持的file协议,` + url.protocol
|
||||||
|
// return res
|
||||||
|
// }
|
||||||
|
// if (isGIF(filePath) && !res.isLocal) {
|
||||||
|
// await fs.rename(filePath, filePath + ".gif");
|
||||||
|
// filePath += ".gif";
|
||||||
|
// }
|
||||||
|
if (!res.isLocal && !res.ext) {
|
||||||
|
try {
|
||||||
|
const ext: string | undefined = (await fileType.fileTypeFromFile(filePath))?.ext;
|
||||||
|
if (ext) {
|
||||||
|
fs.renameSync(filePath, filePath + `.${ext}`);
|
||||||
|
filePath += `.${ext}`;
|
||||||
|
res.fileName += `.${ext}`;
|
||||||
|
res.ext = ext;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
// log("获取文件类型失败", filePath,e.stack)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
res.success = true;
|
||||||
|
res.path = filePath;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function copyFolder(sourcePath: string, destPath: string, logger: LogWrapper) {
|
||||||
|
try {
|
||||||
|
const entries = await fsPromise.readdir(sourcePath, { withFileTypes: true });
|
||||||
|
await fsPromise.mkdir(destPath, { recursive: true });
|
||||||
|
for (const entry of entries) {
|
||||||
|
const srcPath = path.join(sourcePath, entry.name);
|
||||||
|
const dstPath = path.join(destPath, entry.name);
|
||||||
|
if (entry.isDirectory()) {
|
||||||
|
await copyFolder(srcPath, dstPath, logger);
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
await fsPromise.copyFile(srcPath, dstPath);
|
||||||
|
} catch (error) {
|
||||||
|
logger.logError(`无法复制文件 '${srcPath}' 到 '${dstPath}': ${error}`);
|
||||||
|
// 这里可以决定是否要继续复制其他文件
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
logger.logError('复制文件夹时出错:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
165
src/common/utils/helper.ts
Normal file
165
src/common/utils/helper.ts
Normal file
@@ -0,0 +1,165 @@
|
|||||||
|
import crypto from 'node:crypto';
|
||||||
|
import path from 'node:path';
|
||||||
|
import fs from 'fs';
|
||||||
|
import * as fsPromise from 'node:fs/promises';
|
||||||
|
import os from 'node:os';
|
||||||
|
import { QQLevel } from '@/core';
|
||||||
|
|
||||||
|
//下面这个类是用于将uid+msgid合并的类
|
||||||
|
|
||||||
|
export class UUIDConverter {
|
||||||
|
static encode(highStr: string, lowStr: string): string {
|
||||||
|
const high = BigInt(highStr);
|
||||||
|
const low = BigInt(lowStr);
|
||||||
|
const highHex = high.toString(16).padStart(16, '0');
|
||||||
|
const lowHex = low.toString(16).padStart(16, '0');
|
||||||
|
const combinedHex = highHex + lowHex;
|
||||||
|
const uuid = `${combinedHex.substring(0, 8)}-${combinedHex.substring(8, 12)}-${combinedHex.substring(
|
||||||
|
12,
|
||||||
|
16,
|
||||||
|
)}-${combinedHex.substring(16, 20)}-${combinedHex.substring(20)}`;
|
||||||
|
return uuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
static decode(uuid: string): { high: string; low: string } {
|
||||||
|
const hex = uuid.replace(/-/g, '');
|
||||||
|
const high = BigInt('0x' + hex.substring(0, 16));
|
||||||
|
const low = BigInt('0x' + hex.substring(16));
|
||||||
|
return { high: high.toString(), low: low.toString() };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function sleep(ms: number): Promise<void> {
|
||||||
|
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) {
|
||||||
|
const h = crypto.createHash('md5');
|
||||||
|
h.update(s);
|
||||||
|
return h.digest('hex');
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isNull(value: any) {
|
||||||
|
return value === undefined || value === null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isNumeric(str: string) {
|
||||||
|
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 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 function getDefaultQQVersionConfigInfo(): QQVersionConfigType {
|
||||||
|
if (os.platform() === 'linux') {
|
||||||
|
return {
|
||||||
|
baseVersion: '3.2.12-26702',
|
||||||
|
curVersion: '3.2.12-26702',
|
||||||
|
prevVersion: '',
|
||||||
|
onErrorVersions: [],
|
||||||
|
buildId: '26702',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
baseVersion: '9.9.15-26702',
|
||||||
|
curVersion: '9.9.15-26702',
|
||||||
|
prevVersion: '',
|
||||||
|
onErrorVersions: [],
|
||||||
|
buildId: '26702',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getQQVersionConfigPath(exePath: string = ''): string | undefined {
|
||||||
|
let configVersionInfoPath;
|
||||||
|
if (os.platform() !== 'linux') {
|
||||||
|
configVersionInfoPath = path.join(path.dirname(exePath), 'resources', 'app', 'versions', 'config.json');
|
||||||
|
} else {
|
||||||
|
const userPath = os.homedir();
|
||||||
|
const appDataPath = path.resolve(userPath, './.config/QQ');
|
||||||
|
configVersionInfoPath = path.resolve(appDataPath, './versions/config.json');
|
||||||
|
}
|
||||||
|
if (typeof configVersionInfoPath !== 'string') {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
if (!fs.existsSync(configVersionInfoPath)) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
return configVersionInfoPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function deleteOldFiles(directoryPath: string, daysThreshold: number) {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function calcQQLevel(level: QQLevel) {
|
||||||
|
const { crownNum, sunNum, moonNum, starNum } = level;
|
||||||
|
return crownNum * 64 + sunNum * 16 + moonNum * 4 + starNum;
|
||||||
|
}
|
||||||
138
src/common/utils/log.ts
Normal file
138
src/common/utils/log.ts
Normal file
@@ -0,0 +1,138 @@
|
|||||||
|
import log4js, { Configuration } from 'log4js';
|
||||||
|
import { truncateString } from '@/common/utils/helper';
|
||||||
|
import path from 'node:path';
|
||||||
|
import chalk from 'chalk';
|
||||||
|
|
||||||
|
export enum LogLevel {
|
||||||
|
DEBUG = 'debug',
|
||||||
|
INFO = 'info',
|
||||||
|
WARN = 'warn',
|
||||||
|
ERROR = 'error',
|
||||||
|
FATAL = 'fatal',
|
||||||
|
}
|
||||||
|
|
||||||
|
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}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class LogWrapper {
|
||||||
|
fileLogEnabled = true;
|
||||||
|
consoleLogEnabled = true;
|
||||||
|
logConfig: Configuration;
|
||||||
|
loggerConsole: log4js.Logger;
|
||||||
|
loggerFile: log4js.Logger;
|
||||||
|
loggerDefault: log4js.Logger;
|
||||||
|
// eslint-disable-next-line no-control-regex
|
||||||
|
colorEscape = /\x1B[@-_][0-?]*[ -/]*[@-~]/g;
|
||||||
|
|
||||||
|
constructor(logDir: string) {
|
||||||
|
const filename = `${getFormattedTimestamp()}.log`;
|
||||||
|
const logPath = path.join(logDir, filename);
|
||||||
|
this.logConfig = {
|
||||||
|
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(this.logConfig);
|
||||||
|
this.loggerConsole = log4js.getLogger('console');
|
||||||
|
this.loggerFile = log4js.getLogger('file');
|
||||||
|
this.loggerDefault = log4js.getLogger('default');
|
||||||
|
this.setLogSelfInfo({ nick: '', uin: '', uid: '' });
|
||||||
|
}
|
||||||
|
|
||||||
|
setFileAndConsoleLogLevel(fileLogLevel: LogLevel, consoleLogLevel: LogLevel) {
|
||||||
|
this.logConfig.categories.file.level = fileLogLevel;
|
||||||
|
this.logConfig.categories.console.level = consoleLogLevel;
|
||||||
|
log4js.configure(this.logConfig);
|
||||||
|
}
|
||||||
|
|
||||||
|
setLogSelfInfo(selfInfo: { nick: string, uin: string, uid: string }) {
|
||||||
|
const userInfo = `${selfInfo.nick}(${selfInfo.uin})`;
|
||||||
|
this.loggerConsole.addContext('userInfo', userInfo);
|
||||||
|
this.loggerFile.addContext('userInfo', userInfo);
|
||||||
|
this.loggerDefault.addContext('userInfo', userInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
setFileLogEnabled(isEnabled: boolean) {
|
||||||
|
this.fileLogEnabled = isEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
setConsoleLogEnabled(isEnabled: boolean) {
|
||||||
|
this.consoleLogEnabled = isEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
_log(level: LogLevel, ...args: any[]) {
|
||||||
|
if (this.consoleLogEnabled) {
|
||||||
|
this.loggerConsole[level](this.formatMsg(args));
|
||||||
|
}
|
||||||
|
if (this.fileLogEnabled) {
|
||||||
|
this.loggerFile[level](this.formatMsg(args).replace(this.colorEscape, ''));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log(...args: any[]) {
|
||||||
|
// info 等级
|
||||||
|
this._log(LogLevel.INFO, ...args);
|
||||||
|
}
|
||||||
|
|
||||||
|
logDebug(...args: any[]) {
|
||||||
|
this._log(LogLevel.DEBUG, ...args);
|
||||||
|
}
|
||||||
|
|
||||||
|
logError(...args: any[]) {
|
||||||
|
this._log(LogLevel.ERROR, ...args);
|
||||||
|
}
|
||||||
|
|
||||||
|
logWarn(...args: any[]) {
|
||||||
|
this._log(LogLevel.WARN, ...args);
|
||||||
|
}
|
||||||
|
|
||||||
|
logFatal(...args: any[]) {
|
||||||
|
this._log(LogLevel.FATAL, ...args);
|
||||||
|
}
|
||||||
|
}
|
||||||
21
src/common/utils/proxy-handler.ts
Normal file
21
src/common/utils/proxy-handler.ts
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
import { LogWrapper } from './log';
|
||||||
|
|
||||||
|
export function proxyHandlerOf(logger: LogWrapper) {
|
||||||
|
return {
|
||||||
|
get(target: any, prop: any, receiver: any) {
|
||||||
|
// console.log('get', prop, typeof target[prop]);
|
||||||
|
if (typeof target[prop] === 'undefined') {
|
||||||
|
// 如果方法不存在,返回一个函数,这个函数调用existentMethod
|
||||||
|
return (..._args: unknown[]) => {
|
||||||
|
logger.logDebug(`${target.constructor.name} has no method ${prop}`);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
// 如果方法存在,正常返回
|
||||||
|
return Reflect.get(target, prop, receiver);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function proxiedListenerOf<T extends object>(listener: T, logger: LogWrapper) {
|
||||||
|
return new Proxy<T>(listener, proxyHandlerOf(logger));
|
||||||
|
}
|
||||||
194
src/common/utils/request.ts
Normal file
194
src/common/utils/request.ts
Normal file
@@ -0,0 +1,194 @@
|
|||||||
|
import https from 'node:https';
|
||||||
|
import http from 'node:http';
|
||||||
|
import { readFileSync } from 'node:fs';
|
||||||
|
|
||||||
|
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, cookies: string): Promise<string> {
|
||||||
|
return new Promise(async (resolve, reject) => {
|
||||||
|
type retType = { retcode: number, result?: { url: string } };
|
||||||
|
try {
|
||||||
|
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.log('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;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
73
src/common/utils/system.ts
Normal file
73
src/common/utils/system.ts
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
import os from 'node:os';
|
||||||
|
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 cpuArch = os.arch();
|
||||||
|
export const systemVersion = os.release();
|
||||||
|
export const hostname = osName;
|
||||||
|
export const downloadsPath = path.join(homeDir, 'Downloads');
|
||||||
|
export const systemName = os.type();
|
||||||
17
src/common/utils/types.ts
Normal file
17
src/common/utils/types.ts
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
//QQVersionType
|
||||||
|
type QQPackageInfoType = {
|
||||||
|
version: string;
|
||||||
|
buildVersion: string;
|
||||||
|
platform: string;
|
||||||
|
eleArch: string;
|
||||||
|
}
|
||||||
|
type QQVersionConfigType = {
|
||||||
|
baseVersion: string;
|
||||||
|
curVersion: string;
|
||||||
|
prevVersion: string;
|
||||||
|
onErrorVersions: Array<any>;
|
||||||
|
buildId: string;
|
||||||
|
}
|
||||||
|
type QQAppidTableType = {
|
||||||
|
[key: string]: { appid: string, qua: string };
|
||||||
|
}
|
||||||
59
src/common/utils/video.ts
Normal file
59
src/common/utils/video.ts
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
import ffmpeg, { FfprobeStream } from 'fluent-ffmpeg';
|
||||||
|
import fs from 'fs';
|
||||||
|
import type { LogWrapper } from './log';
|
||||||
|
|
||||||
|
export async function getVideoInfo(filePath: string, logger: LogWrapper) {
|
||||||
|
const size = fs.statSync(filePath).size;
|
||||||
|
return new Promise<{
|
||||||
|
width: number,
|
||||||
|
height: number,
|
||||||
|
time: number,
|
||||||
|
format: string,
|
||||||
|
size: number,
|
||||||
|
filePath: string
|
||||||
|
}>((resolve, reject) => {
|
||||||
|
const ffmpegPath = process.env.FFMPEG_PATH;
|
||||||
|
ffmpegPath && ffmpeg.setFfmpegPath(ffmpegPath);
|
||||||
|
ffmpeg(filePath).ffprobe((err: any, metadata: ffmpeg.FfprobeData) => {
|
||||||
|
if (err) {
|
||||||
|
reject(err);
|
||||||
|
} else {
|
||||||
|
const videoStream = metadata.streams.find((s: FfprobeStream) => s.codec_type === 'video');
|
||||||
|
if (videoStream) {
|
||||||
|
logger.log(`视频尺寸: ${videoStream.width}x${videoStream.height}`);
|
||||||
|
} else {
|
||||||
|
return reject('未找到视频流信息。');
|
||||||
|
}
|
||||||
|
resolve({
|
||||||
|
width: videoStream.width!, height: videoStream.height!,
|
||||||
|
time: parseInt(videoStream.duration!),
|
||||||
|
format: metadata.format.format_name!,
|
||||||
|
size,
|
||||||
|
filePath,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function checkFfmpeg(newPath: string | null = null, logger: LogWrapper): Promise<boolean> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
logger.log('开始检查ffmpeg', newPath);
|
||||||
|
if (newPath) {
|
||||||
|
ffmpeg.setFfmpegPath(newPath);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
ffmpeg.getAvailableFormats((err: any, formats: any) => {
|
||||||
|
if (err) {
|
||||||
|
logger.log('ffmpeg is not installed or not found in PATH:', err);
|
||||||
|
resolve(false);
|
||||||
|
} else {
|
||||||
|
logger.log('ffmpeg is installed.');
|
||||||
|
resolve(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
resolve(false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
27
src/core/adapters/NodeIDependsAdapter.ts
Normal file
27
src/core/adapters/NodeIDependsAdapter.ts
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
interface IDependsAdapter {
|
||||||
|
onMSFStatusChange(arg1: number, arg2: number): void;
|
||||||
|
|
||||||
|
onMSFSsoError(args: unknown): void;
|
||||||
|
|
||||||
|
getGroupCode(args: unknown): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface NodeIDependsAdapter extends IDependsAdapter {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-misused-new
|
||||||
|
new(adapter: IDependsAdapter): NodeIDependsAdapter;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class DependsAdapter implements IDependsAdapter {
|
||||||
|
onMSFStatusChange(arg1: number, arg2: number) {
|
||||||
|
// console.log(arg1, arg2);
|
||||||
|
// if (arg1 == 2 && arg2 == 2) {
|
||||||
|
// log("NapCat丢失网络连接,请检查网络")
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
onMSFSsoError(args: unknown) {
|
||||||
|
}
|
||||||
|
|
||||||
|
getGroupCode(args: unknown) {
|
||||||
|
}
|
||||||
|
}
|
||||||
23
src/core/adapters/NodeIDispatcherAdapter.ts
Normal file
23
src/core/adapters/NodeIDispatcherAdapter.ts
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
interface IDispatcherAdapter {
|
||||||
|
dispatchRequest(arg: unknown): void;
|
||||||
|
|
||||||
|
dispatchCall(arg: unknown): void;
|
||||||
|
|
||||||
|
dispatchCallWithJson(arg: unknown): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface NodeIDispatcherAdapter extends IDispatcherAdapter {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-misused-new
|
||||||
|
new(adapter: IDispatcherAdapter): NodeIDispatcherAdapter;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class DispatcherAdapter implements IDispatcherAdapter {
|
||||||
|
dispatchRequest(arg: unknown) {
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatchCall(arg: unknown) {
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatchCallWithJson(arg: unknown) {
|
||||||
|
}
|
||||||
|
}
|
||||||
48
src/core/adapters/NodeIGlobalAdapter.ts
Normal file
48
src/core/adapters/NodeIGlobalAdapter.ts
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
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 {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-misused-new
|
||||||
|
new(adapter: IGlobalAdapter): NodeIGlobalAdapter;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class GlobalAdapter implements IGlobalAdapter {
|
||||||
|
onLog(...args: unknown[]) {
|
||||||
|
}
|
||||||
|
|
||||||
|
onGetSrvCalTime(...args: unknown[]) {
|
||||||
|
}
|
||||||
|
|
||||||
|
onShowErrUITips(...args: unknown[]) {
|
||||||
|
}
|
||||||
|
|
||||||
|
fixPicImgType(...args: unknown[]) {
|
||||||
|
}
|
||||||
|
|
||||||
|
getAppSetting(...args: unknown[]) {
|
||||||
|
}
|
||||||
|
|
||||||
|
onInstallFinished(...args: unknown[]) {
|
||||||
|
}
|
||||||
|
|
||||||
|
onUpdateGeneralFlag(...args: unknown[]) {
|
||||||
|
}
|
||||||
|
|
||||||
|
onGetOfflineMsg(...args: unknown[]) {
|
||||||
|
}
|
||||||
|
}
|
||||||
3
src/core/adapters/index.ts
Normal file
3
src/core/adapters/index.ts
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
export * from './NodeIDependsAdapter';
|
||||||
|
export * from './NodeIDispatcherAdapter';
|
||||||
|
export * from './NodeIGlobalAdapter';
|
||||||
62
src/core/apis/collection.ts
Normal file
62
src/core/apis/collection.ts
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
import { InstanceContext, NapCatCore } from '..';
|
||||||
|
|
||||||
|
export class NTQQCollectionApi {
|
||||||
|
context: InstanceContext;
|
||||||
|
core: NapCatCore;
|
||||||
|
|
||||||
|
constructor(context: InstanceContext, core: NapCatCore) {
|
||||||
|
this.context = context;
|
||||||
|
this.core = core;
|
||||||
|
}
|
||||||
|
|
||||||
|
async createCollection(authorUin: string, authorUid: string, authorName: string, brief: string, rawData: string) {
|
||||||
|
const param = {
|
||||||
|
commInfo: {
|
||||||
|
bid: 1,
|
||||||
|
category: 2,
|
||||||
|
author: {
|
||||||
|
type: 1,
|
||||||
|
numId: authorUin,
|
||||||
|
strId: authorName,
|
||||||
|
groupId: '0',
|
||||||
|
groupName: '',
|
||||||
|
uid: authorUid,
|
||||||
|
},
|
||||||
|
customGroupId: '0',
|
||||||
|
createTime: Date.now().toString(),
|
||||||
|
sequence: Date.now().toString(),
|
||||||
|
},
|
||||||
|
richMediaSummary: {
|
||||||
|
originalUri: '',
|
||||||
|
publisher: '',
|
||||||
|
richMediaVersion: 0,
|
||||||
|
subTitle: '',
|
||||||
|
title: '',
|
||||||
|
brief: brief,
|
||||||
|
picList: [],
|
||||||
|
contentType: 1,
|
||||||
|
},
|
||||||
|
richMediaContent: {
|
||||||
|
rawData: rawData,
|
||||||
|
bizDataList: [],
|
||||||
|
picList: [],
|
||||||
|
fileList: [],
|
||||||
|
},
|
||||||
|
need_share_url: false,
|
||||||
|
};
|
||||||
|
return this.context.session.getCollectionService().createNewCollectionItem(param);
|
||||||
|
}
|
||||||
|
|
||||||
|
async getAllCollection(category: number = 0, count: number = 50) {
|
||||||
|
const param = {
|
||||||
|
category: category,
|
||||||
|
groupId: -1,
|
||||||
|
forceSync: true,
|
||||||
|
forceFromDb: false,
|
||||||
|
timeStamp: '0',
|
||||||
|
count: count,
|
||||||
|
searchDown: true,
|
||||||
|
};
|
||||||
|
return this.context.session.getCollectionService().getCollectionItemList(param);
|
||||||
|
}
|
||||||
|
}
|
||||||
380
src/core/apis/file.ts
Normal file
380
src/core/apis/file.ts
Normal file
@@ -0,0 +1,380 @@
|
|||||||
|
import {
|
||||||
|
CacheFileListItem,
|
||||||
|
CacheFileType,
|
||||||
|
ChatCacheListItemBasic,
|
||||||
|
ChatType,
|
||||||
|
ElementType,
|
||||||
|
IMAGE_HTTP_HOST,
|
||||||
|
IMAGE_HTTP_HOST_NT,
|
||||||
|
Peer,
|
||||||
|
PicElement,
|
||||||
|
} from '@/core/entities';
|
||||||
|
import path from 'path';
|
||||||
|
import fs from 'fs';
|
||||||
|
import fsPromises from 'fs/promises';
|
||||||
|
import { InstanceContext, NapCatCore, OnRichMediaDownloadCompleteParams } from '@/core';
|
||||||
|
import * as fileType from 'file-type';
|
||||||
|
import imageSize from 'image-size';
|
||||||
|
import { ISizeCalculationResult } from 'image-size/dist/types/interface';
|
||||||
|
import { NodeIKernelSearchService } from '../services/NodeIKernelSearchService';
|
||||||
|
import { RkeyManager } from '../helper/rkey';
|
||||||
|
import { calculateFileMD5 } from '@/common/utils/file';
|
||||||
|
|
||||||
|
|
||||||
|
export class NTQQFileApi {
|
||||||
|
context: InstanceContext;
|
||||||
|
core: NapCatCore;
|
||||||
|
rkeyManager: RkeyManager;
|
||||||
|
|
||||||
|
constructor(context: InstanceContext, core: NapCatCore) {
|
||||||
|
this.context = context;
|
||||||
|
this.core = core;
|
||||||
|
this.rkeyManager = new RkeyManager('http://napcat-sign.wumiao.wang:2082/rkey', this.context.logger);
|
||||||
|
}
|
||||||
|
|
||||||
|
async getFileType(filePath: string) {
|
||||||
|
return fileType.fileTypeFromFile(filePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
async copyFile(filePath: string, destPath: string) {
|
||||||
|
await this.core.util.copyFile(filePath, destPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
async getFileSize(filePath: string): Promise<number> {
|
||||||
|
return await this.core.util.getFileSize(filePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
async getVideoUrl(peer: Peer, msgId: string, elementId: string) {
|
||||||
|
return (await this.context.session.getRichMediaService().getVideoPlayUrlV2(peer, msgId, elementId, 0, {
|
||||||
|
downSourceType: 1,
|
||||||
|
triggerType: 1,
|
||||||
|
})).urlResult.domainUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 上传文件到QQ的文件夹
|
||||||
|
async uploadFile(filePath: string, elementType: ElementType = ElementType.PIC, elementSubType: number = 0) {
|
||||||
|
// napCatCore.wrapper.util.
|
||||||
|
const fileMd5 = await calculateFileMD5(filePath);
|
||||||
|
let ext: string = (await this.getFileType(filePath))?.ext as string || '';
|
||||||
|
if (ext) {
|
||||||
|
ext = '.' + ext;
|
||||||
|
}
|
||||||
|
let fileName = `${path.basename(filePath)}`;
|
||||||
|
if (fileName.indexOf('.') === -1) {
|
||||||
|
fileName += ext;
|
||||||
|
}
|
||||||
|
const mediaPath = this.context.session.getMsgService().getRichMediaFilePathForGuild({
|
||||||
|
md5HexStr: fileMd5,
|
||||||
|
fileName: fileName,
|
||||||
|
elementType: elementType,
|
||||||
|
elementSubType,
|
||||||
|
thumbSize: 0,
|
||||||
|
needCreate: true,
|
||||||
|
downloadType: 1,
|
||||||
|
file_uuid: '',
|
||||||
|
});
|
||||||
|
await this.copyFile(filePath, mediaPath!);
|
||||||
|
const fileSize = await this.getFileSize(filePath);
|
||||||
|
return {
|
||||||
|
md5: fileMd5,
|
||||||
|
fileName,
|
||||||
|
path: mediaPath,
|
||||||
|
fileSize,
|
||||||
|
ext,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async downloadMediaByUuid() {
|
||||||
|
//napCatCore.session.getRichMediaService().downloadFileForFileUuid();
|
||||||
|
}
|
||||||
|
|
||||||
|
async downloadMedia(msgId: string, chatType: ChatType, peerUid: string, elementId: string, thumbPath: string, sourcePath: string, timeout = 1000 * 60 * 2, force: boolean = false) {
|
||||||
|
//logDebug('receive downloadMedia task', msgId, chatType, peerUid, elementId, thumbPath, sourcePath, timeout, force);
|
||||||
|
// 用于下载收到的消息中的图片等
|
||||||
|
if (sourcePath && fs.existsSync(sourcePath)) {
|
||||||
|
if (force) {
|
||||||
|
try {
|
||||||
|
await fsPromises.unlink(sourcePath);
|
||||||
|
} catch (e) {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return sourcePath;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const data = await this.core.eventWrapper.CallNormalEvent<
|
||||||
|
(
|
||||||
|
params: {
|
||||||
|
fileModelId: string,
|
||||||
|
downloadSourceType: number,
|
||||||
|
triggerType: number,
|
||||||
|
msgId: string,
|
||||||
|
chatType: ChatType,
|
||||||
|
peerUid: string,
|
||||||
|
elementId: string,
|
||||||
|
thumbSize: number,
|
||||||
|
downloadType: number,
|
||||||
|
filePath: string
|
||||||
|
}) => Promise<unknown>,
|
||||||
|
(fileTransNotifyInfo: OnRichMediaDownloadCompleteParams) => void
|
||||||
|
>(
|
||||||
|
'NodeIKernelMsgService/downloadRichMedia',
|
||||||
|
'NodeIKernelMsgListener/onRichMediaDownloadComplete',
|
||||||
|
1,
|
||||||
|
timeout,
|
||||||
|
(arg: OnRichMediaDownloadCompleteParams) => {
|
||||||
|
if (arg.msgId === msgId) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fileModelId: '0',
|
||||||
|
downloadSourceType: 0,
|
||||||
|
triggerType: 1,
|
||||||
|
msgId: msgId,
|
||||||
|
chatType: chatType,
|
||||||
|
peerUid: peerUid,
|
||||||
|
elementId: elementId,
|
||||||
|
thumbSize: 0,
|
||||||
|
downloadType: 1,
|
||||||
|
filePath: thumbPath,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
let msg = await this.core.apis.MsgApi.getMsgsByMsgId({
|
||||||
|
guildId: '',
|
||||||
|
chatType: chatType,
|
||||||
|
peerUid: peerUid,
|
||||||
|
}, [msgId]);
|
||||||
|
if (msg.msgList.length === 0) {
|
||||||
|
return data[1].filePath;
|
||||||
|
}
|
||||||
|
//获取原始消息
|
||||||
|
let FileElements = msg?.msgList[0]?.elements?.find(e => e.elementId === elementId);
|
||||||
|
if (!FileElements) {
|
||||||
|
//失败则就乱来 Todo
|
||||||
|
return data[1].filePath;
|
||||||
|
}
|
||||||
|
//从原始消息获取文件路径
|
||||||
|
let filePath =
|
||||||
|
FileElements?.fileElement?.filePath ||
|
||||||
|
FileElements?.pttElement?.filePath ||
|
||||||
|
FileElements?.videoElement?.filePath ||
|
||||||
|
FileElements?.picElement?.sourcePath;
|
||||||
|
return filePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
async getImageSize(filePath: string): Promise<ISizeCalculationResult | undefined> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
imageSize(filePath, (err, dimensions) => {
|
||||||
|
if (err) {
|
||||||
|
reject(err);
|
||||||
|
} else {
|
||||||
|
resolve(dimensions);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async addFileCache(peer: Peer, msgId: string, msgSeq: string, senderUid: string, elemId: string, elemType: string, fileSize: string, fileName: string) {
|
||||||
|
let GroupData;
|
||||||
|
let BuddyData;
|
||||||
|
if (peer.chatType === ChatType.group) {
|
||||||
|
GroupData =
|
||||||
|
[{
|
||||||
|
groupCode: peer.peerUid,
|
||||||
|
isConf: false,
|
||||||
|
hasModifyConfGroupFace: true,
|
||||||
|
hasModifyConfGroupName: true,
|
||||||
|
groupName: 'NapCat.Cached',
|
||||||
|
remark: 'NapCat.Cached',
|
||||||
|
}];
|
||||||
|
} else if (peer.chatType === ChatType.friend) {
|
||||||
|
BuddyData = [{
|
||||||
|
category_name: 'NapCat.Cached',
|
||||||
|
peerUid: peer.peerUid,
|
||||||
|
peerUin: peer.peerUid,
|
||||||
|
remark: 'NapCat.Cached',
|
||||||
|
}];
|
||||||
|
} else {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.context.session.getSearchService().addSearchHistory({
|
||||||
|
type: 4,
|
||||||
|
contactList: [],
|
||||||
|
id: -1,
|
||||||
|
groupInfos: [],
|
||||||
|
msgs: [],
|
||||||
|
fileInfos: [
|
||||||
|
{
|
||||||
|
chatType: peer.chatType,
|
||||||
|
buddyChatInfo: BuddyData || [],
|
||||||
|
discussChatInfo: [],
|
||||||
|
groupChatInfo: GroupData || [],
|
||||||
|
dataLineChatInfo: [],
|
||||||
|
tmpChatInfo: [],
|
||||||
|
msgId: msgId,
|
||||||
|
msgSeq: msgSeq,
|
||||||
|
msgTime: Math.floor(Date.now() / 1000).toString(),
|
||||||
|
senderUid: senderUid,
|
||||||
|
senderNick: 'NapCat.Cached',
|
||||||
|
senderRemark: 'NapCat.Cached',
|
||||||
|
senderCard: 'NapCat.Cached',
|
||||||
|
elemId: elemId,
|
||||||
|
elemType: elemType,
|
||||||
|
fileSize: fileSize,
|
||||||
|
filePath: '',
|
||||||
|
fileName: fileName,
|
||||||
|
hits: [{
|
||||||
|
start: 12,
|
||||||
|
end: 14,
|
||||||
|
}],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async searchfile(keys: string[]) {
|
||||||
|
type EventType = NodeIKernelSearchService['searchFileWithKeywords'];
|
||||||
|
|
||||||
|
interface OnListener {
|
||||||
|
searchId: string,
|
||||||
|
hasMore: boolean,
|
||||||
|
resultItems: {
|
||||||
|
chatType: ChatType,
|
||||||
|
buddyChatInfo: any[],
|
||||||
|
discussChatInfo: any[],
|
||||||
|
groupChatInfo:
|
||||||
|
{
|
||||||
|
groupCode: string,
|
||||||
|
isConf: boolean,
|
||||||
|
hasModifyConfGroupFace: boolean,
|
||||||
|
hasModifyConfGroupName: boolean,
|
||||||
|
groupName: string,
|
||||||
|
remark: string
|
||||||
|
}[],
|
||||||
|
dataLineChatInfo: any[],
|
||||||
|
tmpChatInfo: any[],
|
||||||
|
msgId: string,
|
||||||
|
msgSeq: string,
|
||||||
|
msgTime: string,
|
||||||
|
senderUid: string,
|
||||||
|
senderNick: string,
|
||||||
|
senderRemark: string,
|
||||||
|
senderCard: string,
|
||||||
|
elemId: string,
|
||||||
|
elemType: number,
|
||||||
|
fileSize: string,
|
||||||
|
filePath: string,
|
||||||
|
fileName: string,
|
||||||
|
hits:
|
||||||
|
{
|
||||||
|
start: number,
|
||||||
|
end: number
|
||||||
|
}[]
|
||||||
|
}[]
|
||||||
|
}
|
||||||
|
|
||||||
|
const Event = this.core.eventWrapper.createEventFunction<EventType>('NodeIKernelSearchService/searchFileWithKeywords');
|
||||||
|
let id = '';
|
||||||
|
const Listener = this.core.eventWrapper.RegisterListen<(params: OnListener) => void>
|
||||||
|
(
|
||||||
|
'NodeIKernelSearchListener/onSearchFileKeywordsResult',
|
||||||
|
1,
|
||||||
|
20000,
|
||||||
|
(params) => id !== '' && params.searchId == id,
|
||||||
|
);
|
||||||
|
id = await Event!(keys, 12);
|
||||||
|
const [ret] = (await Listener);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
async getImageUrl(element: PicElement) {
|
||||||
|
if (!element) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
const url: string = element.originImageUrl!; // 没有域名
|
||||||
|
const md5HexStr = element.md5HexStr;
|
||||||
|
const fileMd5 = element.md5HexStr;
|
||||||
|
const fileUuid = element.fileUuid;
|
||||||
|
|
||||||
|
if (url) {
|
||||||
|
const UrlParse = new URL(IMAGE_HTTP_HOST + url);//临时解析拼接
|
||||||
|
const imageAppid = UrlParse.searchParams.get('appid');
|
||||||
|
const isNewPic = imageAppid && ['1406', '1407'].includes(imageAppid);
|
||||||
|
if (isNewPic) {
|
||||||
|
let UrlRkey = UrlParse.searchParams.get('rkey');
|
||||||
|
if (UrlRkey) {
|
||||||
|
return IMAGE_HTTP_HOST_NT + url;
|
||||||
|
}
|
||||||
|
const rkeyData = await this.rkeyManager.getRkey();
|
||||||
|
UrlRkey = imageAppid === '1406' ? rkeyData.private_rkey : rkeyData.group_rkey;
|
||||||
|
return IMAGE_HTTP_HOST_NT + url + `${UrlRkey}`;
|
||||||
|
} else {
|
||||||
|
// 老的图片url,不需要rkey
|
||||||
|
return IMAGE_HTTP_HOST + url;
|
||||||
|
}
|
||||||
|
} else if (fileMd5 || md5HexStr) {
|
||||||
|
// 没有url,需要自己拼接
|
||||||
|
return `${IMAGE_HTTP_HOST}/gchatpic_new/0/0-0-${(fileMd5 || md5HexStr)!.toUpperCase()}/0`;
|
||||||
|
}
|
||||||
|
this.context.logger.logDebug('图片url获取失败', element);
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class NTQQFileCacheApi {
|
||||||
|
context: InstanceContext;
|
||||||
|
core: NapCatCore;
|
||||||
|
|
||||||
|
constructor(context: InstanceContext, core: NapCatCore) {
|
||||||
|
this.context = context;
|
||||||
|
this.core = core;
|
||||||
|
}
|
||||||
|
|
||||||
|
async setCacheSilentScan(isSilent: boolean = true) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
getCacheSessionPathList() {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
clearCache(cacheKeys: Array<string> = ['tmp', 'hotUpdate']) {
|
||||||
|
// 参数未验证
|
||||||
|
return this.context.session.getStorageCleanService().clearCacheDataByKeys(cacheKeys);
|
||||||
|
}
|
||||||
|
|
||||||
|
addCacheScannedPaths(pathMap: object = {}) {
|
||||||
|
return this.context.session.getStorageCleanService().addCacheScanedPaths(pathMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
scanCache() {
|
||||||
|
//return (await this.context.session.getStorageCleanService().scanCache()).size;
|
||||||
|
}
|
||||||
|
|
||||||
|
getHotUpdateCachePath() {
|
||||||
|
// 未实现
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
getDesktopTmpPath() {
|
||||||
|
// 未实现
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
getChatCacheList(type: ChatType, pageSize: number = 1000, pageIndex: number = 0) {
|
||||||
|
return this.context.session.getStorageCleanService().getChatCacheInfo(type, pageSize, 1, pageIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
getFileCacheInfo(fileType: CacheFileType, pageSize: number = 1000, lastRecord?: CacheFileListItem) {
|
||||||
|
const _lastRecord = lastRecord ? lastRecord : { fileType: fileType };
|
||||||
|
//需要五个参数
|
||||||
|
//return napCatCore.session.getStorageCleanService().getFileCacheInfo();
|
||||||
|
}
|
||||||
|
|
||||||
|
async clearChatCache(chats: ChatCacheListItemBasic[] = [], fileKeys: string[] = []) {
|
||||||
|
return this.context.session.getStorageCleanService().clearChatCacheInfo(chats, fileKeys);
|
||||||
|
}
|
||||||
|
}
|
||||||
115
src/core/apis/friend.ts
Normal file
115
src/core/apis/friend.ts
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
import { Friend, FriendV2, User } from '@/core/entities';
|
||||||
|
import { BuddyListReqType, InstanceContext, NapCatCore, NodeIKernelProfileService, OnBuddyChangeParams } from '@/core';
|
||||||
|
import { LimitedHashTable } from '@/common/utils/MessageUnique';
|
||||||
|
|
||||||
|
export class NTQQFriendApi {
|
||||||
|
context: InstanceContext;
|
||||||
|
core: NapCatCore;
|
||||||
|
//friends: Map<string, Friend> = new Map<string, FriendV2>();
|
||||||
|
|
||||||
|
constructor(context: InstanceContext, core: NapCatCore) {
|
||||||
|
this.context = context;
|
||||||
|
this.core = core;
|
||||||
|
// if (!this.context.basicInfoWrapper.requireMinNTQQBuild('26702')) {
|
||||||
|
// this.getFriends(true);
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
async getBuddyV2(refresh = false): Promise<FriendV2[]> {
|
||||||
|
const uids: string[] = [];
|
||||||
|
const buddyService = this.context.session.getBuddyService();
|
||||||
|
const buddyListV2 = refresh ? await buddyService.getBuddyListV2('0', BuddyListReqType.KNOMAL) : await buddyService.getBuddyListV2('0', BuddyListReqType.KNOMAL);
|
||||||
|
uids.push(...buddyListV2.data.flatMap(item => item.buddyUids));
|
||||||
|
const data = await this.core.eventWrapper.callNoListenerEvent<NodeIKernelProfileService['getCoreAndBaseInfo']>(
|
||||||
|
'NodeIKernelProfileService/getCoreAndBaseInfo', 5000, 'nodeStore', uids,
|
||||||
|
);
|
||||||
|
return Array.from(data.values());
|
||||||
|
}
|
||||||
|
|
||||||
|
async getBuddyIdMapCache(refresh = false): Promise<LimitedHashTable<string, string>> {
|
||||||
|
return await this.getBuddyIdMap(refresh);
|
||||||
|
}
|
||||||
|
|
||||||
|
async getBuddyIdMap(refresh = false): Promise<LimitedHashTable<string, string>> {
|
||||||
|
const uids: string[] = [];
|
||||||
|
const retMap: LimitedHashTable<string, string> = new LimitedHashTable<string, string>(5000);
|
||||||
|
const buddyService = this.context.session.getBuddyService();
|
||||||
|
const buddyListV2 = refresh ? await buddyService.getBuddyListV2('0', BuddyListReqType.KNOMAL) : await buddyService.getBuddyListV2('0', BuddyListReqType.KNOMAL);
|
||||||
|
uids.push(...buddyListV2.data.flatMap(item => item.buddyUids));
|
||||||
|
const data = await this.core.eventWrapper.callNoListenerEvent<NodeIKernelProfileService['getCoreAndBaseInfo']>(
|
||||||
|
'NodeIKernelProfileService/getCoreAndBaseInfo', 5000, 'nodeStore', uids,
|
||||||
|
);
|
||||||
|
data.forEach((value, key) => {
|
||||||
|
retMap.set(value.uin!, value.uid!);
|
||||||
|
});
|
||||||
|
//console.log('getBuddyIdMap', retMap.getValue);
|
||||||
|
return retMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
async getBuddyV2ExWithCate(refresh = false) {
|
||||||
|
const uids: string[] = [];
|
||||||
|
const categoryMap: Map<string, any> = new Map();
|
||||||
|
const buddyService = this.context.session.getBuddyService();
|
||||||
|
const buddyListV2 = refresh ? (await buddyService.getBuddyListV2('0', BuddyListReqType.KNOMAL)).data : (await buddyService.getBuddyListV2('0', BuddyListReqType.KNOMAL)).data;
|
||||||
|
uids.push(
|
||||||
|
...buddyListV2.flatMap(item => {
|
||||||
|
item.buddyUids.forEach(uid => {
|
||||||
|
categoryMap.set(uid, { categoryId: item.categoryId, categoryName: item.categroyName });
|
||||||
|
});
|
||||||
|
return item.buddyUids;
|
||||||
|
}));
|
||||||
|
const data = await this.core.eventWrapper.callNoListenerEvent<NodeIKernelProfileService['getCoreAndBaseInfo']>(
|
||||||
|
'NodeIKernelProfileService/getCoreAndBaseInfo', 5000, 'nodeStore', uids,
|
||||||
|
);
|
||||||
|
return buddyListV2.map(category => ({
|
||||||
|
categoryId: category.categoryId,
|
||||||
|
categorySortId: category.categorySortId,
|
||||||
|
categoryName: category.categroyName,
|
||||||
|
categoryMbCount: category.categroyMbCount,
|
||||||
|
onlineCount: category.onlineCount,
|
||||||
|
buddyList: category.buddyUids.map(uid => data.get(uid)!).filter(value => value),
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
async isBuddy(uid: string) {
|
||||||
|
return this.context.session.getBuddyService().isBuddy(uid);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated
|
||||||
|
* @param forced
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
async getFriends(forced = false): Promise<User[]> {
|
||||||
|
const [_retData, _BuddyArg] = await this.core.eventWrapper.CallNormalEvent<(force: boolean) => Promise<any>, (arg: OnBuddyChangeParams) => void>
|
||||||
|
(
|
||||||
|
'NodeIKernelBuddyService/getBuddyList',
|
||||||
|
'NodeIKernelBuddyListener/onBuddyListChange',
|
||||||
|
1,
|
||||||
|
5000,
|
||||||
|
() => true,
|
||||||
|
forced,
|
||||||
|
);
|
||||||
|
const friends: User[] = [];
|
||||||
|
for (const categoryItem of _BuddyArg) {
|
||||||
|
for (const friend of categoryItem.buddyList) {
|
||||||
|
friends.push(friend);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return friends;
|
||||||
|
}
|
||||||
|
|
||||||
|
async handleFriendRequest(flag: string, accept: boolean) {
|
||||||
|
const data = flag.split('|');
|
||||||
|
if (data.length < 2) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const friendUid = data[0];
|
||||||
|
const reqTime = data[1];
|
||||||
|
this.context.session.getBuddyService()?.approvalFriendRequest({
|
||||||
|
friendUid: friendUid,
|
||||||
|
reqTime: reqTime,
|
||||||
|
accept,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
435
src/core/apis/group.ts
Normal file
435
src/core/apis/group.ts
Normal file
@@ -0,0 +1,435 @@
|
|||||||
|
import {
|
||||||
|
ChatType,
|
||||||
|
GeneralCallResult,
|
||||||
|
Group,
|
||||||
|
GroupMember,
|
||||||
|
GroupMemberRole,
|
||||||
|
GroupNotify,
|
||||||
|
GroupRequestOperateTypes,
|
||||||
|
InstanceContext,
|
||||||
|
MemberExtSourceType,
|
||||||
|
NapCatCore,
|
||||||
|
NodeIKernelGroupListener,
|
||||||
|
NodeIKernelGroupService,
|
||||||
|
} from '@/core';
|
||||||
|
import { isNumeric, runAllWithTimeout } from '@/common/utils/helper';
|
||||||
|
|
||||||
|
export class NTQQGroupApi {
|
||||||
|
context: InstanceContext;
|
||||||
|
core: NapCatCore;
|
||||||
|
groupCache: Map<string, Group> = new Map<string, Group>();
|
||||||
|
groupMemberCache: Map<string, Map<string, GroupMember>> = new Map<string, Map<string, GroupMember>>();
|
||||||
|
groups: Group[] = [];
|
||||||
|
constructor(context: InstanceContext, core: NapCatCore) {
|
||||||
|
this.context = context;
|
||||||
|
this.core = core;
|
||||||
|
this.initCache().then().catch(context.logger.logError);
|
||||||
|
}
|
||||||
|
async initCache() {
|
||||||
|
this.groups = await this.getGroups();
|
||||||
|
for (const group of this.groups) {
|
||||||
|
this.groupCache.set(group.groupCode, group);
|
||||||
|
const data = await this.getGroupMembers(group.groupCode, 3000);
|
||||||
|
this.groupMemberCache.set(group.groupCode, data);
|
||||||
|
}
|
||||||
|
this.context.logger.logDebug(`加载${this.groups.length}个群组缓存完成`);
|
||||||
|
}
|
||||||
|
async setGroupAvatar(gc: string, filePath: string) {
|
||||||
|
return this.context.session.getGroupService().setHeader(gc, filePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
async getGroups(forced = false) {
|
||||||
|
type ListenerType = NodeIKernelGroupListener['onGroupListUpdate'];
|
||||||
|
const [_retData, _updateType, groupList] = await this.core.eventWrapper.CallNormalEvent<(force: boolean) => Promise<any>, ListenerType>
|
||||||
|
(
|
||||||
|
'NodeIKernelGroupService/getGroupList',
|
||||||
|
'NodeIKernelGroupListener/onGroupListUpdate',
|
||||||
|
1,
|
||||||
|
5000,
|
||||||
|
() => true,
|
||||||
|
forced,
|
||||||
|
);
|
||||||
|
return groupList;
|
||||||
|
}
|
||||||
|
|
||||||
|
async getGroup(groupCode: string, forced = false) {
|
||||||
|
let group = this.groupCache.get(groupCode.toString());
|
||||||
|
if (!group) {
|
||||||
|
try {
|
||||||
|
const groupList = await this.getGroups(forced);
|
||||||
|
if (groupList.length) {
|
||||||
|
groupList.forEach(g => {
|
||||||
|
this.groupCache.set(g.groupCode, g);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
group = this.groupCache.get(groupCode.toString());
|
||||||
|
return group;
|
||||||
|
}
|
||||||
|
|
||||||
|
async getGroupMemberLatestSendTimeCache(GroupCode: string, uids: string[]) {
|
||||||
|
return this.getGroupMemberLatestSendTime(GroupCode, uids);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通过QQ自带数据库获取群成员最后发言时间(仅返回有效数据 且消耗延迟大 需要进行缓存)
|
||||||
|
* @param GroupCode 群号
|
||||||
|
* @param uids QQ号
|
||||||
|
* @returns Map<string, string> key: uin value: sendTime
|
||||||
|
* @example
|
||||||
|
* let ret = await NTQQGroupApi.getGroupMemberLastestSendTime('123456');
|
||||||
|
* for (let [uin, sendTime] of ret) {
|
||||||
|
* console.log(uin, sendTime);
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
async getGroupMemberLatestSendTime(GroupCode: string, uids: string[]) {
|
||||||
|
const getdata = async (uid: string) => {
|
||||||
|
const NTRet = await this.getLatestMsgByUids(GroupCode, [uid]);
|
||||||
|
if (NTRet.result != 0 && NTRet.msgList.length < 1) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
return { sendUin: NTRet.msgList[0].senderUin, sendTime: NTRet.msgList[0].msgTime };
|
||||||
|
};
|
||||||
|
const PromiseData: Promise<({
|
||||||
|
sendUin: string;
|
||||||
|
sendTime: string;
|
||||||
|
} | undefined)>[] = [];
|
||||||
|
const ret: Map<string, string> = new Map();
|
||||||
|
for (const uid of uids) {
|
||||||
|
PromiseData.push(getdata(uid).catch(() => undefined));
|
||||||
|
}
|
||||||
|
const allRet = await runAllWithTimeout(PromiseData, 2500);
|
||||||
|
for (const PromiseDo of allRet) {
|
||||||
|
if (PromiseDo) {
|
||||||
|
ret.set(PromiseDo.sendUin, PromiseDo.sendTime);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
async getLatestMsgByUids(GroupCode: string, uids: string[]) {
|
||||||
|
return await this.context.session.getMsgService().queryMsgsWithFilterEx('0', '0', '0', {
|
||||||
|
chatInfo: {
|
||||||
|
peerUid: GroupCode,
|
||||||
|
chatType: ChatType.group,
|
||||||
|
},
|
||||||
|
filterMsgType: [],
|
||||||
|
filterSendersUid: uids,
|
||||||
|
filterMsgToTime: '0',
|
||||||
|
filterMsgFromTime: '0',
|
||||||
|
isReverseOrder: false,
|
||||||
|
isIncludeCurrent: true,
|
||||||
|
pageLimit: 1,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async getGroupMemberAll(GroupCode: string, forced = false) {
|
||||||
|
return this.context.session.getGroupService().getAllMemberList(GroupCode, forced);
|
||||||
|
}
|
||||||
|
|
||||||
|
async getGroupMember(groupCode: string | number, memberUinOrUid: string | number) {
|
||||||
|
const groupCodeStr = groupCode.toString();
|
||||||
|
const memberUinOrUidStr = memberUinOrUid.toString();
|
||||||
|
let members = this.groupMemberCache.get(groupCodeStr);
|
||||||
|
if (!members) {
|
||||||
|
try {
|
||||||
|
members = await this.getGroupMembers(groupCodeStr);
|
||||||
|
// 更新群成员列表
|
||||||
|
this.groupMemberCache.set(groupCodeStr, members);
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// log('getGroupMember', members);
|
||||||
|
function getMember() {
|
||||||
|
let member: GroupMember | undefined;
|
||||||
|
if (isNumeric(memberUinOrUidStr)) {
|
||||||
|
member = Array.from(members!.values()).find(member => member.uin === memberUinOrUidStr);
|
||||||
|
} else {
|
||||||
|
member = members!.get(memberUinOrUidStr);
|
||||||
|
}
|
||||||
|
return member;
|
||||||
|
}
|
||||||
|
|
||||||
|
let member = getMember();
|
||||||
|
if (!member) {
|
||||||
|
members = await this.getGroupMembers(groupCodeStr);
|
||||||
|
member = getMember();
|
||||||
|
}
|
||||||
|
return member;
|
||||||
|
}
|
||||||
|
|
||||||
|
async getLatestMsg(GroupCode: string, uins: string[]) {
|
||||||
|
const uids: Array<string> = [];
|
||||||
|
for (const uin of uins) {
|
||||||
|
const uid = await this.core.apis.UserApi.getUidByUinV2(uin);
|
||||||
|
if (uid) {
|
||||||
|
uids.push(uid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return await this.context.session.getMsgService().queryMsgsWithFilterEx('0', '0', '0', {
|
||||||
|
chatInfo: {
|
||||||
|
peerUid: GroupCode,
|
||||||
|
chatType: ChatType.group,
|
||||||
|
},
|
||||||
|
filterMsgType: [],
|
||||||
|
filterSendersUid: uids,
|
||||||
|
filterMsgToTime: '0',
|
||||||
|
filterMsgFromTime: '0',
|
||||||
|
isReverseOrder: false,
|
||||||
|
isIncludeCurrent: true,
|
||||||
|
pageLimit: 1,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async getGroupRecommendContactArkJson(GroupCode: string) {
|
||||||
|
return this.context.session.getGroupService().getGroupRecommendContactArkJson(GroupCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
async CreatGroupFileFolder(groupCode: string, folderName: string) {
|
||||||
|
return this.context.session.getRichMediaService().createGroupFolder(groupCode, folderName);
|
||||||
|
}
|
||||||
|
|
||||||
|
async DelGroupFile(groupCode: string, files: string[]) {
|
||||||
|
return this.context.session.getRichMediaService().deleteGroupFile(groupCode, [102], files);
|
||||||
|
}
|
||||||
|
|
||||||
|
async DelGroupFileFolder(groupCode: string, folderId: string) {
|
||||||
|
return this.context.session.getRichMediaService().deleteGroupFolder(groupCode, folderId);
|
||||||
|
}
|
||||||
|
|
||||||
|
async addGroupEssence(GroupCode: string, msgId: string) {
|
||||||
|
// 代码没测过
|
||||||
|
// 需要 ob11msgid->msgId + (peer) -> msgSeq + msgRandom
|
||||||
|
const MsgData = await this.context.session.getMsgService().getMsgsIncludeSelf({
|
||||||
|
chatType: 2,
|
||||||
|
guildId: '',
|
||||||
|
peerUid: GroupCode,
|
||||||
|
}, msgId, 1, false);
|
||||||
|
const param = {
|
||||||
|
groupCode: GroupCode,
|
||||||
|
msgRandom: parseInt(MsgData.msgList[0].msgRandom),
|
||||||
|
msgSeq: parseInt(MsgData.msgList[0].msgSeq),
|
||||||
|
};
|
||||||
|
// GetMsgByShoretID(ShoretID); -> MsgService.getMsgs(Peer,MsgId,1,false); -> 组出参数
|
||||||
|
return this.context.session.getGroupService().addGroupEssence(param);
|
||||||
|
}
|
||||||
|
|
||||||
|
async removeGroupEssence(GroupCode: string, msgId: string) {
|
||||||
|
// 代码没测过
|
||||||
|
// 需要 ob11msgid->msgId + (peer) -> msgSeq + msgRandom
|
||||||
|
const MsgData = await this.context.session.getMsgService().getMsgsIncludeSelf({
|
||||||
|
chatType: 2,
|
||||||
|
guildId: '',
|
||||||
|
peerUid: GroupCode,
|
||||||
|
}, msgId, 1, false);
|
||||||
|
const param = {
|
||||||
|
groupCode: GroupCode,
|
||||||
|
msgRandom: parseInt(MsgData.msgList[0].msgRandom),
|
||||||
|
msgSeq: parseInt(MsgData.msgList[0].msgSeq),
|
||||||
|
};
|
||||||
|
// GetMsgByShoretID(ShoretID); -> MsgService.getMsgs(Peer,MsgId,1,false); -> 组出参数
|
||||||
|
return this.context.session.getGroupService().removeGroupEssence(param);
|
||||||
|
}
|
||||||
|
|
||||||
|
async getSingleScreenNotifies(num: number) {
|
||||||
|
const [_retData, _doubt, _seq, notifies] = await this.core.eventWrapper.CallNormalEvent<(arg1: boolean, arg2: string, arg3: number) => Promise<any>, (doubt: boolean, seq: string, notifies: GroupNotify[]) => void>
|
||||||
|
(
|
||||||
|
'NodeIKernelGroupService/getSingleScreenNotifies',
|
||||||
|
'NodeIKernelGroupListener/onGroupSingleScreenNotifies',
|
||||||
|
1,
|
||||||
|
5000,
|
||||||
|
() => true,
|
||||||
|
false,
|
||||||
|
'',
|
||||||
|
num,
|
||||||
|
);
|
||||||
|
return notifies;
|
||||||
|
}
|
||||||
|
|
||||||
|
async getGroupMemberV2(GroupCode: string, uid: string, forced = false) {
|
||||||
|
type ListenerType = NodeIKernelGroupListener['onMemberInfoChange'];
|
||||||
|
type EventType = NodeIKernelGroupService['getMemberInfo'];
|
||||||
|
// NTEventDispatch.CreatListenerFunction('NodeIKernelGroupListener/onGroupMemberInfoUpdate',
|
||||||
|
//return napCatCore.session.getGroupService().getMemberInfo(GroupCode, [uid], forced);
|
||||||
|
const [, , , _members] = await this.core.eventWrapper.CallNormalEvent<EventType, ListenerType>
|
||||||
|
(
|
||||||
|
'NodeIKernelGroupService/getMemberInfo',
|
||||||
|
'NodeIKernelGroupListener/onMemberInfoChange',
|
||||||
|
1,
|
||||||
|
5000,
|
||||||
|
(groupCode: string, changeType: number, members: Map<string, GroupMember>) => {
|
||||||
|
return groupCode == GroupCode && members.has(uid);
|
||||||
|
},
|
||||||
|
GroupCode, [uid], forced,
|
||||||
|
);
|
||||||
|
return _members.get(uid);
|
||||||
|
}
|
||||||
|
|
||||||
|
async getGroupMembers(groupQQ: string, num = 3000): Promise<Map<string, GroupMember>> {
|
||||||
|
const groupService = this.context.session.getGroupService();
|
||||||
|
const sceneId = groupService.createMemberListScene(groupQQ, 'groupMemberList_MainWindow');
|
||||||
|
const result = await groupService.getNextMemberList(sceneId!, undefined, num);
|
||||||
|
if (result.errCode !== 0) {
|
||||||
|
throw ('获取群成员列表出错,' + result.errMsg);
|
||||||
|
}
|
||||||
|
|
||||||
|
//logDebug(`获取群(${groupQQ})成员列表结果:`, `finish: ${result.result.finish}`); //, Array.from(result.result.infos.values()));
|
||||||
|
return result.result.infos;
|
||||||
|
/*
|
||||||
|
console.log(sceneId);
|
||||||
|
const result = await napCatCore.getGroupService().getNextMemberList(sceneId, num);
|
||||||
|
console.log(result);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
async getGroupNotifies() {
|
||||||
|
// 获取管理员变更
|
||||||
|
// 加群通知,退出通知,需要管理员权限
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
async GetGroupFileCount(Gids: Array<string>) {
|
||||||
|
return this.context.session.getRichMediaService().batchGetGroupFileCount(Gids);
|
||||||
|
}
|
||||||
|
|
||||||
|
async getGroupIgnoreNotifies() {
|
||||||
|
}
|
||||||
|
|
||||||
|
async getArkJsonGroupShare(GroupCode: string) {
|
||||||
|
const ret = await this.core.eventWrapper.callNoListenerEvent<(GroupId: string) => Promise<GeneralCallResult & {
|
||||||
|
arkJson: string
|
||||||
|
}>>(
|
||||||
|
'NodeIKernelGroupService/getGroupRecommendContactArkJson',
|
||||||
|
5000,
|
||||||
|
GroupCode,
|
||||||
|
);
|
||||||
|
return ret.arkJson;
|
||||||
|
}
|
||||||
|
|
||||||
|
//需要异常处理
|
||||||
|
async uploadGroupBulletinPic(GroupCode: string, imageurl: string) {
|
||||||
|
const _Pskey = (await this.core.apis.UserApi.getPSkey(['qun.qq.com'])).domainPskeyMap.get('qun.qq.com')!;
|
||||||
|
return this.context.session.getGroupService().uploadGroupBulletinPic(GroupCode, _Pskey, imageurl);
|
||||||
|
}
|
||||||
|
|
||||||
|
async handleGroupRequest(flag: string, operateType: GroupRequestOperateTypes, reason?: string) {
|
||||||
|
const flagitem = flag.split('|');
|
||||||
|
const groupCode = flagitem[0];
|
||||||
|
const seq = flagitem[1];
|
||||||
|
const type = parseInt(flagitem[2]);
|
||||||
|
|
||||||
|
return this.context.session.getGroupService().operateSysNotify(
|
||||||
|
false,
|
||||||
|
{
|
||||||
|
'operateType': operateType, // 2 拒绝
|
||||||
|
'targetMsg': {
|
||||||
|
'seq': seq, // 通知序列号
|
||||||
|
'type': type,
|
||||||
|
'groupCode': groupCode,
|
||||||
|
'postscript': reason || ' ', // 仅传空值可能导致处理失败,故默认给个空格
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async quitGroup(groupQQ: string) {
|
||||||
|
return this.context.session.getGroupService().quitGroup(groupQQ);
|
||||||
|
}
|
||||||
|
|
||||||
|
async kickMember(groupQQ: string, kickUids: string[], refuseForever: boolean = false, kickReason: string = '') {
|
||||||
|
return this.context.session.getGroupService().kickMember(groupQQ, kickUids, refuseForever, kickReason);
|
||||||
|
}
|
||||||
|
|
||||||
|
async banMember(groupQQ: string, memList: Array<{ uid: string, timeStamp: number }>) {
|
||||||
|
// timeStamp为秒数, 0为解除禁言
|
||||||
|
return this.context.session.getGroupService().setMemberShutUp(groupQQ, memList);
|
||||||
|
}
|
||||||
|
|
||||||
|
async banGroup(groupQQ: string, shutUp: boolean) {
|
||||||
|
return this.context.session.getGroupService().setGroupShutUp(groupQQ, shutUp);
|
||||||
|
}
|
||||||
|
|
||||||
|
async setMemberCard(groupQQ: string, memberUid: string, cardName: string) {
|
||||||
|
return this.context.session.getGroupService().modifyMemberCardName(groupQQ, memberUid, cardName);
|
||||||
|
}
|
||||||
|
|
||||||
|
async setMemberRole(groupQQ: string, memberUid: string, role: GroupMemberRole) {
|
||||||
|
return this.context.session.getGroupService().modifyMemberRole(groupQQ, memberUid, role);
|
||||||
|
}
|
||||||
|
|
||||||
|
async setGroupName(groupQQ: string, groupName: string) {
|
||||||
|
return this.context.session.getGroupService().modifyGroupName(groupQQ, groupName, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 头衔不可用
|
||||||
|
/*
|
||||||
|
async setGroupTitle(groupQQ: string, uid: string, title: string) {
|
||||||
|
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
async publishGroupBulletin(groupQQ: string, content: string, picInfo: {
|
||||||
|
id: string,
|
||||||
|
width: number,
|
||||||
|
height: number
|
||||||
|
} | undefined = undefined, pinned: number = 0, confirmRequired: number = 0) {
|
||||||
|
const _Pskey = (await this.core.apis.UserApi.getPSkey(['qun.qq.com'])).domainPskeyMap.get('qun.qq.com');
|
||||||
|
//text是content内容url编码
|
||||||
|
const data = {
|
||||||
|
text: encodeURI(content),
|
||||||
|
picInfo: picInfo,
|
||||||
|
oldFeedsId: '',
|
||||||
|
pinned: pinned,
|
||||||
|
confirmRequired: confirmRequired,
|
||||||
|
};
|
||||||
|
return this.context.session.getGroupService().publishGroupBulletin(groupQQ, _Pskey!, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
async getGroupRemainAtTimes(GroupCode: string) {
|
||||||
|
this.context.session.getGroupService().getGroupRemainAtTimes(GroupCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
async getMemberExtInfo(groupCode: string, uin: string) {
|
||||||
|
// 仅NTQQ 9.9.11 24568测试 容易炸开谨慎使用
|
||||||
|
return this.context.session.getGroupService().getMemberExtInfo(
|
||||||
|
{
|
||||||
|
groupCode: groupCode,
|
||||||
|
sourceType: MemberExtSourceType.TITLETYPE,
|
||||||
|
beginUin: '0',
|
||||||
|
dataTime: '0',
|
||||||
|
uinList: [uin],
|
||||||
|
uinNum: '',
|
||||||
|
seq: '',
|
||||||
|
groupType: '',
|
||||||
|
richCardNameVer: '',
|
||||||
|
memberExtFilter: {
|
||||||
|
memberLevelInfoUin: 1,
|
||||||
|
memberLevelInfoPoint: 1,
|
||||||
|
memberLevelInfoActiveDay: 1,
|
||||||
|
memberLevelInfoLevel: 1,
|
||||||
|
memberLevelInfoName: 1,
|
||||||
|
levelName: 1,
|
||||||
|
dataTime: 1,
|
||||||
|
userShowFlag: 1,
|
||||||
|
sysShowFlag: 1,
|
||||||
|
timeToUpdate: 1,
|
||||||
|
nickName: 1,
|
||||||
|
specialTitle: 1,
|
||||||
|
levelNameNew: 1,
|
||||||
|
userShowFlagNew: 1,
|
||||||
|
msgNeedField: 1,
|
||||||
|
cmdUinFlagExt3Grocery: 1,
|
||||||
|
memberIcon: 1,
|
||||||
|
memberInfoSeq: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
8
src/core/apis/index.ts
Normal file
8
src/core/apis/index.ts
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
export * from './file';
|
||||||
|
export * from './friend';
|
||||||
|
export * from './group';
|
||||||
|
export * from './msg';
|
||||||
|
export * from './user';
|
||||||
|
export * from './webapi';
|
||||||
|
export * from './sign';
|
||||||
|
export * from './system';
|
||||||
313
src/core/apis/msg.ts
Normal file
313
src/core/apis/msg.ts
Normal file
@@ -0,0 +1,313 @@
|
|||||||
|
import { ChatType, GetFileListParam, Peer, RawMessage, SendMessageElement } from '@/core/entities';
|
||||||
|
import { InstanceContext, NapCatCore } from '@/core';
|
||||||
|
import { onGroupFileInfoUpdateParamType } from '@/core/listeners';
|
||||||
|
import { GeneralCallResult } from '@/core/services/common';
|
||||||
|
|
||||||
|
export class NTQQMsgApi {
|
||||||
|
context: InstanceContext;
|
||||||
|
core: NapCatCore;
|
||||||
|
|
||||||
|
constructor(context: InstanceContext, core: NapCatCore) {
|
||||||
|
this.context = context;
|
||||||
|
this.core = core;
|
||||||
|
}
|
||||||
|
|
||||||
|
async FetchLongMsg(peer: Peer, msgId: string) {
|
||||||
|
return this.context.session.getMsgService().fetchLongMsg(peer, msgId);
|
||||||
|
}
|
||||||
|
|
||||||
|
async getMsgEmojiLikesList(peer: Peer, msgSeq: string, emojiId: string, emojiType: string, count: number = 20) {
|
||||||
|
//console.log(peer, msgSeq, emojiId, emojiType, count);
|
||||||
|
//注意此处emojiType 可选值一般为1-2 2好像是unicode表情dec值 大部分情况 Taged M likiowa
|
||||||
|
return this.context.session.getMsgService().getMsgEmojiLikesList(peer, msgSeq, emojiId, emojiType, '', false, 20);
|
||||||
|
}
|
||||||
|
|
||||||
|
// napCatCore: NapCatCore | null = null;
|
||||||
|
// enum BaseEmojiType {
|
||||||
|
// NORMAL_EMOJI,
|
||||||
|
// SUPER_EMOJI,
|
||||||
|
// RANDOM_SUPER_EMOJI,
|
||||||
|
// CHAIN_SUPER_EMOJI,
|
||||||
|
// EMOJI_EMOJI
|
||||||
|
// }
|
||||||
|
async setEmojiLike(peer: Peer, msgSeq: string, emojiId: string, set: boolean = true) {
|
||||||
|
// nt_qq//global//nt_data//Emoji//emoji-resource//sysface_res/apng/ 下可以看到所有QQ表情预览
|
||||||
|
// nt_qq\global\nt_data\Emoji\emoji-resource\face_config.json 里面有所有表情的id, 自带表情id是QSid, 标准emoji表情id是QCid
|
||||||
|
// 其实以官方文档为准是最好的,https://bot.q.qq.com/wiki/develop/api-v2/openapi/emoji/model.html#EmojiType
|
||||||
|
emojiId = emojiId.toString();
|
||||||
|
return this.context.session.getMsgService().setMsgEmojiLikes(peer, msgSeq, emojiId, emojiId.length > 3 ? '2' : '1', set);
|
||||||
|
}
|
||||||
|
|
||||||
|
async getMultiMsg(peer: Peer, rootMsgId: string, parentMsgId: string): Promise<GeneralCallResult & {
|
||||||
|
msgList: RawMessage[]
|
||||||
|
} | undefined> {
|
||||||
|
return this.context.session.getMsgService().getMultiMsg(peer, rootMsgId, parentMsgId);
|
||||||
|
}
|
||||||
|
|
||||||
|
async ForwardMsg(peer: Peer, msgIds: string[]) {
|
||||||
|
return this.context.session.getMsgService().forwardMsg(msgIds, peer, [peer], new Map());
|
||||||
|
}
|
||||||
|
|
||||||
|
async getLastestMsgByUids(peer: Peer, count: number = 20, isReverseOrder: boolean = false) {
|
||||||
|
const ret = await this.context.session.getMsgService().queryMsgsWithFilterEx('0', '0', '0', {
|
||||||
|
chatInfo: peer,
|
||||||
|
filterMsgType: [],
|
||||||
|
filterSendersUid: [],
|
||||||
|
filterMsgToTime: '0',
|
||||||
|
filterMsgFromTime: '0',
|
||||||
|
isReverseOrder: isReverseOrder,//此参数有点离谱 注意不是本次查询的排序 而是全部消历史信息的排序 默认false 从新消息拉取到旧消息
|
||||||
|
isIncludeCurrent: true,
|
||||||
|
pageLimit: count,
|
||||||
|
});
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
async getMsgsByMsgId(peer: Peer | undefined, msgIds: string[] | undefined) {
|
||||||
|
if (!peer) throw new Error('peer is not allowed');
|
||||||
|
if (!msgIds) throw new Error('msgIds is not allowed');
|
||||||
|
//Mlikiowa: 参数不合规会导致NC异常崩溃 原因是TX未对进入参数判断 对应Android标记@NotNull AndroidJADX分析可得
|
||||||
|
return await this.context.session.getMsgService().getMsgsByMsgId(peer, msgIds);
|
||||||
|
}
|
||||||
|
|
||||||
|
async getSingleMsg(peer: Peer, seq: string) {
|
||||||
|
return await this.context.session.getMsgService().getSingleMsg(peer, seq);
|
||||||
|
}
|
||||||
|
|
||||||
|
async fetchFavEmojiList(num: number) {
|
||||||
|
return this.context.session.getMsgService().fetchFavEmojiList('', num, true, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
async queryMsgsWithFilterExWithSeq(peer: Peer, msgSeq: string) {
|
||||||
|
const ret = await this.context.session.getMsgService().queryMsgsWithFilterEx('0', '0', msgSeq, {
|
||||||
|
chatInfo: peer,//此处为Peer 为关键查询参数 没有啥也没有 by mlik iowa
|
||||||
|
filterMsgType: [],
|
||||||
|
filterSendersUid: [],
|
||||||
|
filterMsgToTime: '0',
|
||||||
|
filterMsgFromTime: '0',
|
||||||
|
isReverseOrder: false,
|
||||||
|
isIncludeCurrent: true,
|
||||||
|
pageLimit: 1,
|
||||||
|
});
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
async getMsgsBySeqAndCount(peer: Peer, seq: string, count: number, desc: boolean, z: boolean) {
|
||||||
|
return await this.context.session.getMsgService().getMsgsBySeqAndCount(peer, seq, count, desc, z);
|
||||||
|
}
|
||||||
|
|
||||||
|
async setMsgRead(peer: Peer) {
|
||||||
|
return this.context.session.getMsgService().setMsgRead(peer);
|
||||||
|
}
|
||||||
|
|
||||||
|
async getGroupFileList(GroupCode: string, params: GetFileListParam) {
|
||||||
|
const data = await this.core.eventWrapper.CallNormalEvent<
|
||||||
|
(GroupCode: string, params: GetFileListParam) => Promise<unknown>,
|
||||||
|
(groupFileListResult: onGroupFileInfoUpdateParamType) => void
|
||||||
|
>(
|
||||||
|
'NodeIKernelRichMediaService/getGroupFileList',
|
||||||
|
'NodeIKernelMsgListener/onGroupFileInfoUpdate',
|
||||||
|
1,
|
||||||
|
5000,
|
||||||
|
(groupFileListResult: onGroupFileInfoUpdateParamType) => {
|
||||||
|
//Developer Mlikiowa Todo: 此处有问题 无法判断是否成功
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
GroupCode,
|
||||||
|
params,
|
||||||
|
);
|
||||||
|
return data[1].item;
|
||||||
|
}
|
||||||
|
|
||||||
|
async getMsgHistory(peer: Peer, msgId: string, count: number, isReverseOrder: boolean = false) {
|
||||||
|
// 消息时间从旧到新
|
||||||
|
return this.context.session.getMsgService().getMsgsIncludeSelf(peer, msgId, count, isReverseOrder);
|
||||||
|
}
|
||||||
|
|
||||||
|
async recallMsg(peer: Peer, msgIds: string[]) {
|
||||||
|
await this.context.session.getMsgService().recallMsg({
|
||||||
|
chatType: peer.chatType,
|
||||||
|
peerUid: peer.peerUid,
|
||||||
|
}, msgIds);
|
||||||
|
}
|
||||||
|
|
||||||
|
async sendMsgV2(peer: Peer, msgElements: SendMessageElement[], waitComplete = true, timeout = 10000) {
|
||||||
|
function generateMsgId() {
|
||||||
|
const timestamp = Math.floor(Date.now() / 1000);
|
||||||
|
const random = Math.floor(Math.random() * Math.pow(2, 32));
|
||||||
|
const buffer = Buffer.alloc(8);
|
||||||
|
buffer.writeUInt32BE(timestamp, 0);
|
||||||
|
buffer.writeUInt32BE(random, 4);
|
||||||
|
const msgId = BigInt('0x' + buffer.toString('hex')).toString();
|
||||||
|
return msgId;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 此处有采用Hack方法 利用数据返回正确得到对应消息
|
||||||
|
// 与之前 Peer队列 MsgSeq队列 真正的MsgId并发不同
|
||||||
|
// 谨慎采用 目前测试暂无问题 Developer.Mlikiowa
|
||||||
|
let msgId: string;
|
||||||
|
try {
|
||||||
|
msgId = await this.getMsgUnique(peer.chatType, await this.getServerTime());
|
||||||
|
} catch (error) {
|
||||||
|
//if (!napCatCore.session.getMsgService()['generateMsgUniqueId'])
|
||||||
|
//兜底识别策略V2
|
||||||
|
msgId = generateMsgId().toString();
|
||||||
|
}
|
||||||
|
const data = await this.core.eventWrapper.CallNormalEvent<
|
||||||
|
(msgId: string, peer: Peer, msgElements: SendMessageElement[], map: Map<any, any>) => Promise<unknown>,
|
||||||
|
(msgList: RawMessage[]) => void
|
||||||
|
>(
|
||||||
|
'NodeIKernelMsgService/sendMsg',
|
||||||
|
'NodeIKernelMsgListener/onMsgInfoListUpdate',
|
||||||
|
1,
|
||||||
|
timeout,
|
||||||
|
(msgRecords: RawMessage[]) => {
|
||||||
|
for (const msgRecord of msgRecords) {
|
||||||
|
if (msgRecord.msgId === msgId && msgRecord.sendStatus === 2) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
msgId,
|
||||||
|
peer,
|
||||||
|
msgElements,
|
||||||
|
new Map(),
|
||||||
|
);
|
||||||
|
const retMsg = data[1].find(msgRecord => {
|
||||||
|
if (msgRecord.msgId === msgId) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return retMsg;
|
||||||
|
}
|
||||||
|
|
||||||
|
sendMsgEx(peer: Peer, msgElements: SendMessageElement[], waitComplete = true, timeout = 10000) {
|
||||||
|
//return NTQQMsgApi.sendMsgV1(peer, msgElements, waitComplete, timeout);
|
||||||
|
}
|
||||||
|
async PrepareTempChat(toUserUid: string, GroupCode: string, nickname: string) {
|
||||||
|
//By Jadx/Ida Mlikiowa
|
||||||
|
let TempGameSession = {
|
||||||
|
nickname: "",
|
||||||
|
gameAppId: "",
|
||||||
|
selfTinyId: "",
|
||||||
|
peerRoleId: "",
|
||||||
|
peerOpenId: "",
|
||||||
|
};
|
||||||
|
return this.context.session.getMsgService().prepareTempChat({
|
||||||
|
chatType: ChatType.temp,
|
||||||
|
peerUid: toUserUid,
|
||||||
|
peerNickname: nickname,
|
||||||
|
fromGroupCode: GroupCode,
|
||||||
|
sig: "",
|
||||||
|
selfPhone: "",
|
||||||
|
selfUid: this.core.selfInfo.uid,
|
||||||
|
gameSession: TempGameSession
|
||||||
|
});
|
||||||
|
}
|
||||||
|
async getTempChatInfo(chatType: ChatType, peerUid: string) {
|
||||||
|
return this.context.session.getMsgService().getTempChatInfo(chatType, peerUid);
|
||||||
|
}
|
||||||
|
async sendMsg(peer: Peer, msgElements: SendMessageElement[], waitComplete = true, timeout = 10000) {
|
||||||
|
//唉? !我有个想法
|
||||||
|
if (peer.chatType === ChatType.temp && peer.guildId && peer.guildId !== '') {
|
||||||
|
let member = await this.core.apis.GroupApi.getGroupMember(peer.guildId, peer.peerUid!);
|
||||||
|
if (member) {
|
||||||
|
await this.PrepareTempChat(peer.peerUid, peer.guildId, member.nick);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const msgId = await this.getMsgUnique(peer.chatType, await this.getServerTime());
|
||||||
|
peer.guildId = msgId;
|
||||||
|
const data = await this.core.eventWrapper.CallNormalEvent<
|
||||||
|
(msgId: string, peer: Peer, msgElements: SendMessageElement[], map: Map<any, any>) => Promise<unknown>,
|
||||||
|
(msgList: RawMessage[]) => void
|
||||||
|
>(
|
||||||
|
'NodeIKernelMsgService/sendMsg',
|
||||||
|
'NodeIKernelMsgListener/onMsgInfoListUpdate',
|
||||||
|
1,
|
||||||
|
timeout,
|
||||||
|
(msgRecords: RawMessage[]) => {
|
||||||
|
for (const msgRecord of msgRecords) {
|
||||||
|
if (msgRecord.guildId === msgId && msgRecord.sendStatus === 2) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
'0',
|
||||||
|
peer,
|
||||||
|
msgElements,
|
||||||
|
new Map(),
|
||||||
|
);
|
||||||
|
const retMsg = data[1].find(msgRecord => {
|
||||||
|
if (msgRecord.guildId === msgId) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return retMsg;
|
||||||
|
}
|
||||||
|
|
||||||
|
async getMsgUnique(chatType: number, time: string) {
|
||||||
|
if (this.context.basicInfoWrapper.requireMinNTQQBuild('26702')) {
|
||||||
|
return this.context.session.getMsgService().generateMsgUniqueId(chatType, time);
|
||||||
|
}
|
||||||
|
return this.context.session.getMsgService().getMsgUniqueId(time);
|
||||||
|
}
|
||||||
|
|
||||||
|
async getServerTime() {
|
||||||
|
return this.context.session.getMSFService().getServerTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
async getServerTimeV2() {
|
||||||
|
return this.core.eventWrapper.callNoListenerEvent<() => string>('NodeIKernelMsgService/getServerTime', 5000);
|
||||||
|
}
|
||||||
|
|
||||||
|
async forwardMsg(srcPeer: Peer, destPeer: Peer, msgIds: string[]) {
|
||||||
|
return this.context.session.getMsgService().forwardMsg(msgIds, srcPeer, [destPeer], new Map());
|
||||||
|
}
|
||||||
|
|
||||||
|
async multiForwardMsg(srcPeer: Peer, destPeer: Peer, msgIds: string[]): Promise<RawMessage> {
|
||||||
|
const msgInfos = msgIds.map(id => {
|
||||||
|
return { msgId: id, senderShowName: this.core.selfInfo.nick };
|
||||||
|
});
|
||||||
|
const data = await this.core.eventWrapper.CallNormalEvent<
|
||||||
|
(msgInfo: typeof msgInfos, srcPeer: Peer, destPeer: Peer, comment: Array<any>, attr: Map<any, any>) => Promise<unknown>,
|
||||||
|
(msgList: RawMessage[]) => void
|
||||||
|
>(
|
||||||
|
'NodeIKernelMsgService/multiForwardMsgWithComment',
|
||||||
|
'NodeIKernelMsgListener/onMsgInfoListUpdate',
|
||||||
|
1,
|
||||||
|
5000,
|
||||||
|
(msgRecords: RawMessage[]) => {
|
||||||
|
for (const msgRecord of msgRecords) {
|
||||||
|
if (msgRecord.peerUid == destPeer.peerUid && msgRecord.senderUid == this.core.selfInfo.uid) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
msgInfos,
|
||||||
|
srcPeer,
|
||||||
|
destPeer,
|
||||||
|
[],
|
||||||
|
new Map(),
|
||||||
|
);
|
||||||
|
for (const msg of data[1]) {
|
||||||
|
const arkElement = msg.elements.find(ele => ele.arkElement);
|
||||||
|
if (!arkElement) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const forwardData: any = JSON.parse(arkElement.arkElement.bytesData);
|
||||||
|
if (forwardData.app != 'com.tencent.multimsg') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (msg.peerUid == destPeer.peerUid && msg.senderUid == this.core.selfInfo.uid) {
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new Error('转发消息超时');
|
||||||
|
}
|
||||||
|
|
||||||
|
async markallMsgAsRead() {
|
||||||
|
return this.context.session.getMsgService().setAllC2CAndGroupMsgRead();
|
||||||
|
}
|
||||||
|
}
|
||||||
312
src/core/apis/sign.ts
Normal file
312
src/core/apis/sign.ts
Normal file
@@ -0,0 +1,312 @@
|
|||||||
|
import { RequestUtil } from '@/common/utils/request';
|
||||||
|
import { MiniAppLuaJsonType } from '@/core';
|
||||||
|
import { InstanceContext, NapCatCore } from '..';
|
||||||
|
|
||||||
|
// let t = await napCatCore.session.getGroupService().shareDigest({
|
||||||
|
// appId: "100497308",
|
||||||
|
// appType: 1,
|
||||||
|
// msgStyle: 0,
|
||||||
|
// recvUin: "726067488",
|
||||||
|
// sendType: 1,
|
||||||
|
// clientInfo: {
|
||||||
|
// platform: 1
|
||||||
|
// },
|
||||||
|
// richMsg: {
|
||||||
|
// usingArk: true,
|
||||||
|
// title: "Bot测试title",
|
||||||
|
// summary: "Bot测试summary",
|
||||||
|
// url: "https://www.bilibili.com",
|
||||||
|
// pictureUrl: "https://y.qq.com/music/photo_new/T002R300x300M0000035DC6W4ZpSqf_1.jpg?max_age=2592000",
|
||||||
|
// brief: "Bot测试brief",
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
// {
|
||||||
|
// errCode: 0,
|
||||||
|
// errMsg: '',
|
||||||
|
// rsp: {
|
||||||
|
// sendType: 1,
|
||||||
|
// recvUin: '726067488',
|
||||||
|
// recvOpenId: '',
|
||||||
|
// errCode: 901501,
|
||||||
|
// errMsg: 'imagent service_error:150_OIDB_NO_PRIV',
|
||||||
|
// extInfo: {
|
||||||
|
// wording: '消息下发失败(错误码:901501)',
|
||||||
|
// jumpResult: 0,
|
||||||
|
// jumpUrl: '',
|
||||||
|
// level: 0,
|
||||||
|
// subLevel: 0,
|
||||||
|
// developMsg: 'imagent error'
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// export class MusicSign {
|
||||||
|
// private readonly url: string;
|
||||||
|
|
||||||
|
// constructor(url: string) {
|
||||||
|
// this.url = url;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// sign(postData: CustomMusicSignPostData | IdMusicSignPostData): Promise<any> {
|
||||||
|
// return new Promise((resolve, reject) => {
|
||||||
|
// fetch(this.url, {
|
||||||
|
// method: 'POST', // 指定请求方法为 POST
|
||||||
|
// headers: {
|
||||||
|
// 'Content-Type': 'application/json' // 设置请求头,指明发送的数据类型为 JSON
|
||||||
|
// },
|
||||||
|
// body: JSON.stringify(postData) // 将 JavaScript 对象转换为 JSON 字符串作为请求体
|
||||||
|
// })
|
||||||
|
// .then(response => {
|
||||||
|
// if (!response.ok) {
|
||||||
|
// reject(response.statusText); // 请求失败,返回错误信息
|
||||||
|
// }
|
||||||
|
// return response.json(); // 解析 JSON 格式的响应体
|
||||||
|
// })
|
||||||
|
// .then(data => {
|
||||||
|
// logDebug('音乐消息生成成功', data);
|
||||||
|
// resolve(data);
|
||||||
|
// })
|
||||||
|
// .catch(error => {
|
||||||
|
// reject(error);
|
||||||
|
// });
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
export class NTQQMusicSignApi {
|
||||||
|
context: InstanceContext;
|
||||||
|
core: NapCatCore;
|
||||||
|
|
||||||
|
constructor(context: InstanceContext, core: NapCatCore) {
|
||||||
|
this.context = context;
|
||||||
|
this.core = core;
|
||||||
|
}
|
||||||
|
|
||||||
|
async signMiniApp(CardData: MiniAppLuaJsonType) {
|
||||||
|
// {
|
||||||
|
// "app": "com.tencent.miniapp.lua",
|
||||||
|
// "bizsrc": "tianxuan.imgJumpArk",
|
||||||
|
// "view": "miniapp",
|
||||||
|
// "prompt": "hi! 这里有我的日常故事,只想讲给你听",
|
||||||
|
// "config": {
|
||||||
|
// "type": "normal",
|
||||||
|
// "forward": 1,
|
||||||
|
// "autosize": 0
|
||||||
|
// },
|
||||||
|
// "meta": {
|
||||||
|
// "miniapp": {
|
||||||
|
// "title": "hi! 这里有我的日常故事,只想讲给你听",
|
||||||
|
// "preview": "https:\/\/tianquan.gtimg.cn\/qqAIAgent\/item\/7\/square.png",
|
||||||
|
// "jumpUrl": "https:\/\/club.vip.qq.com\/transfer?open_kuikly_info=%7B%22version%22%3A%20%221%22%2C%22src_type%22%3A%20%22web%22%2C%22kr_turbo_display%22%3A%20%221%22%2C%22page_name%22%3A%20%22vas_ai_persona_moments%22%2C%22bundle_name%22%3A%20%22vas_ai_persona_moments%22%7D&page_name=vas_ai_persona_moments&enteranceId=share&robot_uin=3889008584",
|
||||||
|
// "tag": "QQ智能体",
|
||||||
|
// "tagIcon": "https:\/\/tianquan.gtimg.cn\/shoal\/qqAIAgent\/3e9d70c9-d98c-45b8-80b4-79d82971b514.png",
|
||||||
|
// "source": "QQ智能体",
|
||||||
|
// "sourcelogo": "https:\/\/tianquan.gtimg.cn\/shoal\/qqAIAgent\/3e9d70c9-d98c-45b8-80b4-79d82971b514.png"
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// token : function(url,skey){
|
||||||
|
// var str = skey || cookie('skey') || cookie('rv2') || '',
|
||||||
|
// hash = 5381;
|
||||||
|
// if(url){
|
||||||
|
// var hostname = uri(url).hostname;
|
||||||
|
// if(hostname.indexOf('qun.qq.com') > -1 || (hostname.indexOf('qzone.qq.com') > -1 && hostname.indexOf('qun.qzone.qq.com') === -1)){
|
||||||
|
// str = cookie('p_skey') || str;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// for(var i = 0, len = str.length; i < len; ++i){
|
||||||
|
// hash += (hash << 5) + str.charAt(i).charCodeAt();
|
||||||
|
// }
|
||||||
|
// return hash & 0x7fffffff;
|
||||||
|
// },
|
||||||
|
//
|
||||||
|
|
||||||
|
// function signToken(skey: string) {
|
||||||
|
// let hash = 5381;
|
||||||
|
// for (let i = 0, len = skey.length; i < len; ++i) {
|
||||||
|
// hash += (hash << 5) + skey.charCodeAt(i);
|
||||||
|
// }
|
||||||
|
// return hash & 0x7fffffff;
|
||||||
|
// }
|
||||||
|
const signCard = {
|
||||||
|
'app': 'com.tencent.miniapp.lua',
|
||||||
|
'bizsrc': 'tianxuan.imgJumpArk',
|
||||||
|
'view': 'miniapp',
|
||||||
|
'prompt': CardData.prompt,
|
||||||
|
'config': {
|
||||||
|
'type': 'normal',
|
||||||
|
'forward': 1,
|
||||||
|
'autosize': 0,
|
||||||
|
},
|
||||||
|
'meta': {
|
||||||
|
'miniapp': {
|
||||||
|
'title': CardData.title,
|
||||||
|
'preview': (CardData.preview as string).replace(/\\/g, '\\/\\/'),
|
||||||
|
'jumpUrl': (CardData.jumpUrl as string).replace(/\\/g, '\\/\\/'),
|
||||||
|
'tag': CardData.tag,
|
||||||
|
'tagIcon': (CardData.tagIcon as string).replace(/\\/g, '\\/\\/'),
|
||||||
|
'source': CardData.source,
|
||||||
|
'sourcelogo': (CardData.sourcelogo as string).replace(/\\/g, '\\/\\/'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
// let signCard = {
|
||||||
|
// "app": "com.tencent.eventshare.lua",
|
||||||
|
// "prompt": "Bot Test",
|
||||||
|
// "bizsrc": "tianxuan.business",
|
||||||
|
// "meta": {
|
||||||
|
// "eventshare": {
|
||||||
|
// "button1URL": "https://www.bilibili.com",
|
||||||
|
// "button1disable": false,
|
||||||
|
// "button1title": "点我前往",
|
||||||
|
// "button2URL": "",
|
||||||
|
// "button2disable": false,
|
||||||
|
// "button2title": "",
|
||||||
|
// "buttonNum": 1,
|
||||||
|
// "jumpURL": "https://www.bilibili.com",
|
||||||
|
// "preview": "https://tianquan.gtimg.cn/shoal/card/9930bc4e-4a92-4da3-814f-8094a2421d9c.png",
|
||||||
|
// "tag": "QQ集卡",
|
||||||
|
// "tagIcon": "https://tianquan.gtimg.cn/shoal/card/c034854b-102d-40be-a545-5ca90a7c49c9.png",
|
||||||
|
// "title": "Bot Test"
|
||||||
|
// }
|
||||||
|
// },
|
||||||
|
// "config": {
|
||||||
|
// "autosize": 0,
|
||||||
|
// "collect": 0,
|
||||||
|
// "ctime": 1716568575,
|
||||||
|
// "forward": 1,
|
||||||
|
// "height": 336,
|
||||||
|
// "reply": 0,
|
||||||
|
// "round": 1,
|
||||||
|
// "type": "normal",
|
||||||
|
// "width": 263
|
||||||
|
// },
|
||||||
|
// "view": "eventshare",
|
||||||
|
// "ver": "0.0.0.1"
|
||||||
|
// };
|
||||||
|
const data = (await this.core.apis.UserApi.getQzoneCookies());
|
||||||
|
const Bkn = this.core.apis.WebApi.getBknFromCookie(data.p_skey);
|
||||||
|
|
||||||
|
const CookieValue = 'p_skey=' + data.p_skey + '; skey=' + data.skey + '; p_uin=o' + this.core.selfInfo.uin + '; uin=o' + this.core.selfInfo.uin;
|
||||||
|
|
||||||
|
const signurl = 'https://h5.qzone.qq.com/v2/vip/tx/trpc/ark-share/GenNewSignedArk?g_tk=' + Bkn + '&ark=' + encodeURIComponent(JSON.stringify(signCard));
|
||||||
|
let signed_ark = '';
|
||||||
|
try {
|
||||||
|
const retData = await RequestUtil.HttpGetJson<{
|
||||||
|
code: number,
|
||||||
|
data: { signed_ark: string }
|
||||||
|
}>(signurl, 'GET', undefined, { Cookie: CookieValue });
|
||||||
|
//logDebug('MiniApp JSON 消息生成成功', retData);
|
||||||
|
signed_ark = retData.data.signed_ark;
|
||||||
|
} catch (error) {
|
||||||
|
this.context.logger.logDebug('MiniApp JSON 消息生成失败', error);
|
||||||
|
}
|
||||||
|
return signed_ark;
|
||||||
|
}
|
||||||
|
|
||||||
|
async signInternal(songname: string, singer: string, cover: string, songmid: string, songmusic: string) {
|
||||||
|
//curl -X POST 'https://mqq.reader.qq.com/api/mqq/share/card?accessToken&_csrfToken&source=c0003' -H 'Content-Type: application/json' -H 'Cookie: uin=o10086' -d '{"app":"com.tencent.qqreader.share","config":{"ctime":1718634110,"forward":1,"token":"9a63343c32d5a16bcde653eb97faa25d","type":"normal"},"extra":{"app_type":1,"appid":100497308,"msg_seq":14386738075403815000.0,"uin":1733139081},"meta":{"music":{"action":"","android_pkg_name":"","app_type":1,"appid":100497308,"ctime":1718634110,"desc":"周杰伦","jumpUrl":"https://i.y.qq.com/v8/playsong.html?songmid=0039MnYb0qxYhV&type=0","musicUrl":"http://ws.stream.qqmusic.qq.com/http://isure6.stream.qqmusic.qq.com/M800002202B43Cq4V4.mp3?fromtag=810033622&guid=br_xzg&trace=23fe7bcbe2336bbf&uin=553&vkey=CF0F5CE8B0FA16F3001F8A88D877A217EB5E4F00BDCEF1021EB6C48969CA33C6303987AEECE9CC840122DD2F917A59D6130D8A8CA4577C87","preview":"https://y.qq.com/music/photo_new/T002R800x800M000000MkMni19ClKG.jpg","cover":"https://y.qq.com/music/photo_new/T002R800x800M000000MkMni19ClKG.jpg","sourceMsgId":"0","source_icon":"https://p.qpic.cn/qqconnect/0/app_100497308_1626060999/100?max-age=2592000&t=0","source_url":"","tag":"QQ音乐","title":"晴天","uin":10086}},"prompt":"[分享]晴天","ver":"0.0.0.1","view":"music"}'
|
||||||
|
const signurl = 'https://mqq.reader.qq.com/api/mqq/share/card?accessToken&_csrfToken&source=c0003';
|
||||||
|
//let = "https://y.qq.com/music/photo_new/T002R800x800M000000MkMni19ClKG.jpg";
|
||||||
|
const signCard = {
|
||||||
|
app: 'com.tencent.qqreader.share',
|
||||||
|
config: {
|
||||||
|
ctime: 1718634110,
|
||||||
|
forward: 1,
|
||||||
|
token: '9a63343c32d5a16bcde653eb97faa25d',
|
||||||
|
type: 'normal',
|
||||||
|
},
|
||||||
|
extra: {
|
||||||
|
app_type: 1,
|
||||||
|
appid: 100497308,
|
||||||
|
msg_seq: 14386738075403815000,
|
||||||
|
uin: 1733139081,
|
||||||
|
},
|
||||||
|
meta: {
|
||||||
|
music: {
|
||||||
|
action: '',
|
||||||
|
android_pkg_name: '',
|
||||||
|
app_type: 1,
|
||||||
|
appid: 100497308,
|
||||||
|
ctime: 1718634110,
|
||||||
|
desc: singer,
|
||||||
|
jumpUrl: 'https://i.y.qq.com/v8/playsong.html?songmid=' + songmid + '&type=0',
|
||||||
|
musicUrl: songmusic,
|
||||||
|
preview: cover,
|
||||||
|
cover: cover,
|
||||||
|
sourceMsgId: '0',
|
||||||
|
source_icon: 'https://p.qpic.cn/qqconnect/0/app_100497308_1626060999/100?max-age=2592000&t=0',
|
||||||
|
source_url: '',
|
||||||
|
tag: 'QQ音乐',
|
||||||
|
title: songname,
|
||||||
|
uin: 10086,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
prompt: '[分享]' + songname,
|
||||||
|
ver: '0.0.0.1',
|
||||||
|
view: 'music',
|
||||||
|
};
|
||||||
|
//console.log(JSON.stringify(signCard, null, 2));
|
||||||
|
const data = await RequestUtil.HttpGetJson<{ code: number, data: { arkResult: string } }>
|
||||||
|
(signurl, 'POST', signCard, { 'Cookie': 'uin=o10086', 'Content-Type': 'application/json' });
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
//注意处理错误
|
||||||
|
async signWay03(id: string = '', mid: string = '') {
|
||||||
|
let signedMid;
|
||||||
|
if (mid == '') {
|
||||||
|
const MusicInfo = await RequestUtil.HttpGetJson<{
|
||||||
|
songinfo?: {
|
||||||
|
data?: {
|
||||||
|
track_info: {
|
||||||
|
mid: string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}>(
|
||||||
|
'https://u.y.qq.com/cgi-bin/musicu.fcg?format=json&inCharset=utf8&outCharset=utf-8¬ice=0&platform=yqq.json&needNewCode=0&data={"comm":{"ct":24,"cv":0},"songinfo":{"method":"get_song_detail_yqq","param":{"song_type":0,"song_mid":"","song_id":' + id + '},"module":"music.pf_song_detail_svr"}}',
|
||||||
|
'GET',
|
||||||
|
undefined,
|
||||||
|
);
|
||||||
|
signedMid = MusicInfo.songinfo?.data?.track_info.mid;
|
||||||
|
}
|
||||||
|
//第三方接口 存在速率限制 现在勉强用
|
||||||
|
const MusicReal = await RequestUtil.HttpGetJson<{
|
||||||
|
code: number,
|
||||||
|
data?: {
|
||||||
|
name: string,
|
||||||
|
singer: string,
|
||||||
|
url: string,
|
||||||
|
cover: string
|
||||||
|
}
|
||||||
|
}>('https://api.leafone.cn/api/qqmusic?id=' + signedMid + '&type=8', 'GET');
|
||||||
|
//console.log(MusicReal);
|
||||||
|
return { ...MusicReal.data, mid: signedMid };
|
||||||
|
}
|
||||||
|
|
||||||
|
async CreateMusicThirdWay1(id: string = '', mid: string = '') {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//转换外域名为 https://qq.ugcimg.cn/v1/cpqcbu4b8870i61bde6k7cbmjgejq8mr3in82qir4qi7ielffv5slv8ck8g42novtmev26i233ujtuab6tvu2l2sjgtupfr389191v00s1j5oh5325j5eqi40774jv1i/khovifoh7jrqd6eahoiv7koh8o
|
||||||
|
//https://cgi.connect.qq.com/qqconnectopen/openapi/change_image_url?url=https://th.bing.com/th?id=OSK.b8ed36f1fb1889de6dc84fd81c187773&w=46&h=46&c=11&rs=1&qlt=80&o=6&dpr=2&pid=SANGAM
|
||||||
|
|
||||||
|
//外域名不行得走qgroup中转
|
||||||
|
//https://proxy.gtimg.cn/tx_tls_gate=y.qq.com/music/photo_new/T002R800x800M000000y5gq7449K9I.jpg
|
||||||
|
|
||||||
|
//可外域名
|
||||||
|
//https://pic.ugcimg.cn/500955bdd6657ecc8e82e02d2df06800/jpg1
|
||||||
|
|
||||||
|
//QQ音乐gtimg接口
|
||||||
|
//https://y.gtimg.cn/music/photo_new/T002R800x800M000000y5gq7449K9I.jpg?max_age=2592000
|
||||||
|
|
||||||
|
//还有一处公告上传可以上传高质量图片 持久为qq域名
|
||||||
|
async SignMusicWrapper(id: string = '') {
|
||||||
|
const MusicInfo = await this.signWay03(id)!;
|
||||||
|
return await this.signInternal(MusicInfo.name!, MusicInfo.singer!, MusicInfo.cover!, MusicInfo.mid!, 'https://ws.stream.qqmusic.qq.com/' + MusicInfo.url!);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
47
src/core/apis/system.ts
Normal file
47
src/core/apis/system.ts
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
import { GeneralCallResult, InstanceContext, NapCatCore } from '@/core';
|
||||||
|
|
||||||
|
export class NTQQSystemApi {
|
||||||
|
context: InstanceContext;
|
||||||
|
core: NapCatCore;
|
||||||
|
|
||||||
|
constructor(context: InstanceContext, core: NapCatCore) {
|
||||||
|
this.context = context;
|
||||||
|
this.core = core;
|
||||||
|
}
|
||||||
|
|
||||||
|
async hasOtherRunningQQProcess() {
|
||||||
|
return this.core.util.hasOtherRunningQQProcess();
|
||||||
|
}
|
||||||
|
|
||||||
|
async ORCImage(filePath: string) {
|
||||||
|
return this.context.session.getNodeMiscService().wantWinScreenOCR(filePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
async translateEnWordToZn(words: string[]) {
|
||||||
|
return this.context.session.getRichMediaService().translateEnWordToZn(words);
|
||||||
|
}
|
||||||
|
|
||||||
|
//调用会超时 没灯用 (好像是通知listener的) onLineDev
|
||||||
|
async getOnlineDev() {
|
||||||
|
return this.context.session.getMsgService().getOnLineDev();
|
||||||
|
}
|
||||||
|
|
||||||
|
//1-2-162b9b42-65b9-4405-a8ed-2e256ec8aa50
|
||||||
|
async getArkJsonCollection(cid: string) {
|
||||||
|
const ret = await this.core.eventWrapper.callNoListenerEvent<(cid: string) => Promise<GeneralCallResult & {
|
||||||
|
arkJson: string
|
||||||
|
}>>(
|
||||||
|
'NodeIKernelCollectionService/collectionArkShare',
|
||||||
|
5000,
|
||||||
|
'1717662698058',
|
||||||
|
);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
async BootMiniApp(appfile: string, params: string) {
|
||||||
|
await this.context.session.getNodeMiscService().setMiniAppVersion('2.16.4');
|
||||||
|
const c = await this.context.session.getNodeMiscService().getMiniAppPath();
|
||||||
|
|
||||||
|
return this.context.session.getNodeMiscService().startNewMiniApp(appfile, params);
|
||||||
|
}
|
||||||
|
}
|
||||||
326
src/core/apis/user.ts
Normal file
326
src/core/apis/user.ts
Normal file
@@ -0,0 +1,326 @@
|
|||||||
|
import type { ModifyProfileParams, User, UserDetailInfoByUin, UserDetailInfoByUinV2 } from '@/core/entities';
|
||||||
|
import { NodeIKernelProfileListener } from '@/core/listeners';
|
||||||
|
import { RequestUtil } from '@/common/utils/request';
|
||||||
|
import { NodeIKernelProfileService, ProfileBizType, UserDetailSource } from '@/core/services';
|
||||||
|
import { InstanceContext, NapCatCore } from '..';
|
||||||
|
|
||||||
|
export class NTQQUserApi {
|
||||||
|
context: InstanceContext;
|
||||||
|
core: NapCatCore;
|
||||||
|
|
||||||
|
constructor(context: InstanceContext, core: NapCatCore) {
|
||||||
|
this.context = context;
|
||||||
|
this.core = core;
|
||||||
|
}
|
||||||
|
|
||||||
|
async getProfileLike(uid: string) {
|
||||||
|
return this.context.session.getProfileLikeService().getBuddyProfileLike({
|
||||||
|
friendUids: [
|
||||||
|
uid,
|
||||||
|
],
|
||||||
|
basic: 1,
|
||||||
|
vote: 1,
|
||||||
|
favorite: 0,
|
||||||
|
userProfile: 1,
|
||||||
|
type: 2,
|
||||||
|
start: 0,
|
||||||
|
limit: 20,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async setLongNick(longNick: string) {
|
||||||
|
return this.context.session.getProfileService().setLongNick(longNick);
|
||||||
|
}
|
||||||
|
|
||||||
|
async setSelfOnlineStatus(status: number, extStatus: number, batteryStatus: number) {
|
||||||
|
return this.context.session.getMsgService().setStatus({
|
||||||
|
status: status,
|
||||||
|
extStatus: extStatus,
|
||||||
|
batteryStatus: batteryStatus,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async getBuddyRecommendContactArkJson(uin: string, sencenID = '') {
|
||||||
|
return this.context.session.getBuddyService().getBuddyRecommendContactArkJson(uin, sencenID);
|
||||||
|
}
|
||||||
|
|
||||||
|
async like(uid: string, count = 1): Promise<{ result: number, errMsg: string, succCounts: number }> {
|
||||||
|
return this.context.session.getProfileLikeService().setBuddyProfileLike({
|
||||||
|
friendUid: uid,
|
||||||
|
sourceId: 71,
|
||||||
|
doLikeCount: count,
|
||||||
|
doLikeTollCount: 0,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async setQQAvatar(filePath: string) {
|
||||||
|
type setQQAvatarRet = { result: number, errMsg: string };
|
||||||
|
const ret = await this.context.session.getProfileService().setHeader(filePath) as setQQAvatarRet;
|
||||||
|
return { result: ret?.result, errMsg: ret?.errMsg };
|
||||||
|
}
|
||||||
|
|
||||||
|
async setGroupAvatar(gc: string, filePath: string) {
|
||||||
|
return this.context.session.getGroupService().setHeader(gc, filePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
async fetchUserDetailInfos(uids: string[]) {
|
||||||
|
//26702 以上使用新接口 .Dev Mlikiowa
|
||||||
|
type EventService = NodeIKernelProfileService['fetchUserDetailInfo'];
|
||||||
|
type EventListener = NodeIKernelProfileListener['onUserDetailInfoChanged'];
|
||||||
|
const retData: User[] = [];
|
||||||
|
const [_retData, _retListener] = await this.core.eventWrapper.CallNormalEvent<
|
||||||
|
EventService, EventListener
|
||||||
|
>(
|
||||||
|
'NodeIKernelProfileService/fetchUserDetailInfo',
|
||||||
|
'NodeIKernelProfileListener/onUserDetailInfoChanged',
|
||||||
|
uids.length,
|
||||||
|
5000,
|
||||||
|
(profile) => {
|
||||||
|
if (uids.includes(profile.uid)) {
|
||||||
|
const RetUser: User = {
|
||||||
|
...profile.simpleInfo.coreInfo,
|
||||||
|
...profile.simpleInfo.status,
|
||||||
|
...profile.simpleInfo.vasInfo,
|
||||||
|
...profile.commonExt,
|
||||||
|
...profile.simpleInfo.baseInfo,
|
||||||
|
qqLevel: profile.commonExt.qqLevel,
|
||||||
|
pendantId: '',
|
||||||
|
};
|
||||||
|
retData.push(RetUser);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
'BuddyProfileStore',
|
||||||
|
uids,
|
||||||
|
UserDetailSource.KSERVER,
|
||||||
|
[ProfileBizType.KALL],
|
||||||
|
);
|
||||||
|
|
||||||
|
return retData;
|
||||||
|
}
|
||||||
|
|
||||||
|
async fetchUserDetailInfo(uid: string) {
|
||||||
|
type EventService = NodeIKernelProfileService['fetchUserDetailInfo'];
|
||||||
|
type EventListener = NodeIKernelProfileListener['onUserDetailInfoChanged'];
|
||||||
|
const [_retData, profile] = await this.core.eventWrapper.CallNormalEvent<EventService, EventListener>(
|
||||||
|
'NodeIKernelProfileService/fetchUserDetailInfo',
|
||||||
|
'NodeIKernelProfileListener/onUserDetailInfoChanged',
|
||||||
|
1,
|
||||||
|
5000,
|
||||||
|
(profile) => profile.uid === uid,
|
||||||
|
'BuddyProfileStore',
|
||||||
|
[uid],
|
||||||
|
UserDetailSource.KSERVER,
|
||||||
|
[ProfileBizType.KALL],
|
||||||
|
);
|
||||||
|
const RetUser: User = {
|
||||||
|
...profile.simpleInfo.coreInfo,
|
||||||
|
...profile.simpleInfo.status,
|
||||||
|
...profile.simpleInfo.vasInfo,
|
||||||
|
...profile.commonExt,
|
||||||
|
...profile.simpleInfo.baseInfo,
|
||||||
|
qqLevel: profile.commonExt.qqLevel,
|
||||||
|
pendantId: '',
|
||||||
|
};
|
||||||
|
return RetUser;
|
||||||
|
}
|
||||||
|
|
||||||
|
async getUserDetailInfo(uid: string) {
|
||||||
|
if (this.context.basicInfoWrapper.requireMinNTQQBuild('26702')) {
|
||||||
|
return this.fetchUserDetailInfo(uid);
|
||||||
|
}
|
||||||
|
return this.getUserDetailInfoOld(uid);
|
||||||
|
}
|
||||||
|
|
||||||
|
async getUserDetailInfoOld(uid: string) {
|
||||||
|
type EventService = NodeIKernelProfileService['getUserDetailInfoWithBizInfo'];
|
||||||
|
type EventListener = NodeIKernelProfileListener['onProfileDetailInfoChanged'];
|
||||||
|
const [_retData, profile] = await this.core.eventWrapper.CallNormalEvent<EventService, EventListener>(
|
||||||
|
'NodeIKernelProfileService/getUserDetailInfoWithBizInfo',
|
||||||
|
'NodeIKernelProfileListener/onProfileDetailInfoChanged',
|
||||||
|
2,
|
||||||
|
5000,
|
||||||
|
(profile) => profile.uid === uid,
|
||||||
|
uid,
|
||||||
|
[0],
|
||||||
|
);
|
||||||
|
return profile;
|
||||||
|
}
|
||||||
|
|
||||||
|
async modifySelfProfile(param: ModifyProfileParams) {
|
||||||
|
return this.context.session.getProfileService().modifyDesktopMiniProfile(param);
|
||||||
|
}
|
||||||
|
|
||||||
|
//需要异常处理
|
||||||
|
async getCookies(domain: string) {
|
||||||
|
const ClientKeyData = await this.forceFetchClientKey();
|
||||||
|
const requestUrl = `https://ssl.ptlogin2.qq.com/jump?${
|
||||||
|
new URLSearchParams({
|
||||||
|
ptlang: '1033',
|
||||||
|
clientuin: this.core.selfInfo.uin,
|
||||||
|
clientkey: ClientKeyData.clientKey,
|
||||||
|
u1: `https://user.qzone.qq.com/${this.core.selfInfo.uin}/infocenter`,
|
||||||
|
keyindex: '19',
|
||||||
|
})
|
||||||
|
}`;
|
||||||
|
return await RequestUtil.HttpsGetCookies(requestUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
async getPSkey(domainList: string[]) {
|
||||||
|
return await this.context.session.getTipOffService().getPskey(domainList, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
async getRobotUinRange(): Promise<Array<any>> {
|
||||||
|
const robotUinRanges = await this.context.session.getRobotService().getRobotUinRange({
|
||||||
|
justFetchMsgConfig: '1',
|
||||||
|
type: 1,
|
||||||
|
version: 0,
|
||||||
|
aioKeywordVersion: 0,
|
||||||
|
});
|
||||||
|
// console.log(robotUinRanges?.response?.robotUinRanges);
|
||||||
|
return robotUinRanges?.response?.robotUinRanges;
|
||||||
|
}
|
||||||
|
|
||||||
|
//需要异常处理
|
||||||
|
|
||||||
|
async getQzoneCookies() {
|
||||||
|
const ClientKeyData = await this.forceFetchClientKey();
|
||||||
|
const requestUrl = 'https://ssl.ptlogin2.qq.com/jump?ptlang=1033&clientuin=' + this.core.selfInfo.uin + '&clientkey=' + ClientKeyData.clientKey + '&u1=https%3A%2F%2Fuser.qzone.qq.com%2F' + this.core.selfInfo.uin + '%2Finfocenter&keyindex=19%27';
|
||||||
|
const cookies: { [key: string]: string; } = await RequestUtil.HttpsGetCookies(requestUrl);
|
||||||
|
return cookies;
|
||||||
|
}
|
||||||
|
|
||||||
|
//需要异常处理
|
||||||
|
|
||||||
|
async getSkey(): Promise<string | undefined> {
|
||||||
|
const ClientKeyData = await this.forceFetchClientKey();
|
||||||
|
if (ClientKeyData.result !== 0) {
|
||||||
|
throw new Error('getClientKey Error');
|
||||||
|
}
|
||||||
|
const clientKey = ClientKeyData.clientKey;
|
||||||
|
const keyIndex = ClientKeyData.keyIndex;
|
||||||
|
const requestUrl = 'https://ssl.ptlogin2.qq.com/jump?ptlang=1033&clientuin=' + this.core.selfInfo.uin + '&clientkey=' + clientKey + '&u1=https%3A%2F%2Fh5.qzone.qq.com%2Fqqnt%2Fqzoneinpcqq%2Ffriend%3Frefresh%3D0%26clientuin%3D0%26darkMode%3D0&keyindex=19%27';
|
||||||
|
const cookies: { [key: string]: string; } = await RequestUtil.HttpsGetCookies(requestUrl);
|
||||||
|
const skey = cookies['skey'];
|
||||||
|
if (!skey) {
|
||||||
|
throw new Error('getSkey Skey is Empty');
|
||||||
|
}
|
||||||
|
return skey;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated
|
||||||
|
*/
|
||||||
|
async getUidByUin(Uin: string) {
|
||||||
|
if (this.context.basicInfoWrapper.requireMinNTQQBuild('26702')) {
|
||||||
|
return await this.getUidByUinV2(Uin);
|
||||||
|
}
|
||||||
|
return await this.getUidByUinV1(Uin);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated
|
||||||
|
*/
|
||||||
|
async getUinByUid(Uid: string) {
|
||||||
|
if (this.context.basicInfoWrapper.requireMinNTQQBuild('26702')) {
|
||||||
|
return await this.getUinByUidV2(Uid);
|
||||||
|
}
|
||||||
|
return await this.getUinByUidV1(Uid);
|
||||||
|
}
|
||||||
|
|
||||||
|
//后期改成流水线处理
|
||||||
|
async getUidByUinV2(Uin: string) {
|
||||||
|
let uid = (await this.context.session.getProfileService().getUidByUinV2('FriendsServiceImpl', [Uin])).get(Uin);
|
||||||
|
if (uid) return uid;
|
||||||
|
uid = (await this.context.session.getGroupService().getUidByUins([Uin])).uids.get(Uin);
|
||||||
|
if (uid) return uid;
|
||||||
|
uid = (await this.context.session.getUixConvertService().getUid([Uin])).uidInfo.get(Uin);
|
||||||
|
if (uid) return uid;
|
||||||
|
// console.log((await this.core.getApiContext().FriendApi.getBuddyIdMapCache(true)));
|
||||||
|
uid = (await this.core.apis.FriendApi.getBuddyIdMapCache(true)).getValue(Uin);//从Buddy缓存获取Uid
|
||||||
|
if (uid) return uid;
|
||||||
|
uid = (await this.core.apis.FriendApi.getBuddyIdMap(true)).getValue(Uin);
|
||||||
|
if (uid) return uid;
|
||||||
|
const unveifyUid = (await this.getUserDetailInfoByUinV2(Uin)).detail.uid;//从QQ Native 特殊转换
|
||||||
|
if (unveifyUid.indexOf('*') == -1) uid = unveifyUid;
|
||||||
|
//if (uid) return uid;
|
||||||
|
return uid;
|
||||||
|
}
|
||||||
|
|
||||||
|
//后期改成流水线处理
|
||||||
|
async getUinByUidV2(Uid: string) {
|
||||||
|
let uin = (await this.context.session.getProfileService().getUinByUid('FriendsServiceImpl', [Uid])).get(Uid);
|
||||||
|
if (uin) return uin;
|
||||||
|
uin = (await this.context.session.getGroupService().getUinByUids([Uid])).uins.get(Uid);
|
||||||
|
if (uin) return uin;
|
||||||
|
uin = (await this.context.session.getUixConvertService().getUin([Uid])).uinInfo.get(Uid);
|
||||||
|
if (uin) return uin;
|
||||||
|
uin = (await this.core.apis.FriendApi.getBuddyIdMapCache(true)).getKey(Uid);//从Buddy缓存获取Uin
|
||||||
|
if (uin) return uin;
|
||||||
|
uin = (await this.core.apis.FriendApi.getBuddyIdMap(true)).getKey(Uid);
|
||||||
|
if (uin) return uin;
|
||||||
|
uin = (await this.getUserDetailInfo(Uid)).uin; //从QQ Native 转换
|
||||||
|
return uin;
|
||||||
|
}
|
||||||
|
|
||||||
|
async getUidByUinV1(Uin: string) {
|
||||||
|
// 通用转换开始尝试
|
||||||
|
let uid = (await this.context.session.getUixConvertService().getUid([Uin])).uidInfo.get(Uin);
|
||||||
|
if (!uid) {
|
||||||
|
const unveifyUid = (await this.getUserDetailInfoByUin(Uin)).info.uid;//从QQ Native 特殊转换 方法三
|
||||||
|
if (unveifyUid.indexOf('*') == -1) {
|
||||||
|
uid = unveifyUid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return uid;
|
||||||
|
}
|
||||||
|
|
||||||
|
async getUinByUidV1(Uid: string) {
|
||||||
|
const ret = await this.core.eventWrapper.callNoListenerEvent<(Uin: string[]) => Promise<{
|
||||||
|
uinInfo: Map<string, string>
|
||||||
|
}>>
|
||||||
|
('NodeIKernelUixConvertService/getUin', 5000, [Uid]);
|
||||||
|
let uin = ret.uinInfo.get(Uid);
|
||||||
|
if (!uin) {
|
||||||
|
uin = (await this.getUserDetailInfo(Uid)).uin; //从QQ Native 转换
|
||||||
|
}
|
||||||
|
// if (!uin) {
|
||||||
|
// uin = (await NTQQFriendApi.getFriends(false)).find((t) => { t.uid == Uid })?.uin; //从QQ Native 缓存转换
|
||||||
|
// }
|
||||||
|
// if (!uin) {
|
||||||
|
// uin = (await NTQQFriendApi.getFriends(true)).find((t) => { t.uid == Uid })?.uin; //从QQ Native 非缓存转换
|
||||||
|
// }
|
||||||
|
return uin;
|
||||||
|
}
|
||||||
|
|
||||||
|
async getRecentContactListSnapShot(count: number) {
|
||||||
|
return await this.context.session.getRecentContactService().getRecentContactListSnapShot(count);
|
||||||
|
}
|
||||||
|
|
||||||
|
async getRecentContactListSyncLimit(count: number) {
|
||||||
|
return await this.context.session.getRecentContactService().getRecentContactListSyncLimit(count);
|
||||||
|
}
|
||||||
|
|
||||||
|
async getRecentContactListSync() {
|
||||||
|
return await this.context.session.getRecentContactService().getRecentContactListSync();
|
||||||
|
}
|
||||||
|
|
||||||
|
async getRecentContactList() {
|
||||||
|
return await this.context.session.getRecentContactService().getRecentContactList();
|
||||||
|
}
|
||||||
|
|
||||||
|
async getUserDetailInfoByUinV2(Uin: string) {
|
||||||
|
return await this.core.eventWrapper.callNoListenerEvent<(Uin: string) => Promise<UserDetailInfoByUinV2>>
|
||||||
|
('NodeIKernelProfileService/getUserDetailInfoByUin', 5000, Uin);
|
||||||
|
}
|
||||||
|
|
||||||
|
async getUserDetailInfoByUin(Uin: string) {
|
||||||
|
return this.core.eventWrapper.callNoListenerEvent<(Uin: string) => Promise<UserDetailInfoByUin>>
|
||||||
|
('NodeIKernelProfileService/getUserDetailInfoByUin', 5000, Uin);
|
||||||
|
}
|
||||||
|
|
||||||
|
async forceFetchClientKey() {
|
||||||
|
return await this.context.session.getTicketService().forceFetchClientKey('');
|
||||||
|
}
|
||||||
|
}
|
||||||
302
src/core/apis/webapi.ts
Normal file
302
src/core/apis/webapi.ts
Normal file
@@ -0,0 +1,302 @@
|
|||||||
|
import { RequestUtil } from '@/common/utils/request';
|
||||||
|
import {
|
||||||
|
GroupEssenceMsgRet,
|
||||||
|
InstanceContext,
|
||||||
|
WebApiGroupMember,
|
||||||
|
WebApiGroupMemberRet,
|
||||||
|
WebApiGroupNoticeRet,
|
||||||
|
WebHonorType,
|
||||||
|
} from '@/core';
|
||||||
|
import { NapCatCore } from '..';
|
||||||
|
|
||||||
|
export class NTQQWebApi {
|
||||||
|
context: InstanceContext;
|
||||||
|
core: NapCatCore;
|
||||||
|
|
||||||
|
constructor(context: InstanceContext, core: NapCatCore) {
|
||||||
|
this.context = context;
|
||||||
|
this.core = core;
|
||||||
|
}
|
||||||
|
|
||||||
|
async shareDigest(groupCode: string, msgSeq: string, msgRandom: string, targetGroupCode: string) {
|
||||||
|
const cookieObject = await this.core.apis.UserApi.getCookies('qun.qq.com');
|
||||||
|
const url = `https://qun.qq.com/cgi-bin/group_digest/share_digest?${
|
||||||
|
new URLSearchParams({
|
||||||
|
bkn: this.getBknFromCookie(cookieObject),
|
||||||
|
group_code: groupCode,
|
||||||
|
msg_seq: msgSeq,
|
||||||
|
msg_random: msgRandom,
|
||||||
|
target_group_code: targetGroupCode,
|
||||||
|
}).toString()
|
||||||
|
}`;
|
||||||
|
try {
|
||||||
|
return RequestUtil.HttpGetText(url, 'GET', '', { 'Cookie': this.cookieToString(cookieObject) });
|
||||||
|
} catch (e) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async getGroupEssenceMsg(GroupCode: string, page_start: string) {
|
||||||
|
const cookieObject = await this.core.apis.UserApi.getCookies('qun.qq.com');
|
||||||
|
const url = `https://qun.qq.com/cgi-bin/group_digest/digest_list?${
|
||||||
|
new URLSearchParams({
|
||||||
|
bkn: this.getBknFromCookie(cookieObject),
|
||||||
|
group_code: GroupCode,
|
||||||
|
page_start,
|
||||||
|
page_limit: '20',
|
||||||
|
}).toString()
|
||||||
|
}`;
|
||||||
|
let ret;
|
||||||
|
try {
|
||||||
|
ret = await RequestUtil.HttpGetJson<GroupEssenceMsgRet>
|
||||||
|
(url, 'GET', '', { 'Cookie': this.cookieToString(cookieObject) });
|
||||||
|
} catch {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
if (ret.retcode !== 0) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
async getGroupMembers(GroupCode: string, cached: boolean = true): Promise<WebApiGroupMember[]> {
|
||||||
|
//logDebug('webapi 获取群成员', GroupCode);
|
||||||
|
const memberData: Array<WebApiGroupMember> = new Array<WebApiGroupMember>();
|
||||||
|
const cookieObject = await this.core.apis.UserApi.getCookies('qun.qq.com');
|
||||||
|
const retList: Promise<WebApiGroupMemberRet>[] = [];
|
||||||
|
const fastRet = await RequestUtil.HttpGetJson<WebApiGroupMemberRet>
|
||||||
|
(`https://qun.qq.com/cgi-bin/qun_mgr/search_group_members?${
|
||||||
|
new URLSearchParams({
|
||||||
|
st: '0',
|
||||||
|
end: '40',
|
||||||
|
sort: '1',
|
||||||
|
gc: GroupCode,
|
||||||
|
bkn: this.getBknFromCookie(cookieObject),
|
||||||
|
}).toString()
|
||||||
|
}`, 'POST', '', { 'Cookie': this.cookieToString(cookieObject) });
|
||||||
|
if (!fastRet?.count || fastRet?.errcode !== 0 || !fastRet?.mems) {
|
||||||
|
return [];
|
||||||
|
} else {
|
||||||
|
for (const key in fastRet.mems) {
|
||||||
|
memberData.push(fastRet.mems[key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//初始化获取PageNum
|
||||||
|
const PageNum = Math.ceil(fastRet.count / 40);
|
||||||
|
//遍历批量请求
|
||||||
|
for (let i = 2; i <= PageNum; i++) {
|
||||||
|
const ret = RequestUtil.HttpGetJson<WebApiGroupMemberRet>
|
||||||
|
(`https://qun.qq.com/cgi-bin/qun_mgr/search_group_members?${
|
||||||
|
new URLSearchParams({
|
||||||
|
st: ((i - 1) * 40).toString(),
|
||||||
|
end: (i * 40).toString(),
|
||||||
|
sort: '1',
|
||||||
|
gc: GroupCode,
|
||||||
|
bkn: this.getBknFromCookie(cookieObject),
|
||||||
|
}).toString()
|
||||||
|
}`, 'POST', '', { 'Cookie': this.cookieToString(cookieObject) });
|
||||||
|
retList.push(ret);
|
||||||
|
}
|
||||||
|
//批量等待
|
||||||
|
for (let i = 1; i <= PageNum; i++) {
|
||||||
|
const ret = await (retList[i]);
|
||||||
|
if (!ret?.count || ret?.errcode !== 0 || !ret?.mems) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for (const key in ret.mems) {
|
||||||
|
memberData.push(ret.mems[key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return memberData;
|
||||||
|
}
|
||||||
|
|
||||||
|
// public async addGroupDigest(groupCode: string, msgSeq: string) {
|
||||||
|
// const url = `https://qun.qq.com/cgi-bin/group_digest/cancel_digest?random=665&X-CROSS-ORIGIN=fetch&group_code=${groupCode}&msg_seq=${msgSeq}&msg_random=444021292`;
|
||||||
|
// const res = await this.request(url);
|
||||||
|
// return await res.json();
|
||||||
|
// }
|
||||||
|
|
||||||
|
// public async getGroupDigest(groupCode: string) {
|
||||||
|
// const url = `https://qun.qq.com/cgi-bin/group_digest/digest_list?random=665&X-CROSS-ORIGIN=fetch&group_code=${groupCode}&page_start=0&page_limit=20`;
|
||||||
|
// const res = await this.request(url);
|
||||||
|
// return await res.json();
|
||||||
|
// }
|
||||||
|
|
||||||
|
async setGroupNotice(GroupCode: string, Content: string) {
|
||||||
|
const cookieObject = await this.core.apis.UserApi.getCookies('qun.qq.com');
|
||||||
|
let ret: any = undefined;
|
||||||
|
try {
|
||||||
|
ret = await RequestUtil.HttpGetJson<any>
|
||||||
|
(`https://web.qun.qq.com/cgi-bin/announce/add_qun_notice${
|
||||||
|
new URLSearchParams({
|
||||||
|
bkn: this.getBknFromCookie(cookieObject),
|
||||||
|
qid: GroupCode,
|
||||||
|
text: Content,
|
||||||
|
pinned: '0',
|
||||||
|
type: '1',
|
||||||
|
settings: '{"is_show_edit_card":1,"tip_window_type":1,"confirm_required":1}',
|
||||||
|
}).toString()
|
||||||
|
}`, 'GET', '', { 'Cookie': this.cookieToString(cookieObject) });
|
||||||
|
return ret;
|
||||||
|
} catch (e) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async getGroupNotice(GroupCode: string): Promise<undefined | WebApiGroupNoticeRet> {
|
||||||
|
const cookieObject = await this.core.apis.UserApi.getCookies('qun.qq.com');
|
||||||
|
let ret: WebApiGroupNoticeRet | undefined = undefined;
|
||||||
|
try {
|
||||||
|
ret = await RequestUtil.HttpGetJson<WebApiGroupNoticeRet>(`https://web.qun.qq.com/cgi-bin/announce/get_t_list?${
|
||||||
|
new URLSearchParams({
|
||||||
|
bkn: this.getBknFromCookie(cookieObject),
|
||||||
|
qid: GroupCode,
|
||||||
|
type: '1',
|
||||||
|
start: '0',
|
||||||
|
num: '1',
|
||||||
|
}).toString()
|
||||||
|
}`, 'GET', '', { 'Cookie': this.cookieToString(cookieObject) });
|
||||||
|
if (ret?.ec !== 0) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
} catch (e) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async getGroupHonorInfo(groupCode: string, getType: WebHonorType) {
|
||||||
|
const cookieObject = await this.core.apis.UserApi.getCookies('qun.qq.com');
|
||||||
|
const getDataInternal = async (Internal_groupCode: string, Internal_type: number) => {
|
||||||
|
const url = `https://qun.qq.com/interactive/honorlist?${
|
||||||
|
new URLSearchParams({
|
||||||
|
gc: Internal_groupCode,
|
||||||
|
type: Internal_type.toString(),
|
||||||
|
}).toString()
|
||||||
|
}`;
|
||||||
|
let resJson;
|
||||||
|
try {
|
||||||
|
const res = await RequestUtil.HttpGetText(url, 'GET', '', { 'Cookie': this.cookieToString(cookieObject) });
|
||||||
|
const match = res.match(/window\.__INITIAL_STATE__=(.*?);/);
|
||||||
|
if (match) {
|
||||||
|
resJson = JSON.parse(match[1].trim());
|
||||||
|
}
|
||||||
|
if (Internal_type === 1) {
|
||||||
|
return resJson?.talkativeList;
|
||||||
|
} else {
|
||||||
|
return resJson?.actorList;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
this.context.logger.logDebug('获取当前群荣耀失败', url, e);
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
const HonorInfo: any = { group_id: groupCode };
|
||||||
|
|
||||||
|
if (getType === WebHonorType.TALKATIVE || getType === WebHonorType.ALL) {
|
||||||
|
try {
|
||||||
|
const RetInternal = await getDataInternal(groupCode, 1);
|
||||||
|
if (!RetInternal) {
|
||||||
|
throw new Error('获取龙王信息失败');
|
||||||
|
}
|
||||||
|
HonorInfo.current_talkative = {
|
||||||
|
user_id: RetInternal[0]?.uin,
|
||||||
|
avatar: RetInternal[0]?.avatar,
|
||||||
|
nickname: RetInternal[0]?.name,
|
||||||
|
day_count: 0,
|
||||||
|
description: RetInternal[0]?.desc,
|
||||||
|
};
|
||||||
|
HonorInfo.talkative_list = [];
|
||||||
|
for (const talkative_ele of RetInternal) {
|
||||||
|
HonorInfo.talkative_list.push({
|
||||||
|
user_id: talkative_ele?.uin,
|
||||||
|
avatar: talkative_ele?.avatar,
|
||||||
|
description: talkative_ele?.desc,
|
||||||
|
day_count: 0,
|
||||||
|
nickname: talkative_ele?.name,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
this.context.logger.logDebug(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (getType === WebHonorType.PERFORMER || getType === WebHonorType.ALL) {
|
||||||
|
try {
|
||||||
|
const RetInternal = await getDataInternal(groupCode, 2);
|
||||||
|
if (!RetInternal) {
|
||||||
|
throw new Error('获取群聊之火失败');
|
||||||
|
}
|
||||||
|
HonorInfo.performer_list = [];
|
||||||
|
for (const performer_ele of RetInternal) {
|
||||||
|
HonorInfo.performer_list.push({
|
||||||
|
user_id: performer_ele?.uin,
|
||||||
|
nickname: performer_ele?.name,
|
||||||
|
avatar: performer_ele?.avatar,
|
||||||
|
description: performer_ele?.desc,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
this.context.logger.logDebug(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (getType === WebHonorType.PERFORMER || getType === WebHonorType.ALL) {
|
||||||
|
try {
|
||||||
|
const RetInternal = await getDataInternal(groupCode, 3);
|
||||||
|
if (!RetInternal) {
|
||||||
|
throw new Error('获取群聊炽焰失败');
|
||||||
|
}
|
||||||
|
HonorInfo.legend_list = [];
|
||||||
|
for (const legend_ele of RetInternal) {
|
||||||
|
HonorInfo.legend_list.push({
|
||||||
|
user_id: legend_ele?.uin,
|
||||||
|
nickname: legend_ele?.name,
|
||||||
|
avatar: legend_ele?.avatar,
|
||||||
|
desc: legend_ele?.description,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
this.context.logger.logDebug('获取群聊炽焰失败', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (getType === WebHonorType.EMOTION || getType === WebHonorType.ALL) {
|
||||||
|
try {
|
||||||
|
const RetInternal = await getDataInternal(groupCode, 6);
|
||||||
|
if (!RetInternal) {
|
||||||
|
throw new Error('获取快乐源泉失败');
|
||||||
|
}
|
||||||
|
HonorInfo.emotion_list = [];
|
||||||
|
for (const emotion_ele of RetInternal) {
|
||||||
|
HonorInfo.emotion_list.push({
|
||||||
|
user_id: emotion_ele.uin,
|
||||||
|
nickname: emotion_ele.name,
|
||||||
|
avatar: emotion_ele.avatar,
|
||||||
|
desc: emotion_ele.description,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
this.context.logger.logDebug('获取快乐源泉失败', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//冒尖小春笋好像已经被tx扬了
|
||||||
|
if (getType === WebHonorType.EMOTION || getType === WebHonorType.ALL) {
|
||||||
|
HonorInfo.strong_newbie_list = [];
|
||||||
|
}
|
||||||
|
return HonorInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
private cookieToString(cookieObject: any) {
|
||||||
|
return Object.entries(cookieObject).map(([key, value]) => `${key}=${value}`).join('; ');
|
||||||
|
}
|
||||||
|
|
||||||
|
public getBknFromCookie(cookieObject: any) {
|
||||||
|
const sKey = cookieObject.skey as string;
|
||||||
|
|
||||||
|
let hash = 5381;
|
||||||
|
for (let i = 0; i < sKey.length; i++) {
|
||||||
|
const code = sKey.charCodeAt(i);
|
||||||
|
hash = hash + (hash << 5) + code;
|
||||||
|
}
|
||||||
|
return (hash & 0x7FFFFFFF).toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
204
src/core/core.ts
Normal file
204
src/core/core.ts
Normal file
@@ -0,0 +1,204 @@
|
|||||||
|
import { NodeQQNTWrapperUtil, NTApiContext, WrapperNodeApi } from '@/core/wrapper';
|
||||||
|
import path from 'node:path';
|
||||||
|
import fs from 'node:fs';
|
||||||
|
import { InstanceContext } from './wrapper';
|
||||||
|
import { proxiedListenerOf } from '@/common/utils/proxy-handler';
|
||||||
|
import { GroupListener, MsgListener, ProfileListener } from './listeners';
|
||||||
|
import { GroupMember, SelfInfo, SelfStatusInfo } from './entities';
|
||||||
|
import { LegacyNTEventWrapper } from '@/common/framework/event-legacy';
|
||||||
|
import { NTQQFileApi, NTQQFriendApi, NTQQGroupApi, NTQQMsgApi, NTQQSystemApi, NTQQUserApi, NTQQWebApi } from './apis';
|
||||||
|
import os from 'node:os';
|
||||||
|
import { NTQQCollectionApi } from './apis/collection';
|
||||||
|
import { NapCatConfigLoader } from './helper/config';
|
||||||
|
import { LogLevel } from '@/common/utils/log';
|
||||||
|
|
||||||
|
export enum NapCatCoreWorkingEnv {
|
||||||
|
Unknown = 0,
|
||||||
|
Shell = 1,
|
||||||
|
Framework = 2,
|
||||||
|
}
|
||||||
|
|
||||||
|
export function loadQQWrapper(QQVersion: string): WrapperNodeApi {
|
||||||
|
let wrapperNodePath = path.resolve(path.dirname(process.execPath), './resources/app/wrapper.node');
|
||||||
|
if (!fs.existsSync(wrapperNodePath)) {
|
||||||
|
wrapperNodePath = path.join(path.dirname(process.execPath), `resources/app/versions/${QQVersion}/wrapper.node`);
|
||||||
|
}
|
||||||
|
const nativemodule: any = { exports: {} };
|
||||||
|
process.dlopen(nativemodule, wrapperNodePath);
|
||||||
|
return nativemodule.exports;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class NapCatCore {
|
||||||
|
readonly context: InstanceContext;
|
||||||
|
readonly apis: NTApiContext;
|
||||||
|
readonly eventWrapper: LegacyNTEventWrapper;
|
||||||
|
// readonly eventChannel: NTEventChannel;
|
||||||
|
NapCatDataPath: string;
|
||||||
|
NapCatTempPath: string;
|
||||||
|
// runtime info, not readonly
|
||||||
|
selfInfo: SelfInfo;
|
||||||
|
util: NodeQQNTWrapperUtil;
|
||||||
|
configLoader: NapCatConfigLoader;
|
||||||
|
|
||||||
|
// 通过构造器递过去的 runtime info 应该尽量少
|
||||||
|
constructor(context: InstanceContext, selfInfo: SelfInfo) {
|
||||||
|
this.selfInfo = selfInfo;
|
||||||
|
this.context = context;
|
||||||
|
this.util = new this.context.wrapper.NodeQQNTWrapperUtil();
|
||||||
|
this.eventWrapper = new LegacyNTEventWrapper(context.wrapper, context.session);
|
||||||
|
this.apis = {
|
||||||
|
FileApi: new NTQQFileApi(this.context, this),
|
||||||
|
SystemApi: new NTQQSystemApi(this.context, this),
|
||||||
|
CollectionApi: new NTQQCollectionApi(this.context, this),
|
||||||
|
WebApi: new NTQQWebApi(this.context, this),
|
||||||
|
FriendApi: new NTQQFriendApi(this.context, this),
|
||||||
|
MsgApi: new NTQQMsgApi(this.context, this),
|
||||||
|
UserApi: new NTQQUserApi(this.context, this),
|
||||||
|
GroupApi: new NTQQGroupApi(this.context, this),
|
||||||
|
};
|
||||||
|
this.configLoader = new NapCatConfigLoader(this, this.context.pathWrapper.configPath);
|
||||||
|
this.NapCatDataPath = path.join(this.dataPath, 'NapCat');
|
||||||
|
fs.mkdirSync(this.NapCatDataPath, { recursive: true });
|
||||||
|
this.NapCatTempPath = path.join(this.NapCatDataPath, 'temp');
|
||||||
|
// 创建临时目录
|
||||||
|
if (!fs.existsSync(this.NapCatTempPath)) {
|
||||||
|
fs.mkdirSync(this.NapCatTempPath, { recursive: true });
|
||||||
|
}
|
||||||
|
this.initNapCatCoreListeners().then().catch(this.context.logger.logError);
|
||||||
|
|
||||||
|
this.context.logger.setFileLogEnabled(
|
||||||
|
this.configLoader.configData.fileLog,
|
||||||
|
);
|
||||||
|
this.context.logger.setConsoleLogEnabled(
|
||||||
|
this.configLoader.configData.consoleLog,
|
||||||
|
);
|
||||||
|
this.context.logger.setFileAndConsoleLogLevel(
|
||||||
|
this.configLoader.configData.fileLogLevel as LogLevel,
|
||||||
|
this.configLoader.configData.consoleLogLevel as LogLevel,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
get dataPath(): string {
|
||||||
|
let result = this.util.getNTUserDataInfoConfig();
|
||||||
|
if (!result) {
|
||||||
|
result = path.resolve(os.homedir(), './.config/QQ');
|
||||||
|
fs.mkdirSync(result, { recursive: true });
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Renamed from 'InitDataListener'
|
||||||
|
async initNapCatCoreListeners() {
|
||||||
|
const msgListener = new MsgListener();
|
||||||
|
msgListener.onRecvMsg = (msg) => {
|
||||||
|
//console.log('RecvMsg', msg);
|
||||||
|
};
|
||||||
|
//await sleep(2500);
|
||||||
|
this.context.session.getMsgService().addKernelMsgListener(
|
||||||
|
new this.context.wrapper.NodeIKernelMsgListener(proxiedListenerOf(msgListener, this.context.logger)),
|
||||||
|
);
|
||||||
|
|
||||||
|
const profileListener = new ProfileListener();
|
||||||
|
profileListener.onProfileDetailInfoChanged = (profile) => {
|
||||||
|
if (profile.uid === this.selfInfo.uid) {
|
||||||
|
Object.assign(this.selfInfo, profile);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
profileListener.onSelfStatusChanged = (Info: SelfStatusInfo) => {
|
||||||
|
// if (Info.status == 20) {
|
||||||
|
// log("账号状态变更为离线")
|
||||||
|
// }
|
||||||
|
};
|
||||||
|
this.context.session.getProfileService().addKernelProfileListener(
|
||||||
|
new this.context.wrapper.NodeIKernelProfileListener(proxiedListenerOf(profileListener, this.context.logger)),
|
||||||
|
);
|
||||||
|
|
||||||
|
// 群相关
|
||||||
|
const groupListener = new GroupListener();
|
||||||
|
groupListener.onGroupListUpdate = (updateType, groupList) => {
|
||||||
|
// console.log("onGroupListUpdate", updateType, groupList)
|
||||||
|
groupList.map(g => {
|
||||||
|
const existGroup = this.apis.GroupApi.groupCache.get(g.groupCode);
|
||||||
|
//群成员数量变化 应该刷新缓存
|
||||||
|
if (existGroup && g.memberCount === existGroup.memberCount) {
|
||||||
|
Object.assign(existGroup, g);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.apis.GroupApi.groupCache.set(g.groupCode, g);
|
||||||
|
// 获取群成员
|
||||||
|
}
|
||||||
|
const sceneId = this.context.session.getGroupService().createMemberListScene(g.groupCode, 'groupMemberList_MainWindow');
|
||||||
|
this.context.session.getGroupService().getNextMemberList(sceneId!, undefined, 3000).then(r => {
|
||||||
|
// console.log(`get group ${g.groupCode} members`, r);
|
||||||
|
// r.result.infos.forEach(member => {
|
||||||
|
// });
|
||||||
|
// groupMembers.set(g.groupCode, r.result.infos);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
groupListener.onMemberListChange = (arg) => {
|
||||||
|
// todo: 应该加一个内部自己维护的成员变动callback,用于判断成员变化通知
|
||||||
|
const groupCode = arg.sceneId.split('_')[0];
|
||||||
|
if (this.apis.GroupApi.groupMemberCache.has(groupCode)) {
|
||||||
|
const existMembers = this.apis.GroupApi.groupMemberCache.get(groupCode)!;
|
||||||
|
arg.infos.forEach((member, uid) => {
|
||||||
|
//console.log('onMemberListChange', member);
|
||||||
|
const existMember = existMembers.get(uid);
|
||||||
|
if (existMember) {
|
||||||
|
Object.assign(existMember, member);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
existMembers!.set(uid, member);
|
||||||
|
}
|
||||||
|
//移除成员
|
||||||
|
if (member.isDelete) {
|
||||||
|
existMembers.delete(uid);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.apis.GroupApi.groupMemberCache.set(groupCode, arg.infos);
|
||||||
|
}
|
||||||
|
// console.log('onMemberListChange', groupCode, arg);
|
||||||
|
};
|
||||||
|
groupListener.onMemberInfoChange = (groupCode, changeType, members) => {
|
||||||
|
//console.log('onMemberInfoChange', groupCode, changeType, members);
|
||||||
|
if (changeType === 0 && members.get(this.selfInfo.uid)?.isDelete) {
|
||||||
|
// 自身退群或者被踢退群 5s用于Api操作 之后不再出现
|
||||||
|
setTimeout(() => {
|
||||||
|
this.apis.GroupApi.groupCache.delete(groupCode);
|
||||||
|
}, 5000);
|
||||||
|
|
||||||
|
}
|
||||||
|
const existMembers = this.apis.GroupApi.groupMemberCache.get(groupCode);
|
||||||
|
if (existMembers) {
|
||||||
|
members.forEach((member, uid) => {
|
||||||
|
const existMember = existMembers.get(uid);
|
||||||
|
if (existMember) {
|
||||||
|
// 检查管理变动
|
||||||
|
member.isChangeRole = this.checkAdminEvent(groupCode, member, existMember);
|
||||||
|
// 更新成员信息
|
||||||
|
Object.assign(existMember, member);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
existMembers.set(uid, member);
|
||||||
|
}
|
||||||
|
//移除成员
|
||||||
|
if (member.isDelete) {
|
||||||
|
existMembers.delete(uid);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.apis.GroupApi.groupMemberCache.set(groupCode, members);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
checkAdminEvent(groupCode: string, memberNew: GroupMember, memberOld: GroupMember | undefined): boolean {
|
||||||
|
if (memberNew.role !== memberOld?.role) {
|
||||||
|
this.context.logger.log(`群 ${groupCode} ${memberNew.nick} 角色变更为 ${memberNew.role === 3 ? '管理员' : '群员'}`);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
65
src/core/entities/cache.ts
Normal file
65
src/core/entities/cache.ts
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
import { ChatType } from './msg';
|
||||||
|
|
||||||
|
export interface CacheScanResult {
|
||||||
|
result: number;
|
||||||
|
size: [ // 单位为字节
|
||||||
|
string, // 系统总存储空间
|
||||||
|
string, // 系统可用存储空间
|
||||||
|
string, // 系统已用存储空间
|
||||||
|
string, // QQ总大小
|
||||||
|
string, // 「聊天与文件」大小
|
||||||
|
string, // 未知
|
||||||
|
string, // 「缓存数据」大小
|
||||||
|
string, // 「其他数据」大小
|
||||||
|
string, // 未知
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ChatCacheList {
|
||||||
|
pageCount: number;
|
||||||
|
infos: ChatCacheListItem[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ChatCacheListItem {
|
||||||
|
chatType: ChatType;
|
||||||
|
basicChatCacheInfo: ChatCacheListItemBasic;
|
||||||
|
guildChatCacheInfo: unknown[]; // TODO: 没用过频道所以不知道这里边的详细内容
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ChatCacheListItemBasic {
|
||||||
|
chatSize: string;
|
||||||
|
chatTime: string;
|
||||||
|
uid: string;
|
||||||
|
uin: string;
|
||||||
|
remarkName: string;
|
||||||
|
nickName: string;
|
||||||
|
chatType?: ChatType;
|
||||||
|
isChecked?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum CacheFileType {
|
||||||
|
IMAGE = 0,
|
||||||
|
VIDEO = 1,
|
||||||
|
AUDIO = 2,
|
||||||
|
DOCUMENT = 3,
|
||||||
|
OTHER = 4,
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CacheFileList {
|
||||||
|
infos: CacheFileListItem[],
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CacheFileListItem {
|
||||||
|
fileSize: string;
|
||||||
|
fileTime: string;
|
||||||
|
fileKey: string;
|
||||||
|
elementId: string;
|
||||||
|
elementIdStr: string;
|
||||||
|
fileType: CacheFileType;
|
||||||
|
path: string;
|
||||||
|
fileName: string;
|
||||||
|
senderId: string;
|
||||||
|
previewPath: string;
|
||||||
|
senderName: string;
|
||||||
|
isChecked?: boolean;
|
||||||
|
}
|
||||||
72
src/core/entities/group.ts
Normal file
72
src/core/entities/group.ts
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
import { QQLevel, Sex } from './user';
|
||||||
|
|
||||||
|
export enum GroupListUpdateType {
|
||||||
|
REFRESHALL,
|
||||||
|
GETALL,
|
||||||
|
MODIFIED,
|
||||||
|
REMOVE
|
||||||
|
}
|
||||||
|
export interface GroupMemberCache {
|
||||||
|
group: {
|
||||||
|
data: GroupMember[];
|
||||||
|
isExpired: boolean;
|
||||||
|
}
|
||||||
|
isExpired: boolean;
|
||||||
|
}
|
||||||
|
export interface Group {
|
||||||
|
groupCode: string,
|
||||||
|
createTime?: string,//高版本才有
|
||||||
|
maxMember: number,
|
||||||
|
memberCount: number,
|
||||||
|
groupName: string,
|
||||||
|
groupStatus: number,
|
||||||
|
memberRole: number,
|
||||||
|
isTop: boolean,
|
||||||
|
toppedTimestamp: string,
|
||||||
|
privilegeFlag: number, //65760
|
||||||
|
isConf: boolean,
|
||||||
|
hasModifyConfGroupFace: boolean,
|
||||||
|
hasModifyConfGroupName: boolean,
|
||||||
|
remarkName: string,
|
||||||
|
hasMemo: boolean,
|
||||||
|
groupShutupExpireTime: string, //"0",
|
||||||
|
personShutupExpireTime: string, //"0",
|
||||||
|
discussToGroupUin: string, //"0",
|
||||||
|
discussToGroupMaxMsgSeq: number,
|
||||||
|
discussToGroupTime: number,
|
||||||
|
groupFlagExt: number, //1073938496,
|
||||||
|
authGroupType: number, //0,
|
||||||
|
groupCreditLevel: number, //0,
|
||||||
|
groupFlagExt3: number, //0,
|
||||||
|
groupOwnerId: {
|
||||||
|
memberUin: string, //"0",
|
||||||
|
memberUid: string, //"u_fbf8N7aeuZEnUiJAbQ9R8Q"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum GroupMemberRole {
|
||||||
|
normal = 2,
|
||||||
|
admin = 3,
|
||||||
|
owner = 4
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface GroupMember {
|
||||||
|
memberSpecialTitle?: string;
|
||||||
|
avatarPath: string;
|
||||||
|
cardName: string;
|
||||||
|
cardType: number;
|
||||||
|
isDelete: boolean;
|
||||||
|
nick: string;
|
||||||
|
qid: string;
|
||||||
|
remark: string;
|
||||||
|
role: GroupMemberRole; // 群主:4, 管理员:3,群员:2
|
||||||
|
shutUpTime: number; // 禁言时间,单位是什么暂时不清楚
|
||||||
|
uid: string; // 加密的字符串
|
||||||
|
uin: string; // QQ号
|
||||||
|
isRobot: boolean;
|
||||||
|
sex?: Sex;
|
||||||
|
qqLevel?: QQLevel;
|
||||||
|
isChangeRole: boolean;
|
||||||
|
joinTime: string;
|
||||||
|
lastSpeakTime: string;
|
||||||
|
}
|
||||||
8
src/core/entities/index.ts
Normal file
8
src/core/entities/index.ts
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
export * from './user';
|
||||||
|
export * from './group';
|
||||||
|
export * from './msg';
|
||||||
|
export * from './notify';
|
||||||
|
export * from './cache';
|
||||||
|
export * from './system';
|
||||||
|
export * from './webapi';
|
||||||
|
export * from './sign';
|
||||||
899
src/core/entities/msg.ts
Normal file
899
src/core/entities/msg.ts
Normal file
@@ -0,0 +1,899 @@
|
|||||||
|
import { GroupMemberRole } from './group';
|
||||||
|
|
||||||
|
export interface Peer {
|
||||||
|
chatType: ChatType;
|
||||||
|
peerUid: string; // 如果是群聊uid为群号,私聊uid就是加密的字符串
|
||||||
|
guildId?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface KickedOffLineInfo {
|
||||||
|
appId: number;
|
||||||
|
instanceId: number;
|
||||||
|
sameDevice: boolean;
|
||||||
|
tipsDesc: string;
|
||||||
|
tipsTitle: string;
|
||||||
|
kickedType: number;
|
||||||
|
securityKickedType: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface GetFileListParam {
|
||||||
|
sortType: number;
|
||||||
|
fileCount: number;
|
||||||
|
startIndex: number;
|
||||||
|
sortOrder: number;
|
||||||
|
showOnlinedocFolder: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum ElementType {
|
||||||
|
UNKNOWN = 0,
|
||||||
|
TEXT = 1,
|
||||||
|
PIC = 2,
|
||||||
|
FILE = 3,
|
||||||
|
PTT = 4,
|
||||||
|
VIDEO = 5,
|
||||||
|
FACE = 6,
|
||||||
|
REPLY = 7,
|
||||||
|
WALLET = 9,
|
||||||
|
GreyTip = 8,//Poke别叫戳一搓了 官方名字拍一拍 戳一戳是另一个名字
|
||||||
|
ARK = 10,
|
||||||
|
MFACE = 11,
|
||||||
|
LIVEGIFT = 12,
|
||||||
|
STRUCTLONGMSG = 13,
|
||||||
|
MARKDOWN = 14,
|
||||||
|
GIPHY = 15,
|
||||||
|
MULTIFORWARD = 16,
|
||||||
|
INLINEKEYBOARD = 17,
|
||||||
|
INTEXTGIFT = 18,
|
||||||
|
CALENDAR = 19,
|
||||||
|
YOLOGAMERESULT = 20,
|
||||||
|
AVRECORD = 21,
|
||||||
|
FEED = 22,
|
||||||
|
TOFURECORD = 23,
|
||||||
|
ACEBUBBLE = 24,
|
||||||
|
ACTIVITY = 25,
|
||||||
|
TOFU = 26,
|
||||||
|
FACEBUBBLE = 27,
|
||||||
|
SHARELOCATION = 28,
|
||||||
|
TASKTOPMSG = 29,
|
||||||
|
RECOMMENDEDMSG = 43,
|
||||||
|
ACTIONBAR = 44
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ActionBarElement {
|
||||||
|
rows: InlineKeyboardRow[];
|
||||||
|
botAppid: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SendActionBarElement {
|
||||||
|
elementType: ElementType.ACTIONBAR;
|
||||||
|
elementId: string;
|
||||||
|
actionBarElement: ActionBarElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface RecommendedMsgElement {
|
||||||
|
rows: InlineKeyboardRow[];
|
||||||
|
botAppid: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SendRecommendedMsgElement {
|
||||||
|
elementType: ElementType.RECOMMENDEDMSG;
|
||||||
|
elementId: string;
|
||||||
|
recommendedMsgElement: RecommendedMsgElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface InlineKeyboardButton {
|
||||||
|
id: string;
|
||||||
|
label: string;
|
||||||
|
visitedLabel: string;
|
||||||
|
unsupportTips: string;
|
||||||
|
data: string;
|
||||||
|
specifyRoleIds: string[];
|
||||||
|
specifyTinyids: string[];
|
||||||
|
style: number;
|
||||||
|
type: number;
|
||||||
|
clickLimit: number;
|
||||||
|
atBotShowChannelList: boolean;
|
||||||
|
permissionType: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface InlineKeyboardRow {
|
||||||
|
buttons: InlineKeyboardButton[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TofuElementContent {
|
||||||
|
color: string;
|
||||||
|
tittle: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TaskTopMsgElement {
|
||||||
|
msgTitle: string;
|
||||||
|
msgSummary: string;
|
||||||
|
iconUrl: string;
|
||||||
|
topMsgType: number;
|
||||||
|
}
|
||||||
|
export enum NTMsgType {
|
||||||
|
KMSGTYPEARKSTRUCT = 11,
|
||||||
|
KMSGTYPEFACEBUBBLE = 24,
|
||||||
|
KMSGTYPEFILE = 3,
|
||||||
|
KMSGTYPEGIFT = 14,
|
||||||
|
KMSGTYPEGIPHY = 13,
|
||||||
|
KMSGTYPEGRAYTIPS = 5,
|
||||||
|
KMSGTYPEMIX = 2,
|
||||||
|
KMSGTYPEMULTIMSGFORWARD = 8,
|
||||||
|
KMSGTYPENULL = 1,
|
||||||
|
KMSGTYPEONLINEFILE = 21,
|
||||||
|
KMSGTYPEONLINEFOLDER = 27,
|
||||||
|
KMSGTYPEPROLOGUE = 29,
|
||||||
|
KMSGTYPEPTT = 6,
|
||||||
|
KMSGTYPEREPLY = 9,
|
||||||
|
KMSGTYPESHARELOCATION = 25,
|
||||||
|
KMSGTYPESTRUCT = 4,
|
||||||
|
KMSGTYPESTRUCTLONGMSG = 12,
|
||||||
|
KMSGTYPETEXTGIFT = 15,
|
||||||
|
KMSGTYPEUNKNOWN = 0,
|
||||||
|
KMSGTYPEVIDEO = 7,
|
||||||
|
KMSGTYPEWALLET = 10
|
||||||
|
}
|
||||||
|
export interface SendTaskTopMsgElement {
|
||||||
|
elementType: ElementType.TASKTOPMSG;
|
||||||
|
elementId: string;
|
||||||
|
taskTopMsgElement: TaskTopMsgElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TofuRecordElement {
|
||||||
|
type: number;
|
||||||
|
busiid: string;
|
||||||
|
busiuuid: string;
|
||||||
|
descriptionContent: string;
|
||||||
|
contentlist: TofuElementContent[],
|
||||||
|
background: string;
|
||||||
|
icon: string;
|
||||||
|
uinlist: string[],
|
||||||
|
uidlist: string[],
|
||||||
|
busiExtra: string;
|
||||||
|
updateTime: string;
|
||||||
|
dependedmsgid: string;
|
||||||
|
msgtime: string;
|
||||||
|
onscreennotify: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SendTofuRecordElement {
|
||||||
|
elementType: ElementType.TOFURECORD;
|
||||||
|
elementId: string;
|
||||||
|
tofuRecordElement: TofuRecordElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FaceBubbleElement {
|
||||||
|
faceCount: number;
|
||||||
|
faceSummary: string;
|
||||||
|
faceFlag: number;
|
||||||
|
content: string;
|
||||||
|
oldVersionStr: string;
|
||||||
|
faceType: number;
|
||||||
|
others: string;
|
||||||
|
yellowFaceInfo: {
|
||||||
|
index: number;
|
||||||
|
buf: string;
|
||||||
|
compatibleText: string;
|
||||||
|
text: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SendFaceBubbleElement {
|
||||||
|
elementType: ElementType.FACEBUBBLE;
|
||||||
|
elementId: string;
|
||||||
|
faceBubbleElement: FaceBubbleElement;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AvRecordElement {
|
||||||
|
type: number;
|
||||||
|
time: string;
|
||||||
|
text: string;
|
||||||
|
mainType: number;
|
||||||
|
hasRead: boolean;
|
||||||
|
extraType: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SendavRecordElement {
|
||||||
|
elementType: ElementType.AVRECORD;
|
||||||
|
elementId: string;
|
||||||
|
avRecordElement: AvRecordElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface YoloUserInfo {
|
||||||
|
uid: string;
|
||||||
|
result: number;
|
||||||
|
rank: number;
|
||||||
|
bizId: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SendInlineKeyboardElement {
|
||||||
|
elementType: ElementType.INLINEKEYBOARD;
|
||||||
|
elementId: string;
|
||||||
|
inlineKeyboardElement: {
|
||||||
|
rows: number;
|
||||||
|
botAppid: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface YoloGameResultElement {
|
||||||
|
UserInfo: YoloUserInfo[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SendYoloGameResultElement {
|
||||||
|
elementType: ElementType.YOLOGAMERESULT;
|
||||||
|
yoloGameResultElement: YoloGameResultElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface GiphyElement {
|
||||||
|
id: string;
|
||||||
|
isClip: boolean;
|
||||||
|
width: number;
|
||||||
|
height: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SendGiphyElement {
|
||||||
|
elementType: ElementType.GIPHY;
|
||||||
|
elementId: string;
|
||||||
|
giphyElement: GiphyElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SendWalletElement {
|
||||||
|
elementType: ElementType.UNKNOWN;//不做 设置位置
|
||||||
|
elementId: string;
|
||||||
|
walletElement: Record<string, never>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CalendarElement {
|
||||||
|
summary: string;
|
||||||
|
msg: string;
|
||||||
|
expireTimeMs: string;
|
||||||
|
schemaType: number;
|
||||||
|
schema: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SendCalendarElement {
|
||||||
|
elementType: ElementType.CALENDAR;
|
||||||
|
elementId: string;
|
||||||
|
calendarElement: CalendarElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SendliveGiftElement {
|
||||||
|
elementType: ElementType.LIVEGIFT;
|
||||||
|
elementId: string;
|
||||||
|
liveGiftElement: Record<string, never>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SendTextElement {
|
||||||
|
elementType: ElementType.TEXT;
|
||||||
|
elementId: string;
|
||||||
|
textElement: {
|
||||||
|
content: string;
|
||||||
|
atType: number;
|
||||||
|
atUid: string;
|
||||||
|
atTinyId: string;
|
||||||
|
atNtUid: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SendPttElement {
|
||||||
|
elementType: ElementType.PTT;
|
||||||
|
elementId: string;
|
||||||
|
pttElement: {
|
||||||
|
fileName: string;
|
||||||
|
filePath: string;
|
||||||
|
md5HexStr: string;
|
||||||
|
fileSize: number;
|
||||||
|
duration: number; // 单位是秒
|
||||||
|
formatType: number;
|
||||||
|
voiceType: number;
|
||||||
|
voiceChangeType: number;
|
||||||
|
canConvert2Text: boolean;
|
||||||
|
waveAmplitudes: number[];
|
||||||
|
fileSubId: string;
|
||||||
|
playState: number;
|
||||||
|
autoConvertText: number;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum PicType {
|
||||||
|
gif = 2000,
|
||||||
|
jpg = 1000
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum PicSubType {
|
||||||
|
normal = 0, // 普通图片,大图
|
||||||
|
face = 1 // 表情包小图
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum NTMsgAtType {
|
||||||
|
ATTYPEALL = 1,
|
||||||
|
ATTYPECATEGORY = 512,
|
||||||
|
ATTYPECHANNEL = 16,
|
||||||
|
ATTYPEME = 4,
|
||||||
|
ATTYPEONE = 2,
|
||||||
|
ATTYPEONLINE = 64,
|
||||||
|
ATTYPEROLE = 8,
|
||||||
|
ATTYPESUMMON = 32,
|
||||||
|
ATTYPESUMMONONLINE = 128,
|
||||||
|
ATTYPESUMMONROLE = 256,
|
||||||
|
ATTYPEUNKNOWN = 0
|
||||||
|
}
|
||||||
|
export interface SendPicElement {
|
||||||
|
elementType: ElementType.PIC;
|
||||||
|
elementId: string;
|
||||||
|
picElement: PicElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ReplyElement {
|
||||||
|
replayMsgSeq: string;
|
||||||
|
replayMsgId: string;
|
||||||
|
senderUin: string;
|
||||||
|
senderUinStr: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SendReplyElement {
|
||||||
|
elementType: ElementType.REPLY;
|
||||||
|
elementId: string;
|
||||||
|
replyElement: ReplyElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SendFaceElement {
|
||||||
|
elementType: ElementType.FACE;
|
||||||
|
elementId: string;
|
||||||
|
faceElement: FaceElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SendMarketFaceElement {
|
||||||
|
elementType: ElementType.MFACE;
|
||||||
|
marketFaceElement: MarketFaceElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SendstructLongMsgElement {
|
||||||
|
elementType: ElementType.STRUCTLONGMSG;
|
||||||
|
elementId: string;
|
||||||
|
structLongMsgElement: StructLongMsgElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface StructLongMsgElement {
|
||||||
|
xmlContent: string;
|
||||||
|
resId: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SendactionBarElement {
|
||||||
|
elementType: ElementType.ACTIONBAR;
|
||||||
|
elementId: string;
|
||||||
|
actionBarElement: {
|
||||||
|
rows: number;
|
||||||
|
botAppid: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ShareLocationElement {
|
||||||
|
text: string;
|
||||||
|
ext: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface sendShareLocationElement {
|
||||||
|
elementType: ElementType.SHARELOCATION;
|
||||||
|
elementId: string;
|
||||||
|
shareLocationElement?: ShareLocationElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FileElement {
|
||||||
|
fileMd5?: string;
|
||||||
|
fileName: string;
|
||||||
|
filePath: string;
|
||||||
|
fileSize: string;
|
||||||
|
picHeight?: number;
|
||||||
|
picWidth?: number;
|
||||||
|
folderId?: string;
|
||||||
|
picThumbPath?: Map<number, string>;
|
||||||
|
file10MMd5?: string;
|
||||||
|
fileSha?: string;
|
||||||
|
fileSha3?: string;
|
||||||
|
fileUuid?: string;
|
||||||
|
fileSubId?: string;
|
||||||
|
thumbFileSize?: number;
|
||||||
|
fileBizId?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SendFileElement {
|
||||||
|
elementType: ElementType.FILE;
|
||||||
|
elementId: string;
|
||||||
|
fileElement: FileElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SendVideoElement {
|
||||||
|
elementType: ElementType.VIDEO;
|
||||||
|
elementId: string;
|
||||||
|
videoElement: VideoElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SendArkElement {
|
||||||
|
elementType: ElementType.ARK;
|
||||||
|
elementId: string;
|
||||||
|
arkElement: ArkElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SendMarkdownElement {
|
||||||
|
elementType: ElementType.MARKDOWN;
|
||||||
|
elementId: string;
|
||||||
|
markdownElement: MarkdownElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type SendMessageElement = SendTextElement | SendPttElement |
|
||||||
|
SendPicElement | SendReplyElement | SendFaceElement | SendMarketFaceElement | SendFileElement |
|
||||||
|
SendVideoElement | SendArkElement | SendMarkdownElement | sendShareLocationElement;
|
||||||
|
|
||||||
|
export interface TextElement {
|
||||||
|
content: string;
|
||||||
|
atType: number;
|
||||||
|
atUid: string;
|
||||||
|
atTinyId: string;
|
||||||
|
atNtUid: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface MessageElement {
|
||||||
|
elementType: ElementType,
|
||||||
|
elementId: string,
|
||||||
|
extBufForUI: string,//"0x",
|
||||||
|
textElement?: TextElement;
|
||||||
|
faceElement?: FaceElement,
|
||||||
|
marketFaceElement?: MarkdownElement,
|
||||||
|
replyElement?: ReplyElement,
|
||||||
|
picElement?: PicElement,
|
||||||
|
pttElement?: PttElement,
|
||||||
|
videoElement?: VideoElement,
|
||||||
|
grayTipElement?: GrayTipElement,
|
||||||
|
arkElement?: ArkElement,
|
||||||
|
fileElement?: FileElement,
|
||||||
|
liveGiftElement?: null,
|
||||||
|
markdownElement?: MarkdownElement,
|
||||||
|
structLongMsgElement?: StructLongMsgElement,
|
||||||
|
multiForwardMsgElement?: MultiForwardMsgElement,
|
||||||
|
giphyElement?: GiphyElement,
|
||||||
|
walletElement?: null,
|
||||||
|
inlineKeyboardElement?: InlineKeyboardElement,
|
||||||
|
textGiftElement?: null,//????
|
||||||
|
calendarElement?: CalendarElement,
|
||||||
|
yoloGameResultElement?: YoloGameResultElement,
|
||||||
|
avRecordElement?: AvRecordElement,
|
||||||
|
structMsgElement?: null,
|
||||||
|
faceBubbleElement?: FaceBubbleElement,
|
||||||
|
shareLocationElement?: ShareLocationElement,
|
||||||
|
tofuRecordElement?: TofuRecordElement,
|
||||||
|
taskTopMsgElement?: TaskTopMsgElement,
|
||||||
|
recommendedMsgElement?: RecommendedMsgElement,
|
||||||
|
actionBarElement?: ActionBarElement
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum AtType {
|
||||||
|
notAt = 0,
|
||||||
|
atAll = 1,
|
||||||
|
atUser = 2
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum ChatType {
|
||||||
|
friend = 1,
|
||||||
|
group = 2,
|
||||||
|
chatDevice = 8, //移动设备?
|
||||||
|
temp = 100
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// 来自Android分析
|
||||||
|
export enum ChatType2 {
|
||||||
|
KCHATTYPEADELIE = 42,
|
||||||
|
KCHATTYPEBUDDYNOTIFY = 5,
|
||||||
|
KCHATTYPEC2C = 1,
|
||||||
|
KCHATTYPECIRCLE = 113,
|
||||||
|
KCHATTYPEDATALINE = 8,
|
||||||
|
KCHATTYPEDATALINEMQQ = 134,
|
||||||
|
KCHATTYPEDISC = 3,
|
||||||
|
KCHATTYPEFAV = 41,
|
||||||
|
KCHATTYPEGAMEMESSAGE = 105,
|
||||||
|
KCHATTYPEGAMEMESSAGEFOLDER = 116,
|
||||||
|
KCHATTYPEGROUP = 2,
|
||||||
|
KCHATTYPEGROUPBLESS = 133,
|
||||||
|
KCHATTYPEGROUPGUILD = 9,
|
||||||
|
KCHATTYPEGROUPHELPER = 7,
|
||||||
|
KCHATTYPEGROUPNOTIFY = 6,
|
||||||
|
KCHATTYPEGUILD = 4,
|
||||||
|
KCHATTYPEGUILDMETA = 16,
|
||||||
|
KCHATTYPEMATCHFRIEND = 104,
|
||||||
|
KCHATTYPEMATCHFRIENDFOLDER = 109,
|
||||||
|
KCHATTYPENEARBY = 106,
|
||||||
|
KCHATTYPENEARBYASSISTANT = 107,
|
||||||
|
KCHATTYPENEARBYFOLDER = 110,
|
||||||
|
KCHATTYPENEARBYHELLOFOLDER = 112,
|
||||||
|
KCHATTYPENEARBYINTERACT = 108,
|
||||||
|
KCHATTYPEQQNOTIFY = 132,
|
||||||
|
KCHATTYPERELATEACCOUNT = 131,
|
||||||
|
KCHATTYPESERVICEASSISTANT = 118,
|
||||||
|
KCHATTYPESERVICEASSISTANTSUB = 201,
|
||||||
|
KCHATTYPESQUAREPUBLIC = 115,
|
||||||
|
KCHATTYPESUBSCRIBEFOLDER = 30,
|
||||||
|
KCHATTYPETEMPADDRESSBOOK = 111,
|
||||||
|
KCHATTYPETEMPBUSSINESSCRM = 102,
|
||||||
|
KCHATTYPETEMPC2CFROMGROUP = 100,
|
||||||
|
KCHATTYPETEMPC2CFROMUNKNOWN = 99,
|
||||||
|
KCHATTYPETEMPFRIENDVERIFY = 101,
|
||||||
|
KCHATTYPETEMPNEARBYPRO = 119,
|
||||||
|
KCHATTYPETEMPPUBLICACCOUNT = 103,
|
||||||
|
KCHATTYPETEMPWPA = 117,
|
||||||
|
KCHATTYPEUNKNOWN = 0,
|
||||||
|
KCHATTYPEWEIYUN = 40,
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PttElement {
|
||||||
|
canConvert2Text: boolean;
|
||||||
|
duration: number; // 秒数
|
||||||
|
fileBizId: null;
|
||||||
|
fileId: number; // 0
|
||||||
|
fileName: string; // "e4d09c784d5a2abcb2f9980bdc7acfe6.amr"
|
||||||
|
filePath: string; // "/Users//Library/Containers/com.tencent.qq/Data/Library/Application Support/QQ/nt_qq_a6b15c9820595d25a56c1633ce19ad40/nt_data/Ptt/2023-11/Ori/e4d09c784d5a2abcb2f9980bdc7acfe6.amr"
|
||||||
|
fileSize: string; // "4261"
|
||||||
|
fileSubId: string; // "0"
|
||||||
|
fileUuid: string; // "90j3z7rmRphDPrdVgP9udFBaYar#oK0TWZIV"
|
||||||
|
formatType: string; // 1
|
||||||
|
invalidState: number; // 0
|
||||||
|
md5HexStr: string; // "e4d09c784d5a2abcb2f9980bdc7acfe6"
|
||||||
|
playState: number; // 0
|
||||||
|
progress: number; // 0
|
||||||
|
text: string; // ""
|
||||||
|
transferStatus: number; // 0
|
||||||
|
translateStatus: number; // 0
|
||||||
|
voiceChangeType: number; // 0
|
||||||
|
voiceType: number; // 0
|
||||||
|
waveAmplitudes: number[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ArkElement {
|
||||||
|
bytesData: string;
|
||||||
|
linkInfo: null;
|
||||||
|
subElementType: null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const IMAGE_HTTP_HOST = 'https://gchat.qpic.cn';
|
||||||
|
export const IMAGE_HTTP_HOST_NT = 'https://multimedia.nt.qq.com.cn';
|
||||||
|
|
||||||
|
export interface PicElement {
|
||||||
|
md5HexStr?: string;
|
||||||
|
fileSize: number | string;//number
|
||||||
|
picWidth: number;
|
||||||
|
picHeight: number;
|
||||||
|
fileName: string;
|
||||||
|
sourcePath: string;
|
||||||
|
original: boolean;
|
||||||
|
picType: PicType;
|
||||||
|
picSubType?: PicSubType;
|
||||||
|
fileUuid: string;
|
||||||
|
fileSubId: string;
|
||||||
|
thumbFileSize: number;
|
||||||
|
summary: string;
|
||||||
|
thumbPath: Map<number, string>;
|
||||||
|
originImageMd5?: string;
|
||||||
|
originImageUrl?: string; // http url, 没有host,host是https://gchat.qpic.cn/, 带download参数的是https://multimedia.nt.qq.com.cn
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum GrayTipElementSubType {
|
||||||
|
INVITE_NEW_MEMBER = 12,
|
||||||
|
MEMBER_NEW_TITLE = 17
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface GrayTipElement {
|
||||||
|
subElementType: GrayTipElementSubType;
|
||||||
|
revokeElement: {
|
||||||
|
operatorRole: string;
|
||||||
|
operatorUid: string;
|
||||||
|
operatorNick: string;
|
||||||
|
operatorRemark: string;
|
||||||
|
operatorMemRemark?: string;
|
||||||
|
wording: string; // 自定义的撤回提示语
|
||||||
|
};
|
||||||
|
aioOpGrayTipElement: TipAioOpGrayTipElement;
|
||||||
|
groupElement: TipGroupElement;
|
||||||
|
xmlElement: {
|
||||||
|
content: string;
|
||||||
|
templId: string;
|
||||||
|
};
|
||||||
|
jsonGrayTipElement: {
|
||||||
|
busiId?: number;
|
||||||
|
jsonStr: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum FaceType {
|
||||||
|
normal = 1, // 小黄脸
|
||||||
|
normal2 = 2, // 新小黄脸, 从faceIndex 222开始?
|
||||||
|
dice = 3 // 骰子
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum FaceIndex {
|
||||||
|
dice = 358,
|
||||||
|
RPS = 359 // 石头剪刀布
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FaceElement {
|
||||||
|
faceIndex: number;
|
||||||
|
faceType: FaceType;
|
||||||
|
faceText?: string;
|
||||||
|
packId?: string;
|
||||||
|
stickerId?: string;
|
||||||
|
sourceType?: number;
|
||||||
|
stickerType?: number;
|
||||||
|
resultId?: string;
|
||||||
|
surpriseId?: string;
|
||||||
|
randomType?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface MarketFaceElement {
|
||||||
|
emojiPackageId: number;
|
||||||
|
faceName: string;
|
||||||
|
emojiId: string;
|
||||||
|
key: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface VideoElement {
|
||||||
|
filePath: string;
|
||||||
|
fileName: string;
|
||||||
|
videoMd5?: string;
|
||||||
|
thumbMd5?: string;
|
||||||
|
fileTime?: number; // second
|
||||||
|
thumbSize?: number; // byte
|
||||||
|
fileFormat?: viedo_type; // 2表示mp4 参考下面条目
|
||||||
|
fileSize?: string; // byte
|
||||||
|
thumbWidth?: number;
|
||||||
|
thumbHeight?: number;
|
||||||
|
busiType?: 0; //
|
||||||
|
subBusiType?: 0; // 未知
|
||||||
|
thumbPath?: Map<number, any>;
|
||||||
|
transferStatus?: 0; // 未知
|
||||||
|
progress?: 0; // 下载进度?
|
||||||
|
invalidState?: 0; // 未知
|
||||||
|
fileUuid?: string; // 可以用于下载链接?
|
||||||
|
fileSubId?: string;
|
||||||
|
fileBizId?: null;
|
||||||
|
originVideoMd5?: string;
|
||||||
|
import_rich_media_context?: null;
|
||||||
|
sourceVideoCodecFormat?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
// export enum busiType{
|
||||||
|
// public static final int CREATOR_SHARE_ADV_XWORLD = 21;
|
||||||
|
// public static final int MINI_APP_MINI_GAME = 11;
|
||||||
|
// public static final int OFFICIAL_ACCOUNT_ADV = 4;
|
||||||
|
// public static final int OFFICIAL_ACCOUNT_ADV_GAME = 8;
|
||||||
|
// public static final int OFFICIAL_ACCOUNT_ADV_SHOP = 9;
|
||||||
|
// public static final int OFFICIAL_ACCOUNT_ADV_VIP = 7;
|
||||||
|
// public static final int OFFICIAL_ACCOUNT_LAYER_MASK_ADV = 14;
|
||||||
|
// public static final int OFFICIAL_ACCOUNT_SPORT = 13;
|
||||||
|
// public static final int OFFICIAL_ACCOUNT_TIAN_QI = 10;
|
||||||
|
// public static final int PC_QQTAB_ADV = 18;
|
||||||
|
// public static final int QIQIAOBAN_SDK = 15;
|
||||||
|
// public static final int QQ_CPS = 16;
|
||||||
|
// public static final int QQ_WALLET_CPS = 17;
|
||||||
|
// public static final int QZONE_FEEDS = 0;
|
||||||
|
// public static final int QZONE_PHOTO_TAIL = 2;
|
||||||
|
// public static final int QZONE_VIDEO_LAYER = 1;
|
||||||
|
// public static final int REWARD_GIFT_ADV = 6;
|
||||||
|
// public static final int REWARD_GROUPGIFT_ADV = 12;
|
||||||
|
// public static final int REWARD_PERSONAL_ADV = 5;
|
||||||
|
// public static final int WEISEE_OFFICIAL_ACCOUNT = 3;
|
||||||
|
// public static final int X_WORLD_CREATOR_ADV = 20;
|
||||||
|
// public static final int X_WORLD_QZONE_LAYER = 22;
|
||||||
|
// public static final int X_WORLD_VIDEO_ADV = 19;
|
||||||
|
|
||||||
|
// }
|
||||||
|
// export enum CategoryBusiType {
|
||||||
|
// _KCateBusiTypeDefault = 0,
|
||||||
|
// _kCateBusiTypeFaceCluster = 1,
|
||||||
|
// _kCateBusiTypeLabelCluster = 4,
|
||||||
|
// _kCateBusiTypeMonthCluster = 16,
|
||||||
|
// _kCateBusiTypePoiCluster = 2,
|
||||||
|
// _kCateBusiTypeYearCluster = 8,
|
||||||
|
// }
|
||||||
|
export enum viedo_type {
|
||||||
|
VIDEO_FORMAT_AFS = 7,
|
||||||
|
VIDEO_FORMAT_AVI = 1,
|
||||||
|
VIDEO_FORMAT_MKV = 4,
|
||||||
|
VIDEO_FORMAT_MOD = 9,
|
||||||
|
VIDEO_FORMAT_MOV = 8,
|
||||||
|
VIDEO_FORMAT_MP4 = 2,
|
||||||
|
VIDEO_FORMAT_MTS = 11,
|
||||||
|
VIDEO_FORMAT_RM = 6,
|
||||||
|
VIDEO_FORMAT_RMVB = 5,
|
||||||
|
VIDEO_FORMAT_TS = 10,
|
||||||
|
VIDEO_FORMAT_WMV = 3,
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface MarkdownElement {
|
||||||
|
content: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface InlineKeyboardElementRowButton {
|
||||||
|
id: string;
|
||||||
|
label: string;
|
||||||
|
visitedLabel: string;
|
||||||
|
style: 1; // 未知
|
||||||
|
type: 2; // 未知
|
||||||
|
clickLimit: 0; // 未知
|
||||||
|
unsupportTips: string;
|
||||||
|
data: string;
|
||||||
|
atBotShowChannelList: boolean;
|
||||||
|
permissionType: number;
|
||||||
|
specifyRoleIds: [];
|
||||||
|
specifyTinyids: [];
|
||||||
|
isReply: false;
|
||||||
|
anchor: 0;
|
||||||
|
enter: false;
|
||||||
|
subscribeDataTemplateIds: [];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface InlineKeyboardElement {
|
||||||
|
rows: [{
|
||||||
|
buttons: InlineKeyboardElementRowButton[]
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TipAioOpGrayTipElement { // 这是什么提示来着?
|
||||||
|
operateType: number;
|
||||||
|
peerUid: string;
|
||||||
|
fromGrpCodeOfTmpChat: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum TipGroupElementType {
|
||||||
|
memberIncrease = 1,
|
||||||
|
kicked = 3, // 被移出群
|
||||||
|
ban = 8
|
||||||
|
}
|
||||||
|
|
||||||
|
// public final class MemberAddShowType {
|
||||||
|
// public static final int KOTHERADD = 0;
|
||||||
|
// public static final int KOTHERADDBYOTHERQRCODE = 2;
|
||||||
|
// public static final int KOTHERADDBYYOURQRCODE = 3;
|
||||||
|
// public static final int KOTHERINVITEOTHER = 5;
|
||||||
|
// public static final int KOTHERINVITEYOU = 6;
|
||||||
|
// public static final int KYOUADD = 1;
|
||||||
|
// public static final int KYOUADDBYOTHERQRCODE = 4;
|
||||||
|
// public static final int KYOUALREADYMEMBER = 8;
|
||||||
|
// public static final int KYOUINVITEOTHER = 7;
|
||||||
|
// }
|
||||||
|
export interface TipGroupElement {
|
||||||
|
type: TipGroupElementType; // 1是表示有人加入群; 自己加入群也会收到这个
|
||||||
|
role: 0; // 暂时不知
|
||||||
|
groupName: string; // 暂时获取不到
|
||||||
|
memberUid: string;
|
||||||
|
memberNick: string;
|
||||||
|
memberRemark: string;
|
||||||
|
adminUid: string;
|
||||||
|
adminNick: string;
|
||||||
|
adminRemark: string;
|
||||||
|
createGroup: null;
|
||||||
|
memberAdd?: {
|
||||||
|
showType: 1;
|
||||||
|
otherAdd: null;
|
||||||
|
otherAddByOtherQRCode: null;
|
||||||
|
otherAddByYourQRCode: null;
|
||||||
|
youAddByOtherQRCode: null;
|
||||||
|
otherInviteOther: null;
|
||||||
|
otherInviteYou: null;
|
||||||
|
youInviteOther: null
|
||||||
|
};
|
||||||
|
shutUp?: {
|
||||||
|
curTime: string;
|
||||||
|
duration: string; // 禁言时间,秒
|
||||||
|
admin: {
|
||||||
|
uid: string;
|
||||||
|
card: string;
|
||||||
|
name: string;
|
||||||
|
role: GroupMemberRole
|
||||||
|
};
|
||||||
|
member: {
|
||||||
|
uid: string
|
||||||
|
card: string;
|
||||||
|
name: string;
|
||||||
|
role: GroupMemberRole
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface MultiForwardMsgElement {
|
||||||
|
xmlContent: string; // xml格式的消息内容
|
||||||
|
resId: string;
|
||||||
|
fileName: string;
|
||||||
|
}
|
||||||
|
export enum NTSubMsgType {
|
||||||
|
KMSGSUBTYPEARKGROUPANNOUNCE = 3,
|
||||||
|
KMSGSUBTYPEARKGROUPANNOUNCECONFIRMREQUIRED = 4,
|
||||||
|
KMSGSUBTYPEARKGROUPGIFTATME = 5,
|
||||||
|
KMSGSUBTYPEARKGROUPTASKATALL = 6,
|
||||||
|
KMSGSUBTYPEARKMULTIMSG = 7,
|
||||||
|
KMSGSUBTYPEARKNORMAL = 0,
|
||||||
|
KMSGSUBTYPEARKTENCENTDOCFROMMINIAPP = 1,
|
||||||
|
KMSGSUBTYPEARKTENCENTDOCFROMPLUSPANEL = 2,
|
||||||
|
KMSGSUBTYPEEMOTICON = 15,
|
||||||
|
KMSGSUBTYPEFILEAPP = 11,
|
||||||
|
KMSGSUBTYPEFILEAUDIO = 3,
|
||||||
|
KMSGSUBTYPEFILEDOC = 4,
|
||||||
|
KMSGSUBTYPEFILEEXCEL = 6,
|
||||||
|
KMSGSUBTYPEFILEFOLDER = 13,
|
||||||
|
KMSGSUBTYPEFILEHTML = 10,
|
||||||
|
KMSGSUBTYPEFILEIPA = 14,
|
||||||
|
KMSGSUBTYPEFILENORMAL = 0,
|
||||||
|
KMSGSUBTYPEFILEPDF = 7,
|
||||||
|
KMSGSUBTYPEFILEPIC = 1,
|
||||||
|
KMSGSUBTYPEFILEPPT = 5,
|
||||||
|
KMSGSUBTYPEFILEPSD = 12,
|
||||||
|
KMSGSUBTYPEFILETXT = 8,
|
||||||
|
KMSGSUBTYPEFILEVIDEO = 2,
|
||||||
|
KMSGSUBTYPEFILEZIP = 9,
|
||||||
|
KMSGSUBTYPELINK = 5,
|
||||||
|
KMSGSUBTYPEMARKETFACE = 1,
|
||||||
|
KMSGSUBTYPEMIXEMOTICON = 7,
|
||||||
|
KMSGSUBTYPEMIXFACE = 3,
|
||||||
|
KMSGSUBTYPEMIXMARKETFACE = 2,
|
||||||
|
KMSGSUBTYPEMIXPIC = 1,
|
||||||
|
KMSGSUBTYPEMIXREPLY = 4,
|
||||||
|
KMSGSUBTYPEMIXTEXT = 0,
|
||||||
|
KMSGSUBTYPETENCENTDOC = 6
|
||||||
|
}
|
||||||
|
export interface RawMessage {
|
||||||
|
parentMsgPeer: Peer;
|
||||||
|
parentMsgIdList: string[];
|
||||||
|
id?: number;//扩展字段 用于处理OB11 ID
|
||||||
|
guildId: string;
|
||||||
|
msgRandom: string;
|
||||||
|
|
||||||
|
|
||||||
|
msgId: string;
|
||||||
|
|
||||||
|
// 时间戳,秒
|
||||||
|
msgTime: string;
|
||||||
|
|
||||||
|
msgSeq: string;
|
||||||
|
msgType: NTMsgType;
|
||||||
|
subMsgType: NTSubMsgType;
|
||||||
|
senderUid: string;
|
||||||
|
senderUin: string; // 发送者QQ号
|
||||||
|
peerUid: string; // 群号 或者 QQ uid
|
||||||
|
peerUin: string; // 群号 或者 发送者QQ号
|
||||||
|
sendNickName: string;
|
||||||
|
sendMemberName?: string; // 发送者群名片
|
||||||
|
chatType: ChatType;
|
||||||
|
sendStatus?: number; // 消息状态,别人发的2是已撤回,自己发的2是已发送
|
||||||
|
recallTime: string; // 撤回时间, "0"是没有撤回
|
||||||
|
records: RawMessage[];
|
||||||
|
elements: {
|
||||||
|
elementId: string;
|
||||||
|
elementType: ElementType;
|
||||||
|
replyElement: {
|
||||||
|
sourceMsgIdInRecords: string;
|
||||||
|
senderUid: string; // 原消息发送者QQ号
|
||||||
|
sourceMsgIsIncPic: boolean; // 原消息是否有图片
|
||||||
|
sourceMsgText: string;
|
||||||
|
replayMsgSeq: string; // 源消息的msgSeq,可以通过这个找到源消息的msgId
|
||||||
|
};
|
||||||
|
textElement: {
|
||||||
|
atType: AtType;
|
||||||
|
atUid: string; // QQ号
|
||||||
|
content: string;
|
||||||
|
atNtUid: string; // uid号
|
||||||
|
};
|
||||||
|
picElement: PicElement;
|
||||||
|
pttElement: PttElement;
|
||||||
|
arkElement: ArkElement;
|
||||||
|
grayTipElement: GrayTipElement;
|
||||||
|
faceElement: FaceElement;
|
||||||
|
videoElement: VideoElement;
|
||||||
|
fileElement: FileElement;
|
||||||
|
marketFaceElement: MarketFaceElement;
|
||||||
|
inlineKeyboardElement: InlineKeyboardElement;
|
||||||
|
markdownElement: MarkdownElement;
|
||||||
|
multiForwardMsgElement: MultiForwardMsgElement;
|
||||||
|
}[];
|
||||||
|
}
|
||||||
120
src/core/entities/notify.ts
Normal file
120
src/core/entities/notify.ts
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
export enum GroupNotifyTypes {
|
||||||
|
INVITE_ME = 1,
|
||||||
|
INVITED_JOIN = 4, // 有人接受了邀请入群
|
||||||
|
JOIN_REQUEST = 7,
|
||||||
|
ADMIN_SET = 8,
|
||||||
|
KICK_MEMBER = 9,
|
||||||
|
MEMBER_EXIT = 11, // 主动退出
|
||||||
|
ADMIN_UNSET = 12,
|
||||||
|
ADMIN_UNSET_OTHER = 13, // 其他人取消管理员
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface GroupNotifies {
|
||||||
|
doubt: boolean;
|
||||||
|
nextStartSeq: string;
|
||||||
|
notifies: GroupNotify[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum GroupNotifyStatus {
|
||||||
|
IGNORE = 0,
|
||||||
|
WAIT_HANDLE = 1,
|
||||||
|
APPROVE = 2,
|
||||||
|
REJECT = 3
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface GroupNotify {
|
||||||
|
time: number; // 自己添加的字段,时间戳,毫秒, 用于判断收到短时间内收到重复的notify
|
||||||
|
seq: string; // 唯一标识符,转成数字再除以1000应该就是时间戳?
|
||||||
|
type: GroupNotifyTypes;
|
||||||
|
status: GroupNotifyStatus; // 0是已忽略?,1是未处理,2是已同意
|
||||||
|
group: { groupCode: string; groupName: string };
|
||||||
|
user1: { uid: string; nickName: string }; // 被设置管理员的人
|
||||||
|
user2: { uid: string; nickName: string }; // 操作者
|
||||||
|
actionUser: { uid: string; nickName: string }; //未知
|
||||||
|
actionTime: string;
|
||||||
|
invitationExt: {
|
||||||
|
srcType: number; // 0?未知
|
||||||
|
groupCode: string; waitStatus: number
|
||||||
|
};
|
||||||
|
postscript: string; // 加群用户填写的验证信息
|
||||||
|
repeatSeqs: [];
|
||||||
|
warningTips: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum GroupRequestOperateTypes {
|
||||||
|
approve = 1,
|
||||||
|
reject = 2
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum BuddyReqType {
|
||||||
|
KMEINITIATOR,
|
||||||
|
KPEERINITIATOR,
|
||||||
|
KMEAGREED,
|
||||||
|
KMEAGREEDANDADDED,
|
||||||
|
KPEERAGREED,
|
||||||
|
KPEERAGREEDANDADDED,
|
||||||
|
KPEERREFUSED,
|
||||||
|
KMEREFUSED,
|
||||||
|
KMEIGNORED,
|
||||||
|
KMEAGREEANYONE,
|
||||||
|
KMESETQUESTION,
|
||||||
|
KMEAGREEANDADDFAILED,
|
||||||
|
KMSGINFO,
|
||||||
|
KMEINITIATORWAITPEERCONFIRM
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FriendRequest {
|
||||||
|
isInitiator?: boolean;
|
||||||
|
isDecide: boolean;
|
||||||
|
friendUid: string;
|
||||||
|
reqType: BuddyReqType,
|
||||||
|
reqTime: string; // 时间戳;秒
|
||||||
|
extWords: string; // 申请人填写的验证消息
|
||||||
|
isUnread: boolean;
|
||||||
|
friendNick: string;
|
||||||
|
sourceId: number;
|
||||||
|
groupCode: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FriendRequestNotify {
|
||||||
|
unreadNums: number;
|
||||||
|
buddyReqs: FriendRequest[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum MemberExtSourceType {
|
||||||
|
DEFAULTTYPE = 0,
|
||||||
|
TITLETYPE = 1,
|
||||||
|
NEWGROUPTYPE = 2,
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface GroupExtParam {
|
||||||
|
groupCode: string;
|
||||||
|
seq: string;
|
||||||
|
beginUin: string;
|
||||||
|
dataTime: string;
|
||||||
|
uinList: Array<string>;
|
||||||
|
uinNum: string;
|
||||||
|
groupType: string;
|
||||||
|
richCardNameVer: string;
|
||||||
|
sourceType: MemberExtSourceType;
|
||||||
|
memberExtFilter: {
|
||||||
|
memberLevelInfoUin: number
|
||||||
|
memberLevelInfoPoint: number
|
||||||
|
memberLevelInfoActiveDay: number
|
||||||
|
memberLevelInfoLevel: number
|
||||||
|
memberLevelInfoName: number
|
||||||
|
levelName: number
|
||||||
|
dataTime: number
|
||||||
|
userShowFlag: number
|
||||||
|
sysShowFlag: number
|
||||||
|
timeToUpdate: number
|
||||||
|
nickName: number
|
||||||
|
specialTitle: number
|
||||||
|
levelNameNew: number
|
||||||
|
userShowFlagNew: number
|
||||||
|
msgNeedField: number
|
||||||
|
cmdUinFlagExt3Grocery: number
|
||||||
|
memberIcon: number
|
||||||
|
memberInfoSeq: number
|
||||||
|
};
|
||||||
|
}
|
||||||
24
src/core/entities/sign.ts
Normal file
24
src/core/entities/sign.ts
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
export interface IdMusicSignPostData {
|
||||||
|
type: 'qq' | '163',
|
||||||
|
id: string | number,
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CustomMusicSignPostData {
|
||||||
|
type: 'custom',
|
||||||
|
url: string,
|
||||||
|
audio: string,
|
||||||
|
title: string,
|
||||||
|
image?: string,
|
||||||
|
singer?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface MiniAppLuaJsonType {
|
||||||
|
prompt: string,
|
||||||
|
title: string,
|
||||||
|
preview: string,
|
||||||
|
jumpUrl: string,
|
||||||
|
tag: string,
|
||||||
|
tagIcon: string,
|
||||||
|
source: string,
|
||||||
|
sourcelogo: string,
|
||||||
|
}
|
||||||
18
src/core/entities/system.ts
Normal file
18
src/core/entities/system.ts
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import { SelfInfo } from './user';
|
||||||
|
|
||||||
|
export interface LineDevice {
|
||||||
|
instanceId: number;
|
||||||
|
clientType: number;
|
||||||
|
devUid: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface OBLineDevice {
|
||||||
|
app_id: string;
|
||||||
|
device_name: string;
|
||||||
|
device_kind: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CoreCache {
|
||||||
|
selfInfo: SelfInfo,
|
||||||
|
DeviceList: OBLineDevice[]
|
||||||
|
}
|
||||||
365
src/core/entities/user.ts
Normal file
365
src/core/entities/user.ts
Normal file
@@ -0,0 +1,365 @@
|
|||||||
|
export enum Sex {
|
||||||
|
male = 1,
|
||||||
|
female = 2,
|
||||||
|
unknown = 255,
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface BuddyCategoryType {
|
||||||
|
categoryId: number;
|
||||||
|
categroyName: string;
|
||||||
|
categroyMbCount: number;
|
||||||
|
buddyList: User[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CoreInfo {
|
||||||
|
uid: string;
|
||||||
|
uin: string;
|
||||||
|
nick: string;
|
||||||
|
remark: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface BaseInfo {
|
||||||
|
qid: string;
|
||||||
|
longNick: string;
|
||||||
|
birthday_year: number;
|
||||||
|
birthday_month: number;
|
||||||
|
birthday_day: number;
|
||||||
|
age: number;
|
||||||
|
sex: number;
|
||||||
|
eMail: string;
|
||||||
|
phoneNum: string;
|
||||||
|
categoryId: number;
|
||||||
|
richTime: number;
|
||||||
|
richBuffer: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface MusicInfo {
|
||||||
|
buf: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface VideoBizInfo {
|
||||||
|
cid: string;
|
||||||
|
tvUrl: string;
|
||||||
|
synchType: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface VideoInfo {
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ExtOnlineBusinessInfo {
|
||||||
|
buf: string;
|
||||||
|
customStatus: any;
|
||||||
|
videoBizInfo: VideoBizInfo;
|
||||||
|
videoInfo: VideoInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ExtBuffer {
|
||||||
|
buf: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface UserStatus {
|
||||||
|
uid: string;
|
||||||
|
uin: string;
|
||||||
|
status: number;
|
||||||
|
extStatus: number;
|
||||||
|
batteryStatus: number;
|
||||||
|
termType: number;
|
||||||
|
netType: number;
|
||||||
|
iconType: number;
|
||||||
|
customStatus: any;
|
||||||
|
setTime: string;
|
||||||
|
specialFlag: number;
|
||||||
|
abiFlag: number;
|
||||||
|
eNetworkType: number;
|
||||||
|
showName: string;
|
||||||
|
termDesc: string;
|
||||||
|
musicInfo: MusicInfo;
|
||||||
|
extOnlineBusinessInfo: ExtOnlineBusinessInfo;
|
||||||
|
extBuffer: ExtBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface PrivilegeIcon {
|
||||||
|
jumpUrl: string;
|
||||||
|
openIconList: any[];
|
||||||
|
closeIconList: any[];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface VasInfo {
|
||||||
|
vipFlag: boolean;
|
||||||
|
yearVipFlag: boolean;
|
||||||
|
svipFlag: boolean;
|
||||||
|
vipLevel: number;
|
||||||
|
bigClub: boolean;
|
||||||
|
bigClubLevel: number;
|
||||||
|
nameplateVipType: number;
|
||||||
|
grayNameplateFlag: number;
|
||||||
|
superVipTemplateId: number;
|
||||||
|
diyFontId: number;
|
||||||
|
pendantId: number;
|
||||||
|
pendantDiyId: number;
|
||||||
|
faceId: number;
|
||||||
|
vipFont: number;
|
||||||
|
vipFontType: number;
|
||||||
|
magicFont: number;
|
||||||
|
fontEffect: number;
|
||||||
|
newLoverDiamondFlag: number;
|
||||||
|
extendNameplateId: number;
|
||||||
|
diyNameplateIDs: any[];
|
||||||
|
vipStartFlag: number;
|
||||||
|
vipDataFlag: number;
|
||||||
|
gameNameplateId: string;
|
||||||
|
gameLastLoginTime: string;
|
||||||
|
gameRank: number;
|
||||||
|
gameIconShowFlag: boolean;
|
||||||
|
gameCardId: string;
|
||||||
|
vipNameColorId: string;
|
||||||
|
privilegeIcon: PrivilegeIcon;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface RelationFlags {
|
||||||
|
topTime: string;
|
||||||
|
isBlock: boolean;
|
||||||
|
isMsgDisturb: boolean;
|
||||||
|
isSpecialCareOpen: boolean;
|
||||||
|
isSpecialCareZone: boolean;
|
||||||
|
ringId: string;
|
||||||
|
isBlocked: boolean;
|
||||||
|
recommendImgFlag: number;
|
||||||
|
disableEmojiShortCuts: number;
|
||||||
|
qidianMasterFlag: number;
|
||||||
|
qidianCrewFlag: number;
|
||||||
|
qidianCrewFlag2: number;
|
||||||
|
isHideQQLevel: number;
|
||||||
|
isHidePrivilegeIcon: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
interface CommonExt {
|
||||||
|
constellation: number;
|
||||||
|
shengXiao: number;
|
||||||
|
kBloodType: number;
|
||||||
|
homeTown: string;
|
||||||
|
makeFriendCareer: number;
|
||||||
|
pos: string;
|
||||||
|
college: string;
|
||||||
|
country: string;
|
||||||
|
province: string;
|
||||||
|
city: string;
|
||||||
|
postCode: string;
|
||||||
|
address: string;
|
||||||
|
regTime: number;
|
||||||
|
interest: string;
|
||||||
|
labels: any[];
|
||||||
|
qqLevel: QQLevel;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Pic {
|
||||||
|
picId: string;
|
||||||
|
picTime: number;
|
||||||
|
picUrlMap: Record<string, string>;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface PhotoWall {
|
||||||
|
picList: Pic[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SimpleInfo {
|
||||||
|
uid?: string;
|
||||||
|
uin?: string;
|
||||||
|
coreInfo: CoreInfo;
|
||||||
|
baseInfo: BaseInfo;
|
||||||
|
status: UserStatus | null;
|
||||||
|
vasInfo: VasInfo | null;
|
||||||
|
relationFlags: RelationFlags | null;
|
||||||
|
otherFlags: any | null;
|
||||||
|
intimate: any | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type FriendV2 = SimpleInfo;
|
||||||
|
|
||||||
|
export interface SelfStatusInfo {
|
||||||
|
uid: string;
|
||||||
|
status: number;
|
||||||
|
extStatus: number;
|
||||||
|
termType: number;
|
||||||
|
netType: number;
|
||||||
|
iconType: number;
|
||||||
|
customStatus: any;
|
||||||
|
setTime: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UserDetailInfoListenerArg {
|
||||||
|
uid: string;
|
||||||
|
uin: string;
|
||||||
|
simpleInfo: SimpleInfo;
|
||||||
|
commonExt: CommonExt;
|
||||||
|
photoWall: PhotoWall;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ModifyProfileParams {
|
||||||
|
nick: string,
|
||||||
|
longNick: string,
|
||||||
|
sex: Sex,
|
||||||
|
birthday: { birthday_year: string, birthday_month: string, birthday_day: string },
|
||||||
|
location: any//undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface BuddyProfileLikeReq {
|
||||||
|
friendUids: string[];
|
||||||
|
basic: number;
|
||||||
|
vote: number;
|
||||||
|
favorite: number;
|
||||||
|
userProfile: number;
|
||||||
|
type: number;
|
||||||
|
start: number;
|
||||||
|
limit: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface QQLevel {
|
||||||
|
crownNum: number;
|
||||||
|
sunNum: number;
|
||||||
|
moonNum: number;
|
||||||
|
starNum: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface User {
|
||||||
|
uid: string; // 加密的字符串
|
||||||
|
uin: string; // QQ号
|
||||||
|
nick: string;
|
||||||
|
avatarUrl?: string;
|
||||||
|
longNick?: string; // 签名
|
||||||
|
remark?: string;
|
||||||
|
sex?: Sex;
|
||||||
|
qqLevel?: QQLevel;
|
||||||
|
qid?: string;
|
||||||
|
birthday_year?: number;
|
||||||
|
birthday_month?: number;
|
||||||
|
birthday_day?: number;
|
||||||
|
topTime?: string;
|
||||||
|
constellation?: number;
|
||||||
|
shengXiao?: number;
|
||||||
|
kBloodType?: number;
|
||||||
|
homeTown?: string; //"0-0-0";
|
||||||
|
makeFriendCareer?: number;
|
||||||
|
pos?: string;
|
||||||
|
eMail?: string;
|
||||||
|
phoneNum?: string;
|
||||||
|
college?: string;
|
||||||
|
country?: string;
|
||||||
|
province?: string;
|
||||||
|
city?: string;
|
||||||
|
postCode?: string;
|
||||||
|
address?: string;
|
||||||
|
isBlock?: boolean;
|
||||||
|
isSpecialCareOpen?: boolean;
|
||||||
|
isSpecialCareZone?: boolean;
|
||||||
|
ringId?: string;
|
||||||
|
regTime?: number;
|
||||||
|
interest?: string;
|
||||||
|
labels?: string[];
|
||||||
|
isHideQQLevel?: number;
|
||||||
|
privilegeIcon?: {
|
||||||
|
jumpUrl: string;
|
||||||
|
openIconList: unknown[];
|
||||||
|
closeIconList: unknown[]
|
||||||
|
};
|
||||||
|
photoWall?: {
|
||||||
|
picList: unknown[]
|
||||||
|
};
|
||||||
|
vipFlag?: boolean;
|
||||||
|
yearVipFlag?: boolean;
|
||||||
|
svipFlag?: boolean;
|
||||||
|
vipLevel?: number;
|
||||||
|
status?: number;
|
||||||
|
qidianMasterFlag?: number;
|
||||||
|
qidianCrewFlag?: number;
|
||||||
|
qidianCrewFlag2?: number;
|
||||||
|
extStatus?: number;
|
||||||
|
recommendImgFlag?: number;
|
||||||
|
disableEmojiShortCuts?: number;
|
||||||
|
pendantId?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SelfInfo extends User {
|
||||||
|
online?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Friend extends User {
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum BizKey {
|
||||||
|
KPRIVILEGEICON,
|
||||||
|
KPHOTOWALL
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UserDetailInfoByUinV2 {
|
||||||
|
result: number,
|
||||||
|
errMsg: string,
|
||||||
|
detail: {
|
||||||
|
uid: string,
|
||||||
|
uin: string,
|
||||||
|
simpleInfo: SimpleInfo,
|
||||||
|
commonExt: CommonExt,
|
||||||
|
photoWall: null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UserDetailInfoByUin {
|
||||||
|
result: number,
|
||||||
|
errMsg: string,
|
||||||
|
info: {
|
||||||
|
uid: string,//这个没办法用
|
||||||
|
qid: string,
|
||||||
|
uin: string,
|
||||||
|
nick: string,
|
||||||
|
remark: string,
|
||||||
|
longNick: string,
|
||||||
|
avatarUrl: string,
|
||||||
|
birthday_year: number,
|
||||||
|
birthday_month: number,
|
||||||
|
birthday_day: number,
|
||||||
|
sex: number,//0
|
||||||
|
topTime: string,
|
||||||
|
constellation: number,
|
||||||
|
shengXiao: number,
|
||||||
|
kBloodType: number,
|
||||||
|
homeTown: string,
|
||||||
|
makeFriendCareer: number,
|
||||||
|
pos: string,
|
||||||
|
eMail: string,
|
||||||
|
phoneNum: string,
|
||||||
|
college: string,
|
||||||
|
country: string,
|
||||||
|
province: string,
|
||||||
|
city: string,
|
||||||
|
postCode: string,
|
||||||
|
address: string,
|
||||||
|
isBlock: boolean,
|
||||||
|
isSpecialCareOpen: boolean,
|
||||||
|
isSpecialCareZone: boolean,
|
||||||
|
ringId: string,
|
||||||
|
regTime: number,
|
||||||
|
interest: string,
|
||||||
|
termType: number,
|
||||||
|
labels: any[],
|
||||||
|
qqLevel: { crownNum: number, sunNum: number, moonNum: number, starNum: number },
|
||||||
|
isHideQQLevel: number,
|
||||||
|
privilegeIcon: { jumpUrl: string, openIconList: any[], closeIconList: any[] },
|
||||||
|
isHidePrivilegeIcon: number,
|
||||||
|
photoWall: { picList: any[] },
|
||||||
|
vipFlag: boolean,
|
||||||
|
yearVipFlag: boolean,
|
||||||
|
svipFlag: boolean,
|
||||||
|
vipLevel: number,
|
||||||
|
status: number,
|
||||||
|
qidianMasterFlag: number,
|
||||||
|
qidianCrewFlag: number,
|
||||||
|
qidianCrewFlag2: number,
|
||||||
|
extStatus: number,
|
||||||
|
recommendImgFlag: number,
|
||||||
|
disableEmojiShortCuts: number,
|
||||||
|
pendantId: string,
|
||||||
|
vipNameColorId: string
|
||||||
|
}
|
||||||
|
}
|
||||||
131
src/core/entities/webapi.ts
Normal file
131
src/core/entities/webapi.ts
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
export enum WebHonorType {
|
||||||
|
ALL = 'all',
|
||||||
|
/**
|
||||||
|
* 群聊之火
|
||||||
|
*/
|
||||||
|
TALKATIVE = 'talkative',
|
||||||
|
/**
|
||||||
|
* 群聊炽焰
|
||||||
|
*/
|
||||||
|
PERFORMER = 'performer',
|
||||||
|
/**
|
||||||
|
* 龙王
|
||||||
|
*/
|
||||||
|
LEGEND = 'legend',
|
||||||
|
/**
|
||||||
|
* 冒尖小春笋(R.I.P)
|
||||||
|
*/
|
||||||
|
STRONG_NEWBIE = 'strong_newbie',
|
||||||
|
/**
|
||||||
|
* 快乐源泉
|
||||||
|
*/
|
||||||
|
EMOTION = 'emotion'
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface WebApiGroupMember {
|
||||||
|
uin: number;
|
||||||
|
role: number;
|
||||||
|
g: number;
|
||||||
|
join_time: number;
|
||||||
|
last_speak_time: number;
|
||||||
|
lv: {
|
||||||
|
point: number
|
||||||
|
level: number
|
||||||
|
};
|
||||||
|
card: string;
|
||||||
|
tags: string;
|
||||||
|
flag: number;
|
||||||
|
nick: string;
|
||||||
|
qage: number;
|
||||||
|
rm: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface WebApiGroupMemberRet {
|
||||||
|
ec: number;
|
||||||
|
errcode: number;
|
||||||
|
em: string;
|
||||||
|
cache: number;
|
||||||
|
adm_num: number;
|
||||||
|
levelname: any;
|
||||||
|
mems: WebApiGroupMember[];
|
||||||
|
count: number;
|
||||||
|
svr_time: number;
|
||||||
|
max_count: number;
|
||||||
|
search_count: number;
|
||||||
|
extmode: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface WebApiGroupNoticeFeed {
|
||||||
|
u: number;//发送者
|
||||||
|
fid: string;//fid
|
||||||
|
pubt: number;//时间
|
||||||
|
msg: {
|
||||||
|
text: string
|
||||||
|
text_face: string
|
||||||
|
title: string,
|
||||||
|
pics?: {
|
||||||
|
id: string,
|
||||||
|
w: string,
|
||||||
|
h: string
|
||||||
|
}[]
|
||||||
|
};
|
||||||
|
type: number;
|
||||||
|
fn: number;
|
||||||
|
cn: number;
|
||||||
|
vn: number;
|
||||||
|
settings: {
|
||||||
|
is_show_edit_card: number
|
||||||
|
remind_ts: number
|
||||||
|
tip_window_type: number
|
||||||
|
confirm_required: number
|
||||||
|
};
|
||||||
|
read_num: number;
|
||||||
|
is_read: number;
|
||||||
|
is_all_confirm: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface WebApiGroupNoticeRet {
|
||||||
|
ec: number
|
||||||
|
em: string
|
||||||
|
ltsm: number
|
||||||
|
srv_code: number
|
||||||
|
read_only: number
|
||||||
|
role: number
|
||||||
|
feeds: WebApiGroupNoticeFeed[]
|
||||||
|
group: {
|
||||||
|
group_id: number
|
||||||
|
class_ext: number
|
||||||
|
}
|
||||||
|
sta: number,
|
||||||
|
gln: number
|
||||||
|
tst: number,
|
||||||
|
ui: any
|
||||||
|
server_time: number
|
||||||
|
svrt: number
|
||||||
|
ad: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface GroupEssenceMsg {
|
||||||
|
group_code: string;
|
||||||
|
msg_seq: number;
|
||||||
|
msg_random: number;
|
||||||
|
sender_uin: string;
|
||||||
|
sender_nick: string;
|
||||||
|
sender_time: number;
|
||||||
|
add_digest_uin: string;
|
||||||
|
add_digest_nick: string;
|
||||||
|
add_digest_time: number;
|
||||||
|
msg_content: any[];
|
||||||
|
can_be_removed: true;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface GroupEssenceMsgRet {
|
||||||
|
retcode: number;
|
||||||
|
retmsg: string;
|
||||||
|
data: {
|
||||||
|
msg_list: GroupEssenceMsg[]
|
||||||
|
is_end: boolean
|
||||||
|
group_role: number
|
||||||
|
config_page_url: string
|
||||||
|
};
|
||||||
|
}
|
||||||
58
src/core/external/appid.json
vendored
Normal file
58
src/core/external/appid.json
vendored
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
{
|
||||||
|
"3.1.2-13107": {
|
||||||
|
"appid": 537146866,
|
||||||
|
"qua": "V1_LNX_NQ_3.1.2-13107_RDM_B"
|
||||||
|
},
|
||||||
|
"3.2.10-25765": {
|
||||||
|
"appid": 537234773,
|
||||||
|
"qua": "V1_LNX_NQ_3.2.10_25765_GW_B"
|
||||||
|
},
|
||||||
|
"3.2.12-26702": {
|
||||||
|
"appid": 537237950,
|
||||||
|
"qua": "V1_LNX_NQ_3.2.12_26702_GW_B"
|
||||||
|
},
|
||||||
|
"3.2.12-26740": {
|
||||||
|
"appid": 537237950,
|
||||||
|
"qua": "V1_WIN_NQ_9.9.15_26740_GW_B"
|
||||||
|
},
|
||||||
|
"3.2.12-26909": {
|
||||||
|
"appid": 537237923,
|
||||||
|
"qua": "V1_LNX_NQ_3.2.12_26909_GW_B"
|
||||||
|
},
|
||||||
|
"9.9.11-24815": {
|
||||||
|
"appid": 537226656,
|
||||||
|
"qua": "V1_WIN_NQ_9.9.11_24815_GW_B"
|
||||||
|
},
|
||||||
|
"9.9.12-25493": {
|
||||||
|
"appid": 537231759,
|
||||||
|
"qua": "V1_WIN_NQ_9.9.12_25493_GW_B"
|
||||||
|
},
|
||||||
|
"9.9.12-25765": {
|
||||||
|
"appid": 537234702,
|
||||||
|
"qua": "V1_WIN_NQ_9.9.12_25765_GW_B"
|
||||||
|
},
|
||||||
|
"9.9.12-26299": {
|
||||||
|
"appid": 537234826,
|
||||||
|
"qua": "V1_WIN_NQ_9.9.12_26299_GW_B"
|
||||||
|
},
|
||||||
|
"9.9.12-26339": {
|
||||||
|
"appid": 537234826,
|
||||||
|
"qua": "V1_WIN_NQ_9.9.12_26339_GW_B"
|
||||||
|
},
|
||||||
|
"9.9.12-26466": {
|
||||||
|
"appid": 537234826,
|
||||||
|
"qua": "V1_WIN_NQ_9.9.12_26466_GW_B"
|
||||||
|
},
|
||||||
|
"9.9.15-26702": {
|
||||||
|
"appid": 537237765,
|
||||||
|
"qua": "V1_WIN_NQ_9.9.15_26702_GW_B"
|
||||||
|
},
|
||||||
|
"9.9.15-26740": {
|
||||||
|
"appid": 537237765,
|
||||||
|
"qua": "V1_WIN_NQ_9.9.15_26702_GW_B"
|
||||||
|
},
|
||||||
|
"9.9.15-26909": {
|
||||||
|
"appid": 537237802,
|
||||||
|
"qua": "V1_WIN_NQ_9.9.15_26909_GW_B"
|
||||||
|
}
|
||||||
|
}
|
||||||
3665
src/core/external/face_config.json
vendored
Normal file
3665
src/core/external/face_config.json
vendored
Normal file
File diff suppressed because it is too large
Load Diff
6
src/core/external/napcat.json
vendored
Normal file
6
src/core/external/napcat.json
vendored
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"fileLog": true,
|
||||||
|
"consoleLog": true,
|
||||||
|
"fileLogLevel": "debug",
|
||||||
|
"consoleLogLevel": "info"
|
||||||
|
}
|
||||||
13
src/core/helper/config.ts
Normal file
13
src/core/helper/config.ts
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import { ConfigBase } from "@/common/utils/ConfigBase";
|
||||||
|
import napCatDefaultConfig from '@/core/external/napcat.json';
|
||||||
|
import { NapCatCore } from '@/core';
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-declaration-merging
|
||||||
|
export type NapCatConfig = typeof napCatDefaultConfig;
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-declaration-merging
|
||||||
|
export class NapCatConfigLoader extends ConfigBase<NapCatConfig> {
|
||||||
|
constructor(coreContext: NapCatCore, configPath: string) {
|
||||||
|
super('napcat', coreContext, configPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
45
src/core/helper/rkey.ts
Normal file
45
src/core/helper/rkey.ts
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
import { LogWrapper } from '@/common/utils/log';
|
||||||
|
import { RequestUtil } from '@/common/utils/request';
|
||||||
|
|
||||||
|
interface ServerRkeyData {
|
||||||
|
group_rkey: string;
|
||||||
|
private_rkey: string;
|
||||||
|
expired_time: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class RkeyManager {
|
||||||
|
serverUrl: string = '';
|
||||||
|
logger: LogWrapper;
|
||||||
|
private rkeyData: ServerRkeyData = {
|
||||||
|
group_rkey: '',
|
||||||
|
private_rkey: '',
|
||||||
|
expired_time: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
constructor(serverUrl: string, logger: LogWrapper) {
|
||||||
|
this.logger = logger;
|
||||||
|
this.serverUrl = serverUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
async getRkey() {
|
||||||
|
if (this.isExpired()) {
|
||||||
|
try {
|
||||||
|
await this.refreshRkey();
|
||||||
|
} catch (e) {
|
||||||
|
this.logger.logError('获取rkey失败', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this.rkeyData;
|
||||||
|
}
|
||||||
|
|
||||||
|
isExpired(): boolean {
|
||||||
|
const now = new Date().getTime() / 1000;
|
||||||
|
// console.log(`now: ${now}, expired_time: ${this.rkeyData.expired_time}`);
|
||||||
|
return now > this.rkeyData.expired_time;
|
||||||
|
}
|
||||||
|
|
||||||
|
async refreshRkey(): Promise<any> {
|
||||||
|
//刷新rkey
|
||||||
|
this.rkeyData = await RequestUtil.HttpGetJson<ServerRkeyData>(this.serverUrl, 'GET');
|
||||||
|
}
|
||||||
|
}
|
||||||
5
src/core/index.ts
Normal file
5
src/core/index.ts
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
export * from './core';
|
||||||
|
export * from './wrapper';
|
||||||
|
export * from './entities';
|
||||||
|
export * from './services';
|
||||||
|
export * from './listeners';
|
||||||
103
src/core/listeners/NodeIKernelBuddyListener.ts
Normal file
103
src/core/listeners/NodeIKernelBuddyListener.ts
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
import { BuddyCategoryType, FriendRequestNotify } from '@/core/entities';
|
||||||
|
|
||||||
|
export type OnBuddyChangeParams = BuddyCategoryType[]
|
||||||
|
|
||||||
|
interface IBuddyListener {
|
||||||
|
onBuddyListChangedV2(arg: unknown): void,//V2版本 还没兼容
|
||||||
|
|
||||||
|
onBuddyListChange(arg: OnBuddyChangeParams): void,
|
||||||
|
|
||||||
|
onBuddyInfoChange(arg: unknown): void,
|
||||||
|
|
||||||
|
onBuddyDetailInfoChange(arg: unknown): void,
|
||||||
|
|
||||||
|
onNickUpdated(arg: unknown): void,
|
||||||
|
|
||||||
|
onBuddyRemarkUpdated(arg: unknown): void,
|
||||||
|
|
||||||
|
onAvatarUrlUpdated(arg: unknown): void,
|
||||||
|
|
||||||
|
onBuddyReqChange(arg: FriendRequestNotify): void,
|
||||||
|
|
||||||
|
onBuddyReqUnreadCntChange(arg: unknown): void,
|
||||||
|
|
||||||
|
onCheckBuddySettingResult(arg: unknown): void,
|
||||||
|
|
||||||
|
onAddBuddyNeedVerify(arg: unknown): void,
|
||||||
|
|
||||||
|
onSmartInfos(arg: unknown): void,
|
||||||
|
|
||||||
|
onSpacePermissionInfos(arg: unknown): void,
|
||||||
|
|
||||||
|
onDoubtBuddyReqChange(arg: unknown): void,
|
||||||
|
|
||||||
|
onDoubtBuddyReqUnreadNumChange(arg: unknown): void,
|
||||||
|
|
||||||
|
onBlockChanged(arg: unknown): void,
|
||||||
|
|
||||||
|
onAddMeSettingChanged(arg: unknown): void,
|
||||||
|
|
||||||
|
onDelBatchBuddyInfos(arg: unknown): void
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface NodeIKernelBuddyListener extends IBuddyListener {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-misused-new
|
||||||
|
new(listener: IBuddyListener): NodeIKernelBuddyListener;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class BuddyListener implements IBuddyListener {
|
||||||
|
onBuddyListChangedV2(arg: unknown): void {
|
||||||
|
//throw new Error('Method not implemented.');
|
||||||
|
}
|
||||||
|
|
||||||
|
onAddBuddyNeedVerify(arg: unknown) {
|
||||||
|
}
|
||||||
|
|
||||||
|
onAddMeSettingChanged(arg: unknown) {
|
||||||
|
}
|
||||||
|
|
||||||
|
onAvatarUrlUpdated(arg: unknown) {
|
||||||
|
}
|
||||||
|
|
||||||
|
onBlockChanged(arg: unknown) {
|
||||||
|
}
|
||||||
|
|
||||||
|
onBuddyDetailInfoChange(arg: unknown) {
|
||||||
|
}
|
||||||
|
|
||||||
|
onBuddyInfoChange(arg: unknown) {
|
||||||
|
}
|
||||||
|
|
||||||
|
onBuddyListChange(arg: OnBuddyChangeParams): void {
|
||||||
|
}
|
||||||
|
|
||||||
|
onBuddyRemarkUpdated(arg: unknown): void {
|
||||||
|
}
|
||||||
|
|
||||||
|
onBuddyReqChange(arg: FriendRequestNotify): void {
|
||||||
|
}
|
||||||
|
|
||||||
|
onBuddyReqUnreadCntChange(arg: unknown): void {
|
||||||
|
}
|
||||||
|
|
||||||
|
onCheckBuddySettingResult(arg: unknown): void {
|
||||||
|
}
|
||||||
|
|
||||||
|
onDelBatchBuddyInfos(arg: unknown): void {
|
||||||
|
}
|
||||||
|
|
||||||
|
onDoubtBuddyReqChange(arg: unknown): void {
|
||||||
|
}
|
||||||
|
|
||||||
|
onDoubtBuddyReqUnreadNumChange(arg: unknown): void {
|
||||||
|
}
|
||||||
|
|
||||||
|
onNickUpdated(arg: unknown): void {
|
||||||
|
}
|
||||||
|
|
||||||
|
onSmartInfos(arg: unknown): void {
|
||||||
|
}
|
||||||
|
|
||||||
|
onSpacePermissionInfos(arg: unknown): void {
|
||||||
|
}
|
||||||
|
}
|
||||||
33
src/core/listeners/NodeIKernelFileAssistantListener.ts
Normal file
33
src/core/listeners/NodeIKernelFileAssistantListener.ts
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
export interface IKernelFileAssistantListener {
|
||||||
|
onFileStatusChanged(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
onSessionListChanged(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
onSessionChanged(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
onFileListChanged(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
onFileSearch(...args: unknown[]): unknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface NodeIKernelFileAssistantListener extends IKernelFileAssistantListener {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-misused-new
|
||||||
|
new(adapter: IKernelFileAssistantListener): NodeIKernelFileAssistantListener;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class KernelFileAssistantListener implements IKernelFileAssistantListener {
|
||||||
|
onFileStatusChanged(...args: unknown[]) {
|
||||||
|
}
|
||||||
|
|
||||||
|
onSessionListChanged(...args: unknown[]) {
|
||||||
|
}
|
||||||
|
|
||||||
|
onSessionChanged(...args: unknown[]) {
|
||||||
|
}
|
||||||
|
|
||||||
|
onFileListChanged(...args: unknown[]) {
|
||||||
|
}
|
||||||
|
|
||||||
|
onFileSearch(...args: unknown[]) {
|
||||||
|
}
|
||||||
|
}
|
||||||
243
src/core/listeners/NodeIKernelGroupListener.ts
Normal file
243
src/core/listeners/NodeIKernelGroupListener.ts
Normal file
@@ -0,0 +1,243 @@
|
|||||||
|
import { Group, GroupListUpdateType, GroupMember, GroupNotify } from '@/core/entities';
|
||||||
|
|
||||||
|
interface IGroupListener {
|
||||||
|
onGroupListUpdate(updateType: GroupListUpdateType, groupList: Group[]): void;
|
||||||
|
|
||||||
|
onGroupExtListUpdate(...args: unknown[]): void;
|
||||||
|
|
||||||
|
onGroupSingleScreenNotifies(doubt: boolean, seq: string, notifies: GroupNotify[]): void;
|
||||||
|
|
||||||
|
onGroupNotifiesUpdated(dboubt: boolean, notifies: GroupNotify[]): void;
|
||||||
|
|
||||||
|
onGroupNotifiesUnreadCountUpdated(...args: unknown[]): void;
|
||||||
|
|
||||||
|
onGroupDetailInfoChange(...args: unknown[]): void;
|
||||||
|
|
||||||
|
onGroupAllInfoChange(...args: unknown[]): void;
|
||||||
|
|
||||||
|
onGroupsMsgMaskResult(...args: unknown[]): void;
|
||||||
|
|
||||||
|
onGroupConfMemberChange(...args: unknown[]): void;
|
||||||
|
|
||||||
|
onGroupBulletinChange(...args: unknown[]): void;
|
||||||
|
|
||||||
|
onGetGroupBulletinListResult(...args: unknown[]): void;
|
||||||
|
|
||||||
|
onMemberListChange(arg: {
|
||||||
|
sceneId: string,
|
||||||
|
ids: string[],
|
||||||
|
infos: Map<string, GroupMember>,
|
||||||
|
finish: boolean,
|
||||||
|
hasRobot: boolean
|
||||||
|
}): void;
|
||||||
|
|
||||||
|
onMemberInfoChange(groupCode: string, changeType: number, members: Map<string, GroupMember>): void;
|
||||||
|
|
||||||
|
onSearchMemberChange(...args: unknown[]): void;
|
||||||
|
|
||||||
|
onGroupBulletinRichMediaDownloadComplete(...args: unknown[]): void;
|
||||||
|
|
||||||
|
onGroupBulletinRichMediaProgressUpdate(...args: unknown[]): void;
|
||||||
|
|
||||||
|
onGroupStatisticInfoChange(...args: unknown[]): void;
|
||||||
|
|
||||||
|
onJoinGroupNotify(...args: unknown[]): void;
|
||||||
|
|
||||||
|
onShutUpMemberListChanged(...args: unknown[]): void;
|
||||||
|
|
||||||
|
onGroupBulletinRemindNotify(...args: unknown[]): void;
|
||||||
|
|
||||||
|
onGroupFirstBulletinNotify(...args: unknown[]): void;
|
||||||
|
|
||||||
|
onJoinGroupNoVerifyFlag(...args: unknown[]): void;
|
||||||
|
|
||||||
|
onGroupArkInviteStateResult(...args: unknown[]): void;
|
||||||
|
|
||||||
|
// 发现于Win 9.9.9 23159
|
||||||
|
onGroupMemberLevelInfoChange(...args: unknown[]): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface NodeIKernelGroupListener extends IGroupListener {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-misused-new
|
||||||
|
new(listener: IGroupListener): NodeIKernelGroupListener;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class GroupListener implements IGroupListener {
|
||||||
|
// 发现于Win 9.9.9 23159
|
||||||
|
onGroupMemberLevelInfoChange(...args: unknown[]): void {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
onGetGroupBulletinListResult(...args: unknown[]) {
|
||||||
|
}
|
||||||
|
|
||||||
|
onGroupAllInfoChange(...args: unknown[]) {
|
||||||
|
}
|
||||||
|
|
||||||
|
onGroupBulletinChange(...args: unknown[]) {
|
||||||
|
}
|
||||||
|
|
||||||
|
onGroupBulletinRemindNotify(...args: unknown[]) {
|
||||||
|
}
|
||||||
|
|
||||||
|
onGroupArkInviteStateResult(...args: unknown[]) {
|
||||||
|
}
|
||||||
|
|
||||||
|
onGroupBulletinRichMediaDownloadComplete(...args: unknown[]) {
|
||||||
|
}
|
||||||
|
|
||||||
|
onGroupConfMemberChange(...args: unknown[]) {
|
||||||
|
}
|
||||||
|
|
||||||
|
onGroupDetailInfoChange(...args: unknown[]) {
|
||||||
|
}
|
||||||
|
|
||||||
|
onGroupExtListUpdate(...args: unknown[]) {
|
||||||
|
}
|
||||||
|
|
||||||
|
onGroupFirstBulletinNotify(...args: unknown[]) {
|
||||||
|
}
|
||||||
|
|
||||||
|
onGroupListUpdate(updateType: GroupListUpdateType, groupList: Group[]) {
|
||||||
|
}
|
||||||
|
|
||||||
|
onGroupNotifiesUpdated(dboubt: boolean, notifies: GroupNotify[]) {
|
||||||
|
}
|
||||||
|
|
||||||
|
onGroupBulletinRichMediaProgressUpdate(...args: unknown[]) {
|
||||||
|
}
|
||||||
|
|
||||||
|
onGroupNotifiesUnreadCountUpdated(...args: unknown[]) {
|
||||||
|
}
|
||||||
|
|
||||||
|
onGroupSingleScreenNotifies(doubt: boolean, seq: string, notifies: GroupNotify[]) {
|
||||||
|
}
|
||||||
|
|
||||||
|
onGroupsMsgMaskResult(...args: unknown[]) {
|
||||||
|
}
|
||||||
|
|
||||||
|
onGroupStatisticInfoChange(...args: unknown[]) {
|
||||||
|
}
|
||||||
|
|
||||||
|
onJoinGroupNotify(...args: unknown[]) {
|
||||||
|
}
|
||||||
|
|
||||||
|
onJoinGroupNoVerifyFlag(...args: unknown[]) {
|
||||||
|
}
|
||||||
|
|
||||||
|
onMemberInfoChange(groupCode: string, changeType: number, members: Map<string, GroupMember>) {
|
||||||
|
}
|
||||||
|
|
||||||
|
onMemberListChange(arg: {
|
||||||
|
sceneId: string,
|
||||||
|
ids: string[],
|
||||||
|
infos: Map<string, GroupMember>, // uid -> GroupMember
|
||||||
|
finish: boolean,
|
||||||
|
hasRobot: boolean
|
||||||
|
}) {
|
||||||
|
}
|
||||||
|
|
||||||
|
onSearchMemberChange(...args: unknown[]) {
|
||||||
|
}
|
||||||
|
|
||||||
|
onShutUpMemberListChanged(...args: unknown[]) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class DebugGroupListener implements IGroupListener {
|
||||||
|
onGroupMemberLevelInfoChange(...args: unknown[]): void {
|
||||||
|
console.log('onGroupMemberLevelInfoChange:', ...args);
|
||||||
|
}
|
||||||
|
|
||||||
|
onGetGroupBulletinListResult(...args: unknown[]) {
|
||||||
|
console.log('onGetGroupBulletinListResult:', ...args);
|
||||||
|
}
|
||||||
|
|
||||||
|
onGroupAllInfoChange(...args: unknown[]) {
|
||||||
|
console.log('onGroupAllInfoChange:', ...args);
|
||||||
|
}
|
||||||
|
|
||||||
|
onGroupBulletinChange(...args: unknown[]) {
|
||||||
|
console.log('onGroupBulletinChange:', ...args);
|
||||||
|
}
|
||||||
|
|
||||||
|
onGroupBulletinRemindNotify(...args: unknown[]) {
|
||||||
|
console.log('onGroupBulletinRemindNotify:', ...args);
|
||||||
|
}
|
||||||
|
|
||||||
|
onGroupArkInviteStateResult(...args: unknown[]) {
|
||||||
|
console.log('onGroupArkInviteStateResult:', ...args);
|
||||||
|
}
|
||||||
|
|
||||||
|
onGroupBulletinRichMediaDownloadComplete(...args: unknown[]) {
|
||||||
|
console.log('onGroupBulletinRichMediaDownloadComplete:', ...args);
|
||||||
|
}
|
||||||
|
|
||||||
|
onGroupConfMemberChange(...args: unknown[]) {
|
||||||
|
console.log('onGroupConfMemberChange:', ...args);
|
||||||
|
}
|
||||||
|
|
||||||
|
onGroupDetailInfoChange(...args: unknown[]) {
|
||||||
|
console.log('onGroupDetailInfoChange:', ...args);
|
||||||
|
}
|
||||||
|
|
||||||
|
onGroupExtListUpdate(...args: unknown[]) {
|
||||||
|
console.log('onGroupExtListUpdate:', ...args);
|
||||||
|
}
|
||||||
|
|
||||||
|
onGroupFirstBulletinNotify(...args: unknown[]) {
|
||||||
|
console.log('onGroupFirstBulletinNotify:', ...args);
|
||||||
|
}
|
||||||
|
|
||||||
|
onGroupListUpdate(...args: unknown[]) {
|
||||||
|
console.log('onGroupListUpdate:', ...args);
|
||||||
|
}
|
||||||
|
|
||||||
|
onGroupNotifiesUpdated(...args: unknown[]) {
|
||||||
|
console.log('onGroupNotifiesUpdated:', ...args);
|
||||||
|
}
|
||||||
|
|
||||||
|
onGroupBulletinRichMediaProgressUpdate(...args: unknown[]) {
|
||||||
|
console.log('onGroupBulletinRichMediaProgressUpdate:', ...args);
|
||||||
|
}
|
||||||
|
|
||||||
|
onGroupNotifiesUnreadCountUpdated(...args: unknown[]) {
|
||||||
|
console.log('onGroupNotifiesUnreadCountUpdated:', ...args);
|
||||||
|
}
|
||||||
|
|
||||||
|
onGroupSingleScreenNotifies(doubt: boolean, seq: string, notifies: GroupNotify[]) {
|
||||||
|
console.log('onGroupSingleScreenNotifies:');
|
||||||
|
}
|
||||||
|
|
||||||
|
onGroupsMsgMaskResult(...args: unknown[]) {
|
||||||
|
console.log('onGroupsMsgMaskResult:', ...args);
|
||||||
|
}
|
||||||
|
|
||||||
|
onGroupStatisticInfoChange(...args: unknown[]) {
|
||||||
|
console.log('onGroupStatisticInfoChange:', ...args);
|
||||||
|
}
|
||||||
|
|
||||||
|
onJoinGroupNotify(...args: unknown[]) {
|
||||||
|
console.log('onJoinGroupNotify:', ...args);
|
||||||
|
}
|
||||||
|
|
||||||
|
onJoinGroupNoVerifyFlag(...args: unknown[]) {
|
||||||
|
console.log('onJoinGroupNoVerifyFlag:', ...args);
|
||||||
|
}
|
||||||
|
|
||||||
|
onMemberInfoChange(groupCode: string, changeType: number, members: Map<string, GroupMember>) {
|
||||||
|
console.log('onMemberInfoChange:', groupCode, changeType, members);
|
||||||
|
}
|
||||||
|
|
||||||
|
onMemberListChange(...args: unknown[]) {
|
||||||
|
console.log('onMemberListChange:', ...args);
|
||||||
|
}
|
||||||
|
|
||||||
|
onSearchMemberChange(...args: unknown[]) {
|
||||||
|
console.log('onSearchMemberChange:', ...args);
|
||||||
|
}
|
||||||
|
|
||||||
|
onShutUpMemberListChanged(...args: unknown[]) {
|
||||||
|
console.log('onShutUpMemberListChanged:', ...args);
|
||||||
|
}
|
||||||
|
}
|
||||||
108
src/core/listeners/NodeIKernelLoginListener.ts
Normal file
108
src/core/listeners/NodeIKernelLoginListener.ts
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
export interface IKernelLoginListener {
|
||||||
|
onLoginConnected(...args: any[]): void;
|
||||||
|
|
||||||
|
onLoginDisConnected(...args: any[]): void;
|
||||||
|
|
||||||
|
onLoginConnecting(...args: any[]): void;
|
||||||
|
|
||||||
|
onQRCodeGetPicture(...args: any[]): void;
|
||||||
|
|
||||||
|
onQRCodeLoginPollingStarted(...args: any[]): void;
|
||||||
|
|
||||||
|
onQRCodeSessionUserScaned(...args: any[]): void;
|
||||||
|
|
||||||
|
onQRCodeLoginSucceed(...args: any[]): void;
|
||||||
|
|
||||||
|
onQRCodeSessionFailed(...args: any[]): void;
|
||||||
|
|
||||||
|
onLoginFailed(...args: any[]): void;
|
||||||
|
|
||||||
|
onLogoutSucceed(...args: any[]): void;
|
||||||
|
|
||||||
|
onLogoutFailed(...args: any[]): void;
|
||||||
|
|
||||||
|
onUserLoggedIn(...args: any[]): void;
|
||||||
|
|
||||||
|
onQRCodeSessionQuickLoginFailed(...args: any[]): void;
|
||||||
|
|
||||||
|
onPasswordLoginFailed(...args: any[]): void;
|
||||||
|
|
||||||
|
OnConfirmUnusualDeviceFailed(...args: any[]): void;
|
||||||
|
|
||||||
|
onQQLoginNumLimited(...args: any[]): void;
|
||||||
|
|
||||||
|
onLoginState(...args: any[]): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface NodeIKernelLoginListener {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-misused-new
|
||||||
|
new(listener: IKernelLoginListener): NodeIKernelLoginListener;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class LoginListener implements IKernelLoginListener {
|
||||||
|
onLoginConnected(...args: any[]): void {
|
||||||
|
}
|
||||||
|
|
||||||
|
onLoginDisConnected(...args: any[]): void {
|
||||||
|
}
|
||||||
|
|
||||||
|
onLoginConnecting(...args: any[]): void {
|
||||||
|
}
|
||||||
|
|
||||||
|
onQRCodeGetPicture(arg: { pngBase64QrcodeData: string, qrcodeUrl: string }): void {
|
||||||
|
// let base64Data: string = arg.pngBase64QrcodeData
|
||||||
|
// base64Data = base64Data.split("data:image/png;base64,")[1]
|
||||||
|
// let buffer = Buffer.from(base64Data, 'base64')
|
||||||
|
// console.log("onQRCodeGetPicture", arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
onQRCodeLoginPollingStarted(...args: any[]): void {
|
||||||
|
}
|
||||||
|
|
||||||
|
onQRCodeSessionUserScaned(...args: any[]): void {
|
||||||
|
}
|
||||||
|
|
||||||
|
onQRCodeLoginSucceed(arg: QRCodeLoginSucceedResult): void {
|
||||||
|
}
|
||||||
|
|
||||||
|
onQRCodeSessionFailed(...args: any[]): void {
|
||||||
|
}
|
||||||
|
|
||||||
|
onLoginFailed(...args: any[]): void {
|
||||||
|
}
|
||||||
|
|
||||||
|
onLogoutSucceed(...args: any[]): void {
|
||||||
|
}
|
||||||
|
|
||||||
|
onLogoutFailed(...args: any[]): void {
|
||||||
|
}
|
||||||
|
|
||||||
|
onUserLoggedIn(...args: any[]): void {
|
||||||
|
}
|
||||||
|
|
||||||
|
onQRCodeSessionQuickLoginFailed(...args: any[]): void {
|
||||||
|
}
|
||||||
|
|
||||||
|
onPasswordLoginFailed(...args: any[]): void {
|
||||||
|
}
|
||||||
|
|
||||||
|
OnConfirmUnusualDeviceFailed(...args: any[]): void {
|
||||||
|
}
|
||||||
|
|
||||||
|
onQQLoginNumLimited(...args: any[]): void {
|
||||||
|
}
|
||||||
|
|
||||||
|
onLoginState(...args: any[]): void {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface QRCodeLoginSucceedResult {
|
||||||
|
account: string;
|
||||||
|
mainAccount: string;
|
||||||
|
uin: string; //拿UIN
|
||||||
|
uid: string; //拿UID
|
||||||
|
nickName: string; //一般是空的 拿不到
|
||||||
|
gender: number;
|
||||||
|
age: number;
|
||||||
|
faceUrl: string;//一般是空的 拿不到
|
||||||
|
}
|
||||||
534
src/core/listeners/NodeIKernelMsgListener.ts
Normal file
534
src/core/listeners/NodeIKernelMsgListener.ts
Normal file
@@ -0,0 +1,534 @@
|
|||||||
|
import { ChatType, RawMessage } from '@/core/entities';
|
||||||
|
|
||||||
|
export interface OnRichMediaDownloadCompleteParams {
|
||||||
|
fileModelId: string,
|
||||||
|
msgElementId: string,
|
||||||
|
msgId: string,
|
||||||
|
fileId: string,
|
||||||
|
fileProgress: string, // '0'
|
||||||
|
fileSpeed: string, // '0'
|
||||||
|
fileErrCode: string, // '0'
|
||||||
|
fileErrMsg: string,
|
||||||
|
fileDownType: number, // 暂时未知
|
||||||
|
thumbSize: number,
|
||||||
|
filePath: string,
|
||||||
|
totalSize: string,
|
||||||
|
trasferStatus: number,
|
||||||
|
step: number,
|
||||||
|
commonFileInfo: unknown | null,
|
||||||
|
fileSrvErrCode: string,
|
||||||
|
clientMsg: string,
|
||||||
|
businessId: number,
|
||||||
|
userTotalSpacePerDay: unknown | null,
|
||||||
|
userUsedSpacePerDay: unknown | null
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface onGroupFileInfoUpdateParamType {
|
||||||
|
retCode: number;
|
||||||
|
retMsg: string;
|
||||||
|
clientWording: string;
|
||||||
|
isEnd: boolean;
|
||||||
|
item: Array<any>;
|
||||||
|
allFileCount: string;
|
||||||
|
nextIndex: string;
|
||||||
|
reqId: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
// {
|
||||||
|
// sessionType: 1,
|
||||||
|
// chatType: 100,
|
||||||
|
// peerUid: 'u_PVQ3tl6K78xxxx',
|
||||||
|
// groupCode: '809079648',
|
||||||
|
// fromNick: '拾xxxx,
|
||||||
|
// sig: '0x'
|
||||||
|
// }
|
||||||
|
export interface TempOnRecvParams {
|
||||||
|
sessionType: number,//1
|
||||||
|
chatType: ChatType,//100
|
||||||
|
peerUid: string,//uid
|
||||||
|
groupCode: string,//gc
|
||||||
|
fromNick: string,//gc name
|
||||||
|
sig: string,
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IKernelMsgListener {
|
||||||
|
onAddSendMsg(msgRecord: RawMessage): void;
|
||||||
|
|
||||||
|
onBroadcastHelperDownloadComplete(broadcastHelperTransNotifyInfo: unknown): void;
|
||||||
|
|
||||||
|
onBroadcastHelperProgressUpdate(broadcastHelperTransNotifyInfo: unknown): void;
|
||||||
|
|
||||||
|
onChannelFreqLimitInfoUpdate(contact: unknown, z: unknown, freqLimitInfo: unknown): void;
|
||||||
|
|
||||||
|
onContactUnreadCntUpdate(hashMap: unknown): void;
|
||||||
|
|
||||||
|
onCustomWithdrawConfigUpdate(customWithdrawConfig: unknown): void;
|
||||||
|
|
||||||
|
onDraftUpdate(contact: unknown, arrayList: unknown, j2: unknown): void;
|
||||||
|
|
||||||
|
onEmojiDownloadComplete(emojiNotifyInfo: unknown): void;
|
||||||
|
|
||||||
|
onEmojiResourceUpdate(emojiResourceInfo: unknown): void;
|
||||||
|
|
||||||
|
onFeedEventUpdate(firstViewDirectMsgNotifyInfo: unknown): void;
|
||||||
|
|
||||||
|
onFileMsgCome(arrayList: unknown): void;
|
||||||
|
|
||||||
|
onFirstViewDirectMsgUpdate(firstViewDirectMsgNotifyInfo: unknown): void;
|
||||||
|
|
||||||
|
onFirstViewGroupGuildMapping(arrayList: unknown): void;
|
||||||
|
|
||||||
|
onGrabPasswordRedBag(i2: unknown, str: unknown, i3: unknown, recvdOrder: unknown, msgRecord: unknown): void;
|
||||||
|
|
||||||
|
onGroupFileInfoAdd(groupItem: unknown): void;
|
||||||
|
|
||||||
|
onGroupFileInfoUpdate(groupFileListResult: onGroupFileInfoUpdateParamType): void;
|
||||||
|
|
||||||
|
onGroupGuildUpdate(groupGuildNotifyInfo: unknown): void;
|
||||||
|
|
||||||
|
onGroupTransferInfoAdd(groupItem: unknown): void;
|
||||||
|
|
||||||
|
onGroupTransferInfoUpdate(groupFileListResult: unknown): void;
|
||||||
|
|
||||||
|
onGuildInteractiveUpdate(guildInteractiveNotificationItem: unknown): void;
|
||||||
|
|
||||||
|
onGuildMsgAbFlagChanged(guildMsgAbFlag: unknown): void;
|
||||||
|
|
||||||
|
onGuildNotificationAbstractUpdate(guildNotificationAbstractInfo: unknown): void;
|
||||||
|
|
||||||
|
onHitCsRelatedEmojiResult(downloadRelateEmojiResultInfo: unknown): void;
|
||||||
|
|
||||||
|
onHitEmojiKeywordResult(hitRelatedEmojiWordsResult: unknown): void;
|
||||||
|
|
||||||
|
onHitRelatedEmojiResult(relatedWordEmojiInfo: unknown): void;
|
||||||
|
|
||||||
|
onImportOldDbProgressUpdate(importOldDbMsgNotifyInfo: unknown): void;
|
||||||
|
|
||||||
|
onInputStatusPush(inputStatusInfo: {
|
||||||
|
chatType: number;
|
||||||
|
eventType: number;
|
||||||
|
fromUin: string;
|
||||||
|
interval: string;
|
||||||
|
showTime: string;
|
||||||
|
statusText: string;
|
||||||
|
timestamp: string;
|
||||||
|
toUin: string;
|
||||||
|
}): void;
|
||||||
|
|
||||||
|
onKickedOffLine(kickedInfo: unknown): void;
|
||||||
|
|
||||||
|
onLineDev(arrayList: unknown): void;
|
||||||
|
|
||||||
|
onLogLevelChanged(j2: unknown): void;
|
||||||
|
|
||||||
|
onMsgAbstractUpdate(arrayList: unknown): void;
|
||||||
|
|
||||||
|
onMsgBoxChanged(arrayList: unknown): void;
|
||||||
|
|
||||||
|
onMsgDelete(contact: unknown, arrayList: unknown): void;
|
||||||
|
|
||||||
|
onMsgEventListUpdate(hashMap: unknown): void;
|
||||||
|
|
||||||
|
onMsgInfoListAdd(arrayList: unknown): void;
|
||||||
|
|
||||||
|
onMsgInfoListUpdate(msgList: RawMessage[]): void;
|
||||||
|
|
||||||
|
onMsgQRCodeStatusChanged(i2: unknown): void;
|
||||||
|
|
||||||
|
onMsgRecall(i2: unknown, str: unknown, j2: unknown): void;
|
||||||
|
|
||||||
|
onMsgSecurityNotify(msgRecord: unknown): void;
|
||||||
|
|
||||||
|
onMsgSettingUpdate(msgSetting: unknown): void;
|
||||||
|
|
||||||
|
onNtFirstViewMsgSyncEnd(): void;
|
||||||
|
|
||||||
|
onNtMsgSyncEnd(): void;
|
||||||
|
|
||||||
|
onNtMsgSyncStart(): void;
|
||||||
|
|
||||||
|
onReadFeedEventUpdate(firstViewDirectMsgNotifyInfo: unknown): void;
|
||||||
|
|
||||||
|
onRecvGroupGuildFlag(i2: unknown): void;
|
||||||
|
|
||||||
|
onRecvMsg(...arrayList: unknown[]): void;
|
||||||
|
|
||||||
|
onRecvMsgSvrRspTransInfo(j2: unknown, contact: unknown, i2: unknown, i3: unknown, str: unknown, bArr: unknown): void;
|
||||||
|
|
||||||
|
onRecvOnlineFileMsg(arrayList: unknown): void;
|
||||||
|
|
||||||
|
onRecvS2CMsg(arrayList: unknown): void;
|
||||||
|
|
||||||
|
onRecvSysMsg(arrayList: unknown): void;
|
||||||
|
|
||||||
|
onRecvUDCFlag(i2: unknown): void;
|
||||||
|
|
||||||
|
onRichMediaDownloadComplete(fileTransNotifyInfo: OnRichMediaDownloadCompleteParams): void;
|
||||||
|
|
||||||
|
onRichMediaProgerssUpdate(fileTransNotifyInfo: unknown): void;
|
||||||
|
|
||||||
|
onRichMediaUploadComplete(fileTransNotifyInfo: unknown): void;
|
||||||
|
|
||||||
|
onSearchGroupFileInfoUpdate(searchGroupFileResult:
|
||||||
|
{
|
||||||
|
result: {
|
||||||
|
retCode: number,
|
||||||
|
retMsg: string,
|
||||||
|
clientWording: string
|
||||||
|
},
|
||||||
|
syncCookie: string,
|
||||||
|
totalMatchCount: number,
|
||||||
|
ownerMatchCount: number,
|
||||||
|
isEnd: boolean,
|
||||||
|
reqId: number,
|
||||||
|
item: Array<{
|
||||||
|
groupCode: string,
|
||||||
|
groupName: string,
|
||||||
|
uploaderUin: string,
|
||||||
|
uploaderName: string,
|
||||||
|
matchUin: string,
|
||||||
|
matchWords: Array<unknown>,
|
||||||
|
fileNameHits: Array<{
|
||||||
|
start: number,
|
||||||
|
end: number
|
||||||
|
}>,
|
||||||
|
fileModelId: string,
|
||||||
|
fileId: string,
|
||||||
|
fileName: string,
|
||||||
|
fileSize: string,
|
||||||
|
busId: number,
|
||||||
|
uploadTime: number,
|
||||||
|
modifyTime: number,
|
||||||
|
deadTime: number,
|
||||||
|
downloadTimes: number,
|
||||||
|
localPath: string
|
||||||
|
}>
|
||||||
|
}): void;
|
||||||
|
|
||||||
|
onSendMsgError(j2: unknown, contact: unknown, i2: unknown, str: unknown): void;
|
||||||
|
|
||||||
|
onSysMsgNotification(i2: unknown, j2: unknown, j3: unknown, arrayList: unknown): void;
|
||||||
|
|
||||||
|
onTempChatInfoUpdate(tempChatInfo: TempOnRecvParams): void;
|
||||||
|
|
||||||
|
onUnreadCntAfterFirstView(hashMap: unknown): void;
|
||||||
|
|
||||||
|
onUnreadCntUpdate(hashMap: unknown): void;
|
||||||
|
|
||||||
|
onUserChannelTabStatusChanged(z: unknown): void;
|
||||||
|
|
||||||
|
onUserOnlineStatusChanged(z: unknown): void;
|
||||||
|
|
||||||
|
onUserTabStatusChanged(arrayList: unknown): void;
|
||||||
|
|
||||||
|
onlineStatusBigIconDownloadPush(i2: unknown, j2: unknown, str: unknown): void;
|
||||||
|
|
||||||
|
onlineStatusSmallIconDownloadPush(i2: unknown, j2: unknown, str: unknown): void;
|
||||||
|
|
||||||
|
// 第一次发现于Linux
|
||||||
|
onUserSecQualityChanged(...args: unknown[]): void;
|
||||||
|
|
||||||
|
onMsgWithRichLinkInfoUpdate(...args: unknown[]): void;
|
||||||
|
|
||||||
|
onRedTouchChanged(...args: unknown[]): void;
|
||||||
|
|
||||||
|
// 第一次发现于Win 9.9.9 23159
|
||||||
|
onBroadcastHelperProgerssUpdate(...args: unknown[]): void;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface NodeIKernelMsgListener extends IKernelMsgListener {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-misused-new
|
||||||
|
new(listener: IKernelMsgListener): NodeIKernelMsgListener;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export class MsgListener implements IKernelMsgListener {
|
||||||
|
onAddSendMsg(msgRecord: RawMessage) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
onBroadcastHelperDownloadComplete(broadcastHelperTransNotifyInfo: unknown) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
onBroadcastHelperProgressUpdate(broadcastHelperTransNotifyInfo: unknown) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
onChannelFreqLimitInfoUpdate(contact: unknown, z: unknown, freqLimitInfo: unknown) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
onContactUnreadCntUpdate(hashMap: unknown) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
onCustomWithdrawConfigUpdate(customWithdrawConfig: unknown) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
onDraftUpdate(contact: unknown, arrayList: unknown, j2: unknown) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
onEmojiDownloadComplete(emojiNotifyInfo: unknown) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
onEmojiResourceUpdate(emojiResourceInfo: unknown) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
onFeedEventUpdate(firstViewDirectMsgNotifyInfo: unknown) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
onFileMsgCome(arrayList: unknown) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
onFirstViewDirectMsgUpdate(firstViewDirectMsgNotifyInfo: unknown) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
onFirstViewGroupGuildMapping(arrayList: unknown) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
onGrabPasswordRedBag(i2: unknown, str: unknown, i3: unknown, recvdOrder: unknown, msgRecord: unknown) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
onGroupFileInfoAdd(groupItem: unknown) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
onGroupFileInfoUpdate(groupFileListResult: onGroupFileInfoUpdateParamType) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
onGroupGuildUpdate(groupGuildNotifyInfo: unknown) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
onGroupTransferInfoAdd(groupItem: unknown) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
onGroupTransferInfoUpdate(groupFileListResult: unknown) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
onGuildInteractiveUpdate(guildInteractiveNotificationItem: unknown) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
onGuildMsgAbFlagChanged(guildMsgAbFlag: unknown) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
onGuildNotificationAbstractUpdate(guildNotificationAbstractInfo: unknown) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
onHitCsRelatedEmojiResult(downloadRelateEmojiResultInfo: unknown) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
onHitEmojiKeywordResult(hitRelatedEmojiWordsResult: unknown) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
onHitRelatedEmojiResult(relatedWordEmojiInfo: unknown) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
onImportOldDbProgressUpdate(importOldDbMsgNotifyInfo: unknown) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
onInputStatusPush(inputStatusInfo: {
|
||||||
|
chatType: number;
|
||||||
|
eventType: number;
|
||||||
|
fromUin: string;
|
||||||
|
interval: string;
|
||||||
|
showTime: string;
|
||||||
|
statusText: string;
|
||||||
|
timestamp: string;
|
||||||
|
toUin: string;
|
||||||
|
}) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
onKickedOffLine(kickedInfo: unknown) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
onLineDev(arrayList: unknown) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
onLogLevelChanged(j2: unknown) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
onMsgAbstractUpdate(arrayList: unknown) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
onMsgBoxChanged(arrayList: unknown) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
onMsgDelete(contact: unknown, arrayList: unknown) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
onMsgEventListUpdate(hashMap: unknown) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
onMsgInfoListAdd(arrayList: unknown) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
onMsgInfoListUpdate(msgList: RawMessage[]) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
onMsgQRCodeStatusChanged(i2: unknown) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
onMsgRecall(i2: unknown, str: unknown, j2: unknown) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
onMsgSecurityNotify(msgRecord: unknown) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
onMsgSettingUpdate(msgSetting: unknown) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
onNtFirstViewMsgSyncEnd() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
onNtMsgSyncEnd() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
onNtMsgSyncStart() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
onReadFeedEventUpdate(firstViewDirectMsgNotifyInfo: unknown) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
onRecvGroupGuildFlag(i2: unknown) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
onRecvMsg(arrayList: RawMessage[]) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
onRecvMsgSvrRspTransInfo(j2: unknown, contact: unknown, i2: unknown, i3: unknown, str: unknown, bArr: unknown) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
onRecvOnlineFileMsg(arrayList: unknown) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
onRecvS2CMsg(arrayList: unknown) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
onRecvSysMsg(arrayList: unknown) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
onRecvUDCFlag(i2: unknown) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
onRichMediaDownloadComplete(fileTransNotifyInfo: OnRichMediaDownloadCompleteParams) {
|
||||||
|
}
|
||||||
|
|
||||||
|
onRichMediaProgerssUpdate(fileTransNotifyInfo: unknown) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
onRichMediaUploadComplete(fileTransNotifyInfo: unknown) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
onSearchGroupFileInfoUpdate(searchGroupFileResult: unknown) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
onSendMsgError(j2: unknown, contact: unknown, i2: unknown, str: unknown) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
onSysMsgNotification(i2: unknown, j2: unknown, j3: unknown, arrayList: unknown) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
onTempChatInfoUpdate(tempChatInfo: TempOnRecvParams) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
onUnreadCntAfterFirstView(hashMap: unknown) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
onUnreadCntUpdate(hashMap: unknown) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
onUserChannelTabStatusChanged(z: unknown) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
onUserOnlineStatusChanged(z: unknown) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
onUserTabStatusChanged(arrayList: unknown) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
onlineStatusBigIconDownloadPush(i2: unknown, j2: unknown, str: unknown) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
onlineStatusSmallIconDownloadPush(i2: unknown, j2: unknown, str: unknown) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// 第一次发现于Linux
|
||||||
|
onUserSecQualityChanged(...args: unknown[]) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
onMsgWithRichLinkInfoUpdate(...args: unknown[]) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
onRedTouchChanged(...args: unknown[]) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// 第一次发现于Win 9.9.9-23159
|
||||||
|
onBroadcastHelperProgerssUpdate(...args: unknown[]) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
46
src/core/listeners/NodeIKernelProfileListener.ts
Normal file
46
src/core/listeners/NodeIKernelProfileListener.ts
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
import { User, UserDetailInfoListenerArg } from '@/core/entities';
|
||||||
|
|
||||||
|
interface IProfileListener {
|
||||||
|
onProfileSimpleChanged(...args: unknown[]): void;
|
||||||
|
|
||||||
|
onUserDetailInfoChanged(arg: UserDetailInfoListenerArg): void;
|
||||||
|
|
||||||
|
onProfileDetailInfoChanged(profile: User): void;
|
||||||
|
|
||||||
|
onStatusUpdate(...args: unknown[]): void;
|
||||||
|
|
||||||
|
onSelfStatusChanged(...args: unknown[]): void;
|
||||||
|
|
||||||
|
onStrangerRemarkChanged(...args: unknown[]): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface NodeIKernelProfileListener extends IProfileListener {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-misused-new
|
||||||
|
new(listener: IProfileListener): NodeIKernelProfileListener;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ProfileListener implements IProfileListener {
|
||||||
|
onUserDetailInfoChanged(arg: UserDetailInfoListenerArg): void {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
onProfileSimpleChanged(...args: unknown[]) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
onProfileDetailInfoChanged(profile: User) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
onStatusUpdate(...args: unknown[]) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
onSelfStatusChanged(...args: unknown[]) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
onStrangerRemarkChanged(...args: unknown[]) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
44
src/core/listeners/NodeIKernelRecentContactListener.ts
Normal file
44
src/core/listeners/NodeIKernelRecentContactListener.ts
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
interface IKernelRecentContactListener {
|
||||||
|
onDeletedContactsNotify(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
onRecentContactNotification(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
onMsgUnreadCountUpdate(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
onGuildDisplayRecentContactListChanged(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
onRecentContactListChanged(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
onRecentContactListChangedVer2(...args: unknown[]): unknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface NodeIKernelRecentContactListener extends IKernelRecentContactListener {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-misused-new
|
||||||
|
new(listener: IKernelRecentContactListener): NodeIKernelRecentContactListener;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class KernelRecentContactListener implements IKernelRecentContactListener {
|
||||||
|
onDeletedContactsNotify(...args: unknown[]) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
onRecentContactNotification(...args: unknown[]) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
onMsgUnreadCountUpdate(...args: unknown[]) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
onGuildDisplayRecentContactListChanged(...args: unknown[]) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
onRecentContactListChanged(...args: unknown[]) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
onRecentContactListChangedVer2(...args: unknown[]) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
26
src/core/listeners/NodeIKernelRobotListener.ts
Normal file
26
src/core/listeners/NodeIKernelRobotListener.ts
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
export interface IKernelRobotListener {
|
||||||
|
onRobotFriendListChanged(...args: unknown[]): void;
|
||||||
|
|
||||||
|
onRobotListChanged(...args: unknown[]): void;
|
||||||
|
|
||||||
|
onRobotProfileChanged(...args: unknown[]): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface NodeIKernelRobotListener extends IKernelRobotListener {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-misused-new
|
||||||
|
new(adapter: IKernelRobotListener): NodeIKernelRobotListener;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class KernelRobotListener implements IKernelRobotListener {
|
||||||
|
onRobotFriendListChanged(...args: unknown[]) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
onRobotListChanged(...args: unknown[]) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
onRobotProfileChanged(...args: unknown[]) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
44
src/core/listeners/NodeIKernelSessionListener.ts
Normal file
44
src/core/listeners/NodeIKernelSessionListener.ts
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
export interface ISessionListener {
|
||||||
|
onNTSessionCreate(args: unknown): void;
|
||||||
|
|
||||||
|
onGProSessionCreate(args: unknown): void;
|
||||||
|
|
||||||
|
onSessionInitComplete(args: unknown): void;
|
||||||
|
|
||||||
|
onOpentelemetryInit(args: unknown): void;
|
||||||
|
|
||||||
|
onUserOnlineResult(args: unknown): void;
|
||||||
|
|
||||||
|
onGetSelfTinyId(args: unknown): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface NodeIKernelSessionListener extends ISessionListener {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-misused-new
|
||||||
|
new(adapter: ISessionListener): NodeIKernelSessionListener;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class SessionListener implements ISessionListener {
|
||||||
|
onNTSessionCreate(args: unknown) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
onGProSessionCreate(args: unknown) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
onSessionInitComplete(args: unknown) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
onOpentelemetryInit(args: unknown) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
onUserOnlineResult(args: unknown) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
onGetSelfTinyId(args: unknown) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
39
src/core/listeners/NodeIKernelStorageCleanListener.ts
Normal file
39
src/core/listeners/NodeIKernelStorageCleanListener.ts
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
export interface IStorageCleanListener {
|
||||||
|
onCleanCacheProgressChanged(args: unknown): void;
|
||||||
|
|
||||||
|
onScanCacheProgressChanged(args: unknown): void;
|
||||||
|
|
||||||
|
onCleanCacheStorageChanged(args: unknown): void;
|
||||||
|
|
||||||
|
onFinishScan(args: unknown): void;
|
||||||
|
|
||||||
|
onChatCleanDone(args: unknown): void;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface NodeIKernelStorageCleanListener extends IStorageCleanListener {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-misused-new
|
||||||
|
new(adapter: IStorageCleanListener): NodeIKernelStorageCleanListener;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class StorageCleanListener implements IStorageCleanListener {
|
||||||
|
onCleanCacheProgressChanged(args: unknown) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
onScanCacheProgressChanged(args: unknown) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
onCleanCacheStorageChanged(args: unknown) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
onFinishScan(args: unknown) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
onChatCleanDone(args: unknown) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
10
src/core/listeners/NodeIKernelTicketListener.ts
Normal file
10
src/core/listeners/NodeIKernelTicketListener.ts
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
export interface IKernelTicketListener {
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface NodeIKernelTicketListener extends IKernelTicketListener {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-misused-new
|
||||||
|
new(adapter: IKernelTicketListener): NodeIKernelTicketListener;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class KernelTicketListener implements IKernelTicketListener {
|
||||||
|
}
|
||||||
12
src/core/listeners/index.ts
Normal file
12
src/core/listeners/index.ts
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
export * from './NodeIKernelSessionListener';
|
||||||
|
export * from './NodeIKernelLoginListener';
|
||||||
|
export * from './NodeIKernelMsgListener';
|
||||||
|
export * from './NodeIKernelGroupListener';
|
||||||
|
export * from './NodeIKernelBuddyListener';
|
||||||
|
export * from './NodeIKernelProfileListener';
|
||||||
|
export * from './NodeIKernelRobotListener';
|
||||||
|
export * from './NodeIKernelProfileListener';
|
||||||
|
export * from './NodeIKernelTicketListener';
|
||||||
|
export * from './NodeIKernelStorageCleanListener';
|
||||||
|
export * from './NodeIKernelFileAssistantListener';
|
||||||
|
|
||||||
46
src/core/services/NodeIKernelAlbumService.ts
Normal file
46
src/core/services/NodeIKernelAlbumService.ts
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
export interface NodeIKernelAlbumService {
|
||||||
|
setAlbumServiceInfo(...args: any[]): unknown;// needs 3 arguments
|
||||||
|
|
||||||
|
getMainPage(...args: any[]): unknown;// needs 2 arguments
|
||||||
|
|
||||||
|
getAlbumList(...args: any[]): unknown;// needs 1 arguments
|
||||||
|
|
||||||
|
getAlbumInfo(...args: any[]): unknown;// needs 1 arguments
|
||||||
|
|
||||||
|
deleteAlbum(...args: any[]): unknown;// needs 3 arguments
|
||||||
|
|
||||||
|
addAlbum(...args: any[]): unknown;// needs 2 arguments
|
||||||
|
|
||||||
|
deleteMedias(...args: any[]): unknown;// needs 4 arguments
|
||||||
|
|
||||||
|
modifyAlbum(...args: any[]): unknown;// needs 3 arguments
|
||||||
|
|
||||||
|
getMediaList(...args: any[]): unknown;// needs 1 arguments
|
||||||
|
|
||||||
|
quoteToQzone(...args: any[]): unknown;// needs 1 arguments
|
||||||
|
|
||||||
|
quoteToQunAlbum(...args: any[]): unknown;// needs 1 arguments
|
||||||
|
|
||||||
|
queryQuoteToQunAlbumStatus(...args: any[]): unknown;// needs 1 arguments
|
||||||
|
|
||||||
|
getQunFeeds(...args: any[]): unknown;//needs 1 arguments
|
||||||
|
|
||||||
|
getQunFeedDetail(...args: any[]): unknown;// needs 1 arguments
|
||||||
|
|
||||||
|
getQunNoticeList(...args: any[]): unknown;// needs 4 arguments
|
||||||
|
|
||||||
|
getQunComment(...args: any[]): unknown;// needs 1 arguments
|
||||||
|
|
||||||
|
getQunLikes(...args: any[]): unknown;// needs 4 arguments
|
||||||
|
|
||||||
|
deleteQunFeed(...args: any[]): unknown;// needs 1 arguments
|
||||||
|
|
||||||
|
doQunComment(...args: any[]): unknown;// needs 6 arguments
|
||||||
|
|
||||||
|
doQunReply(...args: any[]): unknown;// needs 7 arguments
|
||||||
|
|
||||||
|
doQunLike(...args: any[]): unknown;// needs 5 arguments
|
||||||
|
|
||||||
|
getRedPoints(...args: any[]): unknown;// needs 3 arguments
|
||||||
|
|
||||||
|
}
|
||||||
31
src/core/services/NodeIKernelAvatarService.ts
Normal file
31
src/core/services/NodeIKernelAvatarService.ts
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
export interface NodeIKernelAvatarService {
|
||||||
|
addAvatarListener(arg: unknown): unknown;
|
||||||
|
|
||||||
|
removeAvatarListener(arg: unknown): unknown;
|
||||||
|
|
||||||
|
getAvatarPath(arg1: unknown, arg2: unknown): unknown;
|
||||||
|
|
||||||
|
forceDownloadAvatar(uid: string, useCache: number): Promise<unknown>;
|
||||||
|
|
||||||
|
getGroupAvatarPath(arg1: unknown, arg2: unknown): unknown;
|
||||||
|
|
||||||
|
getConfGroupAvatarPath(arg: unknown): unknown;
|
||||||
|
|
||||||
|
forceDownloadGroupAvatar(arg1: unknown, arg2: unknown): unknown;
|
||||||
|
|
||||||
|
getGroupPortraitPath(arg1: unknown, arg2: unknown, arg3: unknown): unknown;
|
||||||
|
|
||||||
|
forceDownloadGroupPortrait(arg1: unknown, arg2: unknown, arg3: unknown): unknown;
|
||||||
|
|
||||||
|
getAvatarPaths(arg1: unknown, arg2: unknown): unknown;
|
||||||
|
|
||||||
|
getGroupAvatarPaths(arg1: unknown, arg2: unknown): unknown;
|
||||||
|
|
||||||
|
getConfGroupAvatarPaths(arg: unknown): unknown;
|
||||||
|
|
||||||
|
getAvatarPathByUin(arg1: unknown, arg2: unknown): unknown;
|
||||||
|
|
||||||
|
forceDownloadAvatarByUin(arg1: unknown, arg2: unknown): unknown;
|
||||||
|
|
||||||
|
isNull(): boolean;
|
||||||
|
}
|
||||||
131
src/core/services/NodeIKernelBuddyService.ts
Normal file
131
src/core/services/NodeIKernelBuddyService.ts
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
import { GeneralCallResult } from '@/core/services/common';
|
||||||
|
import { NodeIKernelBuddyListener } from '@/core/listeners';
|
||||||
|
|
||||||
|
export enum BuddyListReqType {
|
||||||
|
KNOMAL,
|
||||||
|
KLETTER
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface NodeIKernelBuddyService {
|
||||||
|
// 26702 以上
|
||||||
|
getBuddyListV2(callFrom: string, reqType: BuddyListReqType): Promise<GeneralCallResult & {
|
||||||
|
data: Array<{
|
||||||
|
categoryId: number,
|
||||||
|
categorySortId: number,
|
||||||
|
categroyName: string,
|
||||||
|
categroyMbCount: number,
|
||||||
|
onlineCount: number,
|
||||||
|
buddyUids: Array<string>
|
||||||
|
}>
|
||||||
|
}>;
|
||||||
|
|
||||||
|
//26702 以上
|
||||||
|
getBuddyListFromCache(callFrom: string): Promise<Array<
|
||||||
|
{
|
||||||
|
categoryId: number,//9999应该跳过 那是兜底数据吧
|
||||||
|
categorySortId: number,//排序方式
|
||||||
|
categroyName: string,//分类名
|
||||||
|
categroyMbCount: number,//不懂
|
||||||
|
onlineCount: number,//在线数目
|
||||||
|
buddyUids: Array<string>//Uids
|
||||||
|
}>>;
|
||||||
|
|
||||||
|
// 以下为原生方法
|
||||||
|
addKernelBuddyListener(listener: NodeIKernelBuddyListener): number;
|
||||||
|
|
||||||
|
getAllBuddyCount(): number;
|
||||||
|
|
||||||
|
removeKernelBuddyListener(listener: unknown): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated
|
||||||
|
* @param nocache 使用缓存
|
||||||
|
*/
|
||||||
|
getBuddyList(nocache: boolean): Promise<GeneralCallResult>;
|
||||||
|
|
||||||
|
getBuddyNick(uid: number): string;
|
||||||
|
|
||||||
|
getBuddyRemark(uid: number): string;
|
||||||
|
|
||||||
|
setBuddyRemark(uid: number, remark: string): void;
|
||||||
|
|
||||||
|
getAvatarUrl(uid: number): string;
|
||||||
|
|
||||||
|
isBuddy(uid: string): boolean;
|
||||||
|
|
||||||
|
getCategoryNameWithUid(uid: number): string;
|
||||||
|
|
||||||
|
getTargetBuddySetting(uid: number): unknown;
|
||||||
|
|
||||||
|
getTargetBuddySettingByType(uid: number, type: number): unknown;
|
||||||
|
|
||||||
|
getBuddyReqUnreadCnt(): number;
|
||||||
|
|
||||||
|
getBuddyReq(): unknown;
|
||||||
|
|
||||||
|
delBuddyReq(uid: number): void;
|
||||||
|
|
||||||
|
clearBuddyReqUnreadCnt(): void;
|
||||||
|
|
||||||
|
reqToAddFriends(uid: number, msg: string): void;
|
||||||
|
|
||||||
|
setSpacePermission(uid: number, permission: number): void;
|
||||||
|
|
||||||
|
approvalFriendRequest(arg: {
|
||||||
|
friendUid: string;
|
||||||
|
reqTime: string;
|
||||||
|
accept: boolean;
|
||||||
|
}): Promise<void>;
|
||||||
|
|
||||||
|
delBuddy(uid: number): void;
|
||||||
|
|
||||||
|
delBatchBuddy(uids: number[]): void;
|
||||||
|
|
||||||
|
getSmartInfos(uid: number): unknown;
|
||||||
|
|
||||||
|
setBuddyCategory(uid: number, category: number): void;
|
||||||
|
|
||||||
|
setBatchBuddyCategory(uids: number[], category: number): void;
|
||||||
|
|
||||||
|
addCategory(category: string): void;
|
||||||
|
|
||||||
|
delCategory(category: string): void;
|
||||||
|
|
||||||
|
renameCategory(oldCategory: string, newCategory: string): void;
|
||||||
|
|
||||||
|
resortCategory(categorys: string[]): void;
|
||||||
|
|
||||||
|
pullCategory(uid: number, category: string): void;
|
||||||
|
|
||||||
|
setTop(uid: number, isTop: boolean): void;
|
||||||
|
|
||||||
|
SetSpecialCare(uid: number, isSpecialCare: boolean): void;
|
||||||
|
|
||||||
|
setMsgNotify(uid: number, isNotify: boolean): void;
|
||||||
|
|
||||||
|
hasBuddyList(): boolean;
|
||||||
|
|
||||||
|
setBlock(uid: number, isBlock: boolean): void;
|
||||||
|
|
||||||
|
isBlocked(uid: number): boolean;
|
||||||
|
|
||||||
|
modifyAddMeSetting(setting: unknown): void;
|
||||||
|
|
||||||
|
getAddMeSetting(): unknown;
|
||||||
|
|
||||||
|
getDoubtBuddyReq(): unknown;
|
||||||
|
|
||||||
|
getDoubtBuddyUnreadNum(): number;
|
||||||
|
|
||||||
|
approvalDoubtBuddyReq(uid: number, isAgree: boolean): void;
|
||||||
|
|
||||||
|
delDoubtBuddyReq(uid: number): void;
|
||||||
|
|
||||||
|
delAllDoubtBuddyReq(): void;
|
||||||
|
|
||||||
|
reportDoubtBuddyReqUnread(): void;
|
||||||
|
|
||||||
|
getBuddyRecommendContactArkJson(uid: string, phoneNumber: string): Promise<unknown>;
|
||||||
|
|
||||||
|
isNull(): boolean;
|
||||||
|
}
|
||||||
91
src/core/services/NodeIKernelCollectionService.ts
Normal file
91
src/core/services/NodeIKernelCollectionService.ts
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
import { GeneralCallResult } from './common';
|
||||||
|
|
||||||
|
export interface NodeIKernelCollectionService {
|
||||||
|
addKernelCollectionListener(...args: any[]): unknown;//needs 1 arguments
|
||||||
|
|
||||||
|
removeKernelCollectionListener(...args: any[]): unknown;//needs 1 arguments
|
||||||
|
|
||||||
|
getCollectionItemList(param: {
|
||||||
|
category: number,
|
||||||
|
groupId: number,
|
||||||
|
forceSync: boolean,
|
||||||
|
forceFromDb: boolean,
|
||||||
|
timeStamp: string,
|
||||||
|
count: number,
|
||||||
|
searchDown: boolean
|
||||||
|
}): Promise<GeneralCallResult &
|
||||||
|
{
|
||||||
|
collectionSearchList: {
|
||||||
|
collectionItemList: Array<
|
||||||
|
{
|
||||||
|
cid: string,
|
||||||
|
type: number,
|
||||||
|
status: number,
|
||||||
|
author: {
|
||||||
|
type: number,
|
||||||
|
numId: string,
|
||||||
|
strId: string,
|
||||||
|
groupId: string,
|
||||||
|
groupName: string,
|
||||||
|
uid: string
|
||||||
|
},
|
||||||
|
bid: number,
|
||||||
|
category: number,
|
||||||
|
createTime: string,
|
||||||
|
collectTime: string,
|
||||||
|
modifyTime: string,
|
||||||
|
sequence: string,
|
||||||
|
shareUrl: string,
|
||||||
|
customGroupId: number,
|
||||||
|
securityBeat: boolean,
|
||||||
|
summary: {
|
||||||
|
textSummary: unknown,
|
||||||
|
linkSummary: unknown,
|
||||||
|
gallerySummary: unknown,
|
||||||
|
audioSummary: unknown,
|
||||||
|
videoSummary: unknown,
|
||||||
|
fileSummary: unknown,
|
||||||
|
locationSummary: unknown,
|
||||||
|
richMediaSummary: unknown,
|
||||||
|
}
|
||||||
|
}>,
|
||||||
|
hasMore: boolean,
|
||||||
|
bottomTimeStamp: string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
>;//needs 1 arguments
|
||||||
|
|
||||||
|
getCollectionContent(...args: any[]): unknown;//needs 5 arguments
|
||||||
|
|
||||||
|
getCollectionCustomGroupList(...args: any[]): unknown;//needs 0 arguments
|
||||||
|
|
||||||
|
getCollectionUserInfo(...args: any[]): unknown;//needs 0 arguments
|
||||||
|
|
||||||
|
searchCollectionItemList(...args: any[]): unknown;//needs 2 arguments
|
||||||
|
|
||||||
|
addMsgToCollection(...args: any[]): unknown;//needs 2 arguments
|
||||||
|
|
||||||
|
collectionArkShare(...args: any[]): unknown;//needs 1 arguments
|
||||||
|
|
||||||
|
collectionFileForward(...args: any[]): unknown;//needs 3 arguments
|
||||||
|
|
||||||
|
downloadCollectionFile(...args: any[]): unknown;//needs 4 arguments
|
||||||
|
|
||||||
|
downloadCollectionFileThumbPic(...args: any[]): unknown;//needs 4 arguments
|
||||||
|
|
||||||
|
downloadCollectionPic(...args: any[]): unknown;//needs 3 arguments
|
||||||
|
|
||||||
|
cancelDownloadCollectionFile(...args: any[]): unknown;//needs 1 arguments
|
||||||
|
|
||||||
|
deleteCollectionItemList(...args: any[]): unknown;//needs 1 arguments
|
||||||
|
|
||||||
|
editCollectionItem(...args: any[]): unknown;//needs 2 arguments
|
||||||
|
|
||||||
|
getEditPicInfoByPath(...args: any[]): unknown;//needs 1 arguments
|
||||||
|
|
||||||
|
collectionFastUpload(...args: any[]): unknown;//needs 1 arguments
|
||||||
|
|
||||||
|
editCollectionItemAfterFastUpload(...args: any[]): unknown;//needs 2 arguments
|
||||||
|
|
||||||
|
createNewCollectionItem(...args: any[]): unknown;//needs 1 arguments
|
||||||
|
}
|
||||||
7
src/core/services/NodeIKernelDbToolsService.ts
Normal file
7
src/core/services/NodeIKernelDbToolsService.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
export interface NodeIKernelDbToolsService {
|
||||||
|
depositDatabase(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
backupDatabase(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
retrieveDatabase(...args: unknown[]): unknown;
|
||||||
|
}
|
||||||
3
src/core/services/NodeIKernelECDHService.ts
Normal file
3
src/core/services/NodeIKernelECDHService.ts
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
export interface NodeIKernelECDHService {
|
||||||
|
|
||||||
|
}
|
||||||
35
src/core/services/NodeIKernelFileAssistantService.ts
Normal file
35
src/core/services/NodeIKernelFileAssistantService.ts
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
export interface NodeIKernelFileAssistantService {
|
||||||
|
addKernelFileAssistantListener(arg1: unknown[]): unknown;
|
||||||
|
|
||||||
|
removeKernelFileAssistantListener(arg1: unknown[]): unknown;
|
||||||
|
|
||||||
|
getFileAssistantList(arg1: unknown[]): unknown;
|
||||||
|
|
||||||
|
getMoreFileAssistantList(arg1: unknown[]): unknown;
|
||||||
|
|
||||||
|
getFileSessionList(): unknown;
|
||||||
|
|
||||||
|
searchFile(arg1: unknown, arg2: unknown, arg3: unknown): unknown;
|
||||||
|
|
||||||
|
resetSearchFileSortType(arg1: unknown, arg2: unknown, arg3: unknown): unknown;
|
||||||
|
|
||||||
|
searchMoreFile(arg1: unknown[]): unknown;
|
||||||
|
|
||||||
|
cancelSearchFile(arg1: unknown, arg2: unknown, arg3: unknown): unknown;
|
||||||
|
|
||||||
|
downloadFile(arg1: unknown[]): unknown;
|
||||||
|
|
||||||
|
forwardFile(arg1: unknown, arg2: unknown, arg3: unknown): unknown;
|
||||||
|
|
||||||
|
cancelFileAction(arg1: unknown[]): unknown;
|
||||||
|
|
||||||
|
retryFileAction(arg1: unknown[]): unknown;
|
||||||
|
|
||||||
|
deleteFile(arg1: unknown[]): unknown;
|
||||||
|
|
||||||
|
saveAs(arg1: unknown, arg2: unknown): unknown;
|
||||||
|
|
||||||
|
saveAsWithRename(arg1: unknown, arg2: unknown, arg3: unknown): unknown;
|
||||||
|
|
||||||
|
isNull(): boolean;
|
||||||
|
}
|
||||||
264
src/core/services/NodeIKernelGroupService.ts
Normal file
264
src/core/services/NodeIKernelGroupService.ts
Normal file
@@ -0,0 +1,264 @@
|
|||||||
|
import { NodeIKernelGroupListener } from '@/core/listeners/NodeIKernelGroupListener';
|
||||||
|
import {
|
||||||
|
GroupExtParam,
|
||||||
|
GroupMember,
|
||||||
|
GroupMemberRole,
|
||||||
|
GroupNotifyTypes,
|
||||||
|
GroupRequestOperateTypes,
|
||||||
|
} from '@/core/entities';
|
||||||
|
import { GeneralCallResult } from '@/core/services/common';
|
||||||
|
|
||||||
|
//高版本的接口不应该随意使用 使用应该严格进行pr审核 同时部分ipc中未出现的接口不要过于依赖 应该做好数据兜底
|
||||||
|
|
||||||
|
export interface NodeIKernelGroupService {
|
||||||
|
getMemberCommonInfo(Req: {
|
||||||
|
groupCode: string,
|
||||||
|
startUin: string,
|
||||||
|
identifyFlag: string,
|
||||||
|
uinList: string[],
|
||||||
|
memberCommonFilter: {
|
||||||
|
memberUin: number,
|
||||||
|
uinFlag: number,
|
||||||
|
uinFlagExt: number,
|
||||||
|
uinMobileFlag: number,
|
||||||
|
shutUpTime: number,
|
||||||
|
privilege: number,
|
||||||
|
},
|
||||||
|
memberNum: number,
|
||||||
|
filterMethod: string,
|
||||||
|
onlineFlag: string,
|
||||||
|
realSpecialTitleFlag: number
|
||||||
|
}): Promise<unknown>;
|
||||||
|
|
||||||
|
//26702
|
||||||
|
getGroupMemberLevelInfo(groupCode: string): Promise<unknown>;
|
||||||
|
|
||||||
|
//26702
|
||||||
|
getGroupHonorList(groupCodes: Array<string>): unknown;
|
||||||
|
|
||||||
|
getUinByUids(uins: string[]): Promise<{
|
||||||
|
errCode: number,
|
||||||
|
errMsg: string,
|
||||||
|
uins: Map<string, string>
|
||||||
|
}>;
|
||||||
|
|
||||||
|
getUidByUins(uins: string[]): Promise<{
|
||||||
|
errCode: number,
|
||||||
|
errMsg: string,
|
||||||
|
uids: Map<string, string>
|
||||||
|
}>;
|
||||||
|
|
||||||
|
//26702(其实更早 但是我不知道)
|
||||||
|
checkGroupMemberCache(arrayList: Array<string>): Promise<unknown>;
|
||||||
|
|
||||||
|
//26702(其实更早 但是我不知道)
|
||||||
|
getGroupLatestEssenceList(groupCode: string): Promise<unknown>;
|
||||||
|
|
||||||
|
//26702(其实更早 但是我不知道)
|
||||||
|
shareDigest(Req: {
|
||||||
|
appId: string,
|
||||||
|
appType: number,
|
||||||
|
msgStyle: number,
|
||||||
|
recvUin: string,
|
||||||
|
sendType: number,
|
||||||
|
clientInfo: {
|
||||||
|
platform: number
|
||||||
|
},
|
||||||
|
richMsg: {
|
||||||
|
usingArk: boolean,
|
||||||
|
title: string,
|
||||||
|
summary: string,
|
||||||
|
url: string,
|
||||||
|
pictureUrl: string,
|
||||||
|
brief: string
|
||||||
|
}
|
||||||
|
}): Promise<unknown>;
|
||||||
|
|
||||||
|
//26702(其实更早 但是我不知道)
|
||||||
|
isEssenceMsg(Req: { groupCode: string, msgRandom: number, msgSeq: number }): Promise<unknown>;
|
||||||
|
|
||||||
|
//26702(其实更早 但是我不知道)
|
||||||
|
queryCachedEssenceMsg(Req: { groupCode: string, msgRandom: number, msgSeq: number }): Promise<unknown>;
|
||||||
|
|
||||||
|
//26702(其实更早 但是我不知道)
|
||||||
|
fetchGroupEssenceList(Req: {
|
||||||
|
groupCode: string,
|
||||||
|
pageStart: number,
|
||||||
|
pageLimit: number
|
||||||
|
}, Arg: unknown): Promise<unknown>;
|
||||||
|
|
||||||
|
//26702
|
||||||
|
getAllMemberList(groupCode: string, forceFetch: boolean): Promise<{
|
||||||
|
errCode: number,
|
||||||
|
errMsg: string,
|
||||||
|
result: {
|
||||||
|
ids: Array<{
|
||||||
|
uid: string,
|
||||||
|
index: number//0
|
||||||
|
}>,
|
||||||
|
infos: {},
|
||||||
|
finish: true,
|
||||||
|
hasRobot: false
|
||||||
|
}
|
||||||
|
}>;
|
||||||
|
|
||||||
|
setHeader(uid: string, path: string): unknown;
|
||||||
|
|
||||||
|
addKernelGroupListener(listener: NodeIKernelGroupListener): number;
|
||||||
|
|
||||||
|
removeKernelGroupListener(listenerId: unknown): void;
|
||||||
|
|
||||||
|
createMemberListScene(groupCode: string, scene: string): string;
|
||||||
|
|
||||||
|
destroyMemberListScene(SceneId: string): void;
|
||||||
|
|
||||||
|
//About Arg (a) name: lastId 根据手Q来看为object {index:?(number),uid:string}
|
||||||
|
getNextMemberList(sceneId: string, a: undefined, num: number): Promise<{
|
||||||
|
errCode: number, errMsg: string,
|
||||||
|
result: { ids: string[], infos: Map<string, GroupMember>, finish: boolean, hasRobot: boolean }
|
||||||
|
}>;
|
||||||
|
|
||||||
|
getPrevMemberList(): unknown;
|
||||||
|
|
||||||
|
monitorMemberList(): unknown;
|
||||||
|
|
||||||
|
searchMember(sceneId: string, keywords: string[]): unknown;
|
||||||
|
|
||||||
|
getMemberInfo(group_id: string, uids: string[], forceFetch: boolean): Promise<GeneralCallResult>;
|
||||||
|
|
||||||
|
//getMemberInfo [ '56729xxxx', [ 'u_4Nj08cwW5Hxxxxx' ], true ]
|
||||||
|
|
||||||
|
kickMember(groupCode: string, memberUids: string[], refuseForever: boolean, kickReason: string): Promise<void>;
|
||||||
|
|
||||||
|
modifyMemberRole(groupCode: string, uid: string, role: GroupMemberRole): void;
|
||||||
|
|
||||||
|
modifyMemberCardName(groupCode: string, uid: string, cardName: string): void;
|
||||||
|
|
||||||
|
getTransferableMemberInfo(groupCode: string): unknown;//获取整个群的
|
||||||
|
|
||||||
|
transferGroup(uid: string): void;
|
||||||
|
|
||||||
|
getGroupList(force: boolean): Promise<GeneralCallResult>;
|
||||||
|
|
||||||
|
getGroupExtList(force: boolean): Promise<GeneralCallResult>;
|
||||||
|
|
||||||
|
getGroupDetailInfo(groupCode: string): unknown;
|
||||||
|
|
||||||
|
getMemberExtInfo(param: GroupExtParam): Promise<unknown>;//req
|
||||||
|
|
||||||
|
getGroupAllInfo(): unknown;
|
||||||
|
|
||||||
|
getDiscussExistInfo(): unknown;
|
||||||
|
|
||||||
|
getGroupConfMember(): unknown;
|
||||||
|
|
||||||
|
getGroupMsgMask(): unknown;
|
||||||
|
|
||||||
|
getGroupPortrait(): void;
|
||||||
|
|
||||||
|
modifyGroupName(groupCode: string, groupName: string, arg: false): void;
|
||||||
|
|
||||||
|
modifyGroupRemark(groupCode: string, remark: string): void;
|
||||||
|
|
||||||
|
modifyGroupDetailInfo(groupCode: string, arg: unknown): void;
|
||||||
|
|
||||||
|
setGroupMsgMask(groupCode: string, arg: unknown): void;
|
||||||
|
|
||||||
|
changeGroupShieldSettingTemp(groupCode: string, arg: unknown): void;
|
||||||
|
|
||||||
|
inviteToGroup(arg: unknown): void;
|
||||||
|
|
||||||
|
inviteMembersToGroup(args: unknown[]): void;
|
||||||
|
|
||||||
|
inviteMembersToGroupWithMsg(args: unknown): void;
|
||||||
|
|
||||||
|
createGroup(arg: unknown): void;
|
||||||
|
|
||||||
|
createGroupWithMembers(arg: unknown): void;
|
||||||
|
|
||||||
|
quitGroup(groupCode: string): void;
|
||||||
|
|
||||||
|
destroyGroup(groupCode: string): void;
|
||||||
|
|
||||||
|
//获取单屏群通知列表
|
||||||
|
getSingleScreenNotifies(force: boolean, start_seq: string, num: number): Promise<GeneralCallResult>;
|
||||||
|
|
||||||
|
clearGroupNotifies(groupCode: string): void;
|
||||||
|
|
||||||
|
getGroupNotifiesUnreadCount(unknown: boolean): Promise<GeneralCallResult>;
|
||||||
|
|
||||||
|
clearGroupNotifiesUnreadCount(groupCode: string): void;
|
||||||
|
|
||||||
|
operateSysNotify(
|
||||||
|
doubt: boolean,
|
||||||
|
operateMsg: {
|
||||||
|
operateType: GroupRequestOperateTypes, // 2 拒绝
|
||||||
|
targetMsg: {
|
||||||
|
seq: string, // 通知序列号
|
||||||
|
type: GroupNotifyTypes,
|
||||||
|
groupCode: string,
|
||||||
|
postscript: string
|
||||||
|
}
|
||||||
|
}): Promise<void>;
|
||||||
|
|
||||||
|
setTop(groupCode: string, isTop: boolean): void;
|
||||||
|
|
||||||
|
getGroupBulletin(groupCode: string): unknown;
|
||||||
|
|
||||||
|
deleteGroupBulletin(groupCode: string, seq: string): void;
|
||||||
|
|
||||||
|
publishGroupBulletin(groupCode: string, pskey: string, data: any): Promise<GeneralCallResult>;
|
||||||
|
|
||||||
|
publishInstructionForNewcomers(groupCode: string, arg: unknown): void;
|
||||||
|
|
||||||
|
uploadGroupBulletinPic(groupCode: string, pskey: string, imagePath: string): Promise<GeneralCallResult & {
|
||||||
|
errCode: number;
|
||||||
|
picInfo?: {
|
||||||
|
id: string,
|
||||||
|
width: number,
|
||||||
|
height: number
|
||||||
|
}
|
||||||
|
}>;
|
||||||
|
|
||||||
|
downloadGroupBulletinRichMedia(groupCode: string): unknown;
|
||||||
|
|
||||||
|
getGroupBulletinList(groupCode: string): unknown;
|
||||||
|
|
||||||
|
getGroupStatisticInfo(groupCode: string): unknown;
|
||||||
|
|
||||||
|
getGroupRemainAtTimes(groupCode: string): number;
|
||||||
|
|
||||||
|
getJoinGroupNoVerifyFlag(groupCode: string): unknown;
|
||||||
|
|
||||||
|
getGroupArkInviteState(groupCode: string): unknown;
|
||||||
|
|
||||||
|
reqToJoinGroup(groupCode: string, arg: unknown): void;
|
||||||
|
|
||||||
|
setGroupShutUp(groupCode: string, shutUp: boolean): void;
|
||||||
|
|
||||||
|
getGroupShutUpMemberList(groupCode: string): unknown[];
|
||||||
|
|
||||||
|
setMemberShutUp(groupCode: string, memberTimes: { uid: string, timeStamp: number }[]): Promise<void>;
|
||||||
|
|
||||||
|
getGroupRecommendContactArkJson(groupCode: string): unknown;
|
||||||
|
|
||||||
|
getJoinGroupLink(groupCode: string): unknown;
|
||||||
|
|
||||||
|
modifyGroupExtInfo(groupCode: string, arg: unknown): void;
|
||||||
|
|
||||||
|
//需要提前判断是否存在 高版本新增
|
||||||
|
addGroupEssence(param: {
|
||||||
|
groupCode: string
|
||||||
|
msgRandom: number,
|
||||||
|
msgSeq: number
|
||||||
|
}): Promise<unknown>;
|
||||||
|
|
||||||
|
//需要提前判断是否存在 高版本新增
|
||||||
|
removeGroupEssence(param: {
|
||||||
|
groupCode: string
|
||||||
|
msgRandom: number,
|
||||||
|
msgSeq: number
|
||||||
|
}): Promise<unknown>;
|
||||||
|
|
||||||
|
isNull(): boolean;
|
||||||
|
}
|
||||||
83
src/core/services/NodeIKernelLoginService.ts
Normal file
83
src/core/services/NodeIKernelLoginService.ts
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
import { NodeIKernelLoginListener } from '@/core/listeners/NodeIKernelLoginListener';
|
||||||
|
|
||||||
|
export interface LoginInitConfig {
|
||||||
|
machineId: '';
|
||||||
|
appid: string;
|
||||||
|
platVer: string;
|
||||||
|
commonPath: string;
|
||||||
|
clientVer: string;
|
||||||
|
hostName: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface passwordLoginRetType {
|
||||||
|
result: string,
|
||||||
|
loginErrorInfo: {
|
||||||
|
step: number;
|
||||||
|
errMsg: string;
|
||||||
|
proofWaterUrl: string;
|
||||||
|
newDevicePullQrCodeSig: string;
|
||||||
|
jumpUrl: string,
|
||||||
|
jumpWord: string;
|
||||||
|
tipsTitle: string;
|
||||||
|
tipsContent: string;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface passwordLoginArgType {
|
||||||
|
uin: string;
|
||||||
|
passwordMd5: string;//passwMD5
|
||||||
|
step: number;//猜测是需要二次认证 参数 一次为0
|
||||||
|
newDeviceLoginSig: string;
|
||||||
|
proofWaterSig: string;
|
||||||
|
proofWaterRand: string;
|
||||||
|
proofWaterSid: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface LoginListItem {
|
||||||
|
uin: string;
|
||||||
|
uid: string;
|
||||||
|
nickName: string;
|
||||||
|
faceUrl: string;
|
||||||
|
facePath: string;
|
||||||
|
loginType: 1; // 1是二维码登录?
|
||||||
|
isQuickLogin: boolean; // 是否可以快速登录
|
||||||
|
isAutoLogin: boolean; // 是否可以自动登录
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface QuickLoginResult {
|
||||||
|
result: string;
|
||||||
|
loginErrorInfo: {
|
||||||
|
step: number,
|
||||||
|
errMsg: string,
|
||||||
|
proofWaterUrl: string,
|
||||||
|
newDevicePullQrCodeSig: string,
|
||||||
|
jumpUrl: string,
|
||||||
|
jumpWord: string,
|
||||||
|
tipsTitle: string,
|
||||||
|
tipsContent: string
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface NodeIKernelLoginService {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-misused-new
|
||||||
|
new(): NodeIKernelLoginService;
|
||||||
|
|
||||||
|
addKernelLoginListener(listener: NodeIKernelLoginListener): number;
|
||||||
|
|
||||||
|
removeKernelLoginListener(listener: number): void;
|
||||||
|
|
||||||
|
initConfig(config: LoginInitConfig): void;
|
||||||
|
|
||||||
|
getLoginMiscData(cb: (r: unknown) => void): void;
|
||||||
|
|
||||||
|
getLoginList(): Promise<{
|
||||||
|
result: number, // 0是ok
|
||||||
|
LocalLoginInfoList: LoginListItem[]
|
||||||
|
}>;
|
||||||
|
|
||||||
|
quickLoginWithUin(uin: string): Promise<QuickLoginResult>;
|
||||||
|
|
||||||
|
passwordLogin(param: passwordLoginArgType): Promise<any>;
|
||||||
|
|
||||||
|
getQRCodePicture(): boolean;
|
||||||
|
}
|
||||||
3
src/core/services/NodeIKernelMSFService.ts
Normal file
3
src/core/services/NodeIKernelMSFService.ts
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
export interface NodeIKernelMSFService {
|
||||||
|
getServerTime(): string;
|
||||||
|
}
|
||||||
27
src/core/services/NodeIKernelMsgBackupService.ts
Normal file
27
src/core/services/NodeIKernelMsgBackupService.ts
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
export interface NodeIKernelMsgBackupService {
|
||||||
|
addKernelMsgBackupListener(...args: any[]): unknown;// needs 1 arguments
|
||||||
|
|
||||||
|
removeKernelMsgBackupListener(...args: any[]): unknown;// needs 1 arguments
|
||||||
|
|
||||||
|
getMsgBackupLocation(...args: any[]): unknown;// needs 0 arguments
|
||||||
|
|
||||||
|
setMsgBackupLocation(...args: any[]): unknown;// needs 1 arguments
|
||||||
|
|
||||||
|
requestMsgBackup(...args: any[]): unknown;// needs 0 arguments
|
||||||
|
|
||||||
|
requestMsgRestore(...args: any[]): unknown;// needs 1 arguments
|
||||||
|
|
||||||
|
requestMsgMigrate(...args: any[]): unknown;// needs 1 arguments
|
||||||
|
|
||||||
|
getLocalStorageBackup(...args: any[]): unknown;// needs 0 arguments
|
||||||
|
|
||||||
|
deleteLocalBackup(...args: any[]): unknown;// needs 1 arguments
|
||||||
|
|
||||||
|
clearCache(...args: any[]): unknown;// needs 0 arguments
|
||||||
|
|
||||||
|
start(...args: any[]): unknown;// needs 1 arguments
|
||||||
|
|
||||||
|
stop(...args: any[]): unknown;// needs 1 arguments
|
||||||
|
|
||||||
|
pause(...args: any[]): unknown;// needs 2 arguments
|
||||||
|
}
|
||||||
758
src/core/services/NodeIKernelMsgService.ts
Normal file
758
src/core/services/NodeIKernelMsgService.ts
Normal file
@@ -0,0 +1,758 @@
|
|||||||
|
import { ElementType, MessageElement, Peer, RawMessage, SendMessageElement } from '@/core/entities';
|
||||||
|
import { NodeIKernelMsgListener } from '@/core/listeners/NodeIKernelMsgListener';
|
||||||
|
import { GeneralCallResult } from '@/core/services/common';
|
||||||
|
|
||||||
|
export interface QueryMsgsParams {
|
||||||
|
chatInfo: Peer,
|
||||||
|
filterMsgType: [],
|
||||||
|
filterSendersUid: string[],
|
||||||
|
filterMsgFromTime: string,
|
||||||
|
filterMsgToTime: string,
|
||||||
|
pageLimit: number,
|
||||||
|
isReverseOrder: boolean,
|
||||||
|
isIncludeCurrent: boolean
|
||||||
|
}
|
||||||
|
export interface TmpChatInfoApi {
|
||||||
|
errMsg: string;
|
||||||
|
result: number;
|
||||||
|
tmpChatInfo?: TmpChatInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TmpChatInfo {
|
||||||
|
chatType: number;
|
||||||
|
fromNick: string;
|
||||||
|
groupCode: string;
|
||||||
|
peerUid: string;
|
||||||
|
sessionType: number;
|
||||||
|
sig: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface NodeIKernelMsgService {
|
||||||
|
|
||||||
|
generateMsgUniqueId(chatType: number, time: string): string;
|
||||||
|
|
||||||
|
addKernelMsgListener(nodeIKernelMsgListener: NodeIKernelMsgListener): number;
|
||||||
|
|
||||||
|
sendMsg(msgId: string, peer: Peer, msgElements: SendMessageElement[], map: Map<any, any>): Promise<GeneralCallResult>;
|
||||||
|
|
||||||
|
recallMsg(peer: Peer, msgIds: string[]): Promise<GeneralCallResult>;
|
||||||
|
|
||||||
|
addKernelMsgImportToolListener(arg: Object): unknown;
|
||||||
|
|
||||||
|
removeKernelMsgListener(args: unknown): unknown;
|
||||||
|
|
||||||
|
addKernelTempChatSigListener(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
removeKernelTempChatSigListener(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
setAutoReplyTextList(AutoReplyText: Array<unknown>, i2: number): unknown;
|
||||||
|
|
||||||
|
getAutoReplyTextList(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
getOnLineDev(): void;
|
||||||
|
|
||||||
|
kickOffLine(DevInfo: Object): unknown;
|
||||||
|
|
||||||
|
setStatus(args: { status: number, extStatus: number, batteryStatus: number }): Promise<GeneralCallResult>;
|
||||||
|
|
||||||
|
fetchStatusMgrInfo(): unknown;
|
||||||
|
|
||||||
|
fetchStatusUnitedConfigInfo(): unknown;
|
||||||
|
|
||||||
|
getOnlineStatusSmallIconBasePath(): unknown;
|
||||||
|
|
||||||
|
getOnlineStatusSmallIconFileNameByUrl(Url: string): unknown;
|
||||||
|
|
||||||
|
downloadOnlineStatusSmallIconByUrl(arg0: number, arg1: string): unknown;
|
||||||
|
|
||||||
|
getOnlineStatusBigIconBasePath(): unknown;
|
||||||
|
|
||||||
|
downloadOnlineStatusBigIconByUrl(arg0: number, arg1: string): unknown;
|
||||||
|
|
||||||
|
getOnlineStatusCommonPath(arg: string): unknown;
|
||||||
|
|
||||||
|
getOnlineStatusCommonFileNameByUrl(Url: string): unknown;
|
||||||
|
|
||||||
|
downloadOnlineStatusCommonByUrl(arg0: string, arg1: string): unknown;
|
||||||
|
|
||||||
|
// this.tokenType = i2;
|
||||||
|
// this.apnsToken = bArr;
|
||||||
|
// this.voipToken = bArr2;
|
||||||
|
// this.profileId = str;
|
||||||
|
|
||||||
|
setToken(arg: Object): unknown;
|
||||||
|
|
||||||
|
switchForeGround(): unknown;
|
||||||
|
|
||||||
|
switchBackGround(arg: Object): unknown;
|
||||||
|
|
||||||
|
//hex
|
||||||
|
setTokenForMqq(token: string): unknown;
|
||||||
|
|
||||||
|
switchForeGroundForMqq(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
switchBackGroundForMqq(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
getMsgSetting(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
setMsgSetting(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
addSendMsg(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
cancelSendMsg(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
switchToOfflineSendMsg(peer: Peer, MsgId: string): unknown;
|
||||||
|
|
||||||
|
reqToOfflineSendMsg(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
refuseReceiveOnlineFileMsg(peer: Peer, MsgId: string): unknown;
|
||||||
|
|
||||||
|
resendMsg(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
recallMsg(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
reeditRecallMsg(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
forwardMsg(...args: unknown[]): Promise<GeneralCallResult>;
|
||||||
|
|
||||||
|
forwardMsgWithComment(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
forwardSubMsgWithComment(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
forwardRichMsgInVist(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
forwardFile(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
//Array<Msg>, Peer from, Peer to
|
||||||
|
multiForwardMsg(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
multiForwardMsgWithComment(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
deleteRecallMsg(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
deleteRecallMsgForLocal(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
addLocalGrayTipMsg(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
addLocalJsonGrayTipMsg(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
addLocalJsonGrayTipMsgExt(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
IsLocalJsonTipValid(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
addLocalAVRecordMsg(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
addLocalTofuRecordMsg(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
addLocalRecordMsg(Peer: Peer, msgId: string, ele: MessageElement, attr: Array<any> | number, front: boolean): Promise<unknown>;
|
||||||
|
|
||||||
|
deleteMsg(Peer: Peer, msgIds: Array<string>): Promise<any>;
|
||||||
|
|
||||||
|
updateElementExtBufForUI(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
updateMsgRecordExtPbBufForUI(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
startMsgSync(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
startGuildMsgSync(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
isGuildChannelSync(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
getMsgUniqueId(UniqueId: string): string;
|
||||||
|
|
||||||
|
isMsgMatched(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
getOnlineFileMsgs(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
getAllOnlineFileMsgs(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
getLatestDbMsgs(peer: Peer, cnt: number): Promise<unknown>;
|
||||||
|
|
||||||
|
getLastMessageList(peer: Peer[]): Promise<unknown>;
|
||||||
|
|
||||||
|
getAioFirstViewLatestMsgs(peer: Peer, num: number): unknown;
|
||||||
|
|
||||||
|
getMsgs(peer: Peer, msgId: string, count: unknown, queryOrder: boolean): Promise<unknown>;
|
||||||
|
|
||||||
|
getMsgsIncludeSelf(peer: Peer, msgId: string, count: number, queryOrder: boolean): Promise<GeneralCallResult & {
|
||||||
|
msgList: RawMessage[]
|
||||||
|
}>;
|
||||||
|
|
||||||
|
// this.$peer = contact;
|
||||||
|
// this.$msgTime = j2;
|
||||||
|
// this.$clientSeq = j3;
|
||||||
|
// this.$cnt = i2;
|
||||||
|
|
||||||
|
getMsgsWithMsgTimeAndClientSeqForC2C(...args: unknown[]): Promise<GeneralCallResult & { msgList: RawMessage[] }>;
|
||||||
|
|
||||||
|
getMsgsWithStatus(params: {
|
||||||
|
peer: Peer
|
||||||
|
msgId: string
|
||||||
|
msgTime: unknown
|
||||||
|
cnt: unknown
|
||||||
|
queryOrder: boolean
|
||||||
|
isIncludeSelf: boolean
|
||||||
|
appid: unknown
|
||||||
|
}): Promise<GeneralCallResult & { msgList: RawMessage[] }>;
|
||||||
|
|
||||||
|
getMsgsBySeqRange(peer: Peer, startSeq: string, endSeq: string): Promise<GeneralCallResult & {
|
||||||
|
msgList: RawMessage[]
|
||||||
|
}>;
|
||||||
|
|
||||||
|
getMsgsBySeqAndCount(peer: Peer, seq: string, count: number, desc: boolean, unknownArg: boolean): Promise<GeneralCallResult & {
|
||||||
|
msgList: RawMessage[]
|
||||||
|
}>;
|
||||||
|
|
||||||
|
getMsgsByMsgId(peer: Peer, ids: string[]): Promise<GeneralCallResult & { msgList: RawMessage[] }>;
|
||||||
|
|
||||||
|
getRecallMsgsByMsgId(peer: Peer, MsgId: string[]): Promise<unknown>;
|
||||||
|
|
||||||
|
getMsgsBySeqList(peer: Peer, seqList: string[]): Promise<GeneralCallResult & { msgList: RawMessage[] }>;
|
||||||
|
|
||||||
|
getSingleMsg(Peer: Peer, msgSeq: string): Promise<GeneralCallResult & { msgList: RawMessage[] }>;
|
||||||
|
|
||||||
|
getSourceOfReplyMsg(peer: Peer, MsgId: string, SourceSeq: string): unknown;
|
||||||
|
|
||||||
|
getSourceOfReplyMsgV2(peer: Peer, RootMsgId: string, ReplyMsgId: string): unknown;
|
||||||
|
|
||||||
|
getMsgByClientSeqAndTime(peer: Peer, clientSeq: string, time: string): unknown;
|
||||||
|
|
||||||
|
getSourceOfReplyMsgByClientSeqAndTime(peer: Peer, clientSeq: string, time: string): unknown;
|
||||||
|
|
||||||
|
//cnt clientSeq?并不是吧
|
||||||
|
getMsgsByTypeFilter(peer: Peer, msgId: string, cnt: unknown, queryOrder: boolean, typeFilter: {
|
||||||
|
type: number,
|
||||||
|
subtype: Array<number>
|
||||||
|
}): unknown;
|
||||||
|
|
||||||
|
getMsgsByTypeFilters(peer: Peer, msgId: string, cnt: unknown, queryOrder: boolean, typeFilters: Array<{
|
||||||
|
type: number,
|
||||||
|
subtype: Array<number>
|
||||||
|
}>): unknown;
|
||||||
|
|
||||||
|
getMsgWithAbstractByFilterParam(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
queryMsgsWithFilter(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated 该函数已被标记为废弃,请使用新的替代方法。
|
||||||
|
* 使用过滤条件查询消息列表的版本2接口。
|
||||||
|
*
|
||||||
|
* 该函数通过一系列过滤条件来查询特定聊天中的消息列表。这些条件包括消息类型、发送者、时间范围等。
|
||||||
|
* 函数返回一个Promise,解析为查询结果的未知类型对象。
|
||||||
|
*
|
||||||
|
* @param MsgId 消息ID,用于特定消息的查询。
|
||||||
|
* @param MsgTime 消息时间,用于指定消息的时间范围。
|
||||||
|
* @param param 查询参数对象,包含详细的过滤条件和分页信息。
|
||||||
|
* @param param.chatInfo 聊天信息,包括聊天类型和对方用户ID。
|
||||||
|
* @param param.filterMsgType 需要过滤的消息类型数组,留空表示不过滤。
|
||||||
|
* @param param.filterSendersUid 需要过滤的发送者用户ID数组。
|
||||||
|
* @param param.filterMsgFromTime 查询消息的起始时间。
|
||||||
|
* @param param.filterMsgToTime 查询消息的结束时间。
|
||||||
|
* @param param.pageLimit 每页的消息数量限制。
|
||||||
|
* @param param.isReverseOrder 是否按时间顺序倒序返回消息。
|
||||||
|
* @param param.isIncludeCurrent 是否包含当前页码。
|
||||||
|
* @returns 返回一个Promise,解析为查询结果的未知类型对象。
|
||||||
|
*/
|
||||||
|
queryMsgsWithFilterVer2(MsgId: string, MsgTime: string, param: QueryMsgsParams): Promise<unknown>;
|
||||||
|
|
||||||
|
// this.chatType = i2;
|
||||||
|
// this.peerUid = str;
|
||||||
|
|
||||||
|
// this.chatInfo = new ChatInfo();
|
||||||
|
// this.filterMsgType = new ArrayList<>();
|
||||||
|
// this.filterSendersUid = new ArrayList<>();
|
||||||
|
// this.chatInfo = chatInfo;
|
||||||
|
// this.filterMsgType = arrayList;
|
||||||
|
// this.filterSendersUid = arrayList2;
|
||||||
|
// this.filterMsgFromTime = j2;
|
||||||
|
// this.filterMsgToTime = j3;
|
||||||
|
// this.pageLimit = i2;
|
||||||
|
// this.isReverseOrder = z;
|
||||||
|
// this.isIncludeCurrent = z2;
|
||||||
|
//queryMsgsWithFilterEx(0L, 0L, 0L, new QueryMsgsParams(new ChatInfo(2, str), new ArrayList(), new ArrayList(), 0L, 0L, 250, false, true))
|
||||||
|
queryMsgsWithFilterEx(msgId: string, msgTime: string, megSeq: string, param: QueryMsgsParams): Promise<GeneralCallResult & {
|
||||||
|
msgList: RawMessage[]
|
||||||
|
}>;
|
||||||
|
|
||||||
|
//queryMsgsWithFilterEx(this.$msgId, this.$msgTime, this.$msgSeq, this.$param)
|
||||||
|
queryFileMsgsDesktop(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
setMsgRichInfoFlag(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
queryPicOrVideoMsgs(msgId: string, msgTime: string, megSeq: string, param: QueryMsgsParams): Promise<unknown>;
|
||||||
|
|
||||||
|
queryPicOrVideoMsgsDesktop(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
queryEmoticonMsgs(msgId: string, msgTime: string, msgSeq: string, Params: QueryMsgsParams): Promise<unknown>;
|
||||||
|
|
||||||
|
queryTroopEmoticonMsgs(msgId: string, msgTime: string, msgSeq: string, Params: QueryMsgsParams): Promise<unknown>;
|
||||||
|
|
||||||
|
queryMsgsAndAbstractsWithFilter(msgId: string, msgTime: string, megSeq: string, param: QueryMsgsParams): unknown;
|
||||||
|
|
||||||
|
setFocusOnGuild(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
setFocusSession(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
enableFilterUnreadInfoNotify(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
enableFilterMsgAbstractNotify(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
onScenesChangeForSilenceMode(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
getContactUnreadCnt(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
getUnreadCntInfo(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
getGuildUnreadCntInfo(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
getGuildUnreadCntTabInfo(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
getAllGuildUnreadCntInfo(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
getAllJoinGuildCnt(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
getAllDirectSessionUnreadCntInfo(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
getCategoryUnreadCntInfo(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
getGuildFeedsUnreadCntInfo(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
setUnVisibleChannelCntInfo(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
setUnVisibleChannelTypeCntInfo(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
setVisibleGuildCntInfo(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
setMsgRead(peer: Peer): Promise<GeneralCallResult>;
|
||||||
|
|
||||||
|
setAllC2CAndGroupMsgRead(): Promise<unknown>;
|
||||||
|
|
||||||
|
setGuildMsgRead(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
setAllGuildMsgRead(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
setMsgReadAndReport(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
setSpecificMsgReadAndReport(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
setLocalMsgRead(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
setGroupGuildMsgRead(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
getGuildGroupTransData(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
setGroupGuildBubbleRead(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
getGuildGroupBubble(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
fetchGroupGuildUnread(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
setGroupGuildFlag(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
setGuildUDCFlag(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
setGuildTabUserFlag(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
setBuildMode(flag: number/*0 1 3*/): unknown;
|
||||||
|
|
||||||
|
setConfigurationServiceData(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
setMarkUnreadFlag(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
getChannelEventFlow(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
getMsgEventFlow(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
getRichMediaFilePathForMobileQQSend(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
getRichMediaFilePathForGuild(arg: {
|
||||||
|
md5HexStr: string,
|
||||||
|
fileName: string,
|
||||||
|
elementType: ElementType,
|
||||||
|
elementSubType: number,
|
||||||
|
thumbSize: 0,
|
||||||
|
needCreate: true,
|
||||||
|
downloadType: 1,
|
||||||
|
file_uuid: ''
|
||||||
|
}): string;
|
||||||
|
|
||||||
|
assembleMobileQQRichMediaFilePath(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
getFileThumbSavePathForSend(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
getFileThumbSavePath(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
//猜测居多
|
||||||
|
translatePtt2Text(MsgId: string, Peer: {}, MsgElement: {}): unknown;
|
||||||
|
|
||||||
|
setPttPlayedState(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
// NodeIQQNTWrapperSession fetchFavEmojiList [
|
||||||
|
// "",
|
||||||
|
// 48,
|
||||||
|
// true,
|
||||||
|
// true
|
||||||
|
// ]
|
||||||
|
fetchFavEmojiList(str: string, num: number, uk1: boolean, uk2: boolean): Promise<GeneralCallResult & {
|
||||||
|
emojiInfoList: Array<{
|
||||||
|
uin: string,
|
||||||
|
emoId: number,
|
||||||
|
emoPath: string,
|
||||||
|
isExist: boolean,
|
||||||
|
resId: string,
|
||||||
|
url: string,
|
||||||
|
md5: string,
|
||||||
|
emoOriginalPath: string,
|
||||||
|
thumbPath: string,
|
||||||
|
RomaingType: string,
|
||||||
|
isAPNG: false,
|
||||||
|
isMarkFace: false,
|
||||||
|
eId: string,
|
||||||
|
epId: string,
|
||||||
|
ocrWord: string,
|
||||||
|
modifyWord: string,
|
||||||
|
exposeNum: number,
|
||||||
|
clickNum: number,
|
||||||
|
desc: string
|
||||||
|
}>
|
||||||
|
}>;
|
||||||
|
|
||||||
|
addFavEmoji(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
fetchMarketEmoticonList(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
fetchMarketEmoticonShowImage(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
fetchMarketEmoticonAioImage(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
fetchMarketEmotionJsonFile(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
getMarketEmoticonPath(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
getMarketEmoticonPathBySync(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
fetchMarketEmoticonFaceImages(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
fetchMarketEmoticonAuthDetail(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
getFavMarketEmoticonInfo(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
addRecentUsedFace(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
getRecentUsedFaceList(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
getMarketEmoticonEncryptKeys(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
downloadEmojiPic(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
deleteFavEmoji(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
modifyFavEmojiDesc(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
queryFavEmojiByDesc(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
getHotPicInfoListSearchString(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
getHotPicSearchResult(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
getHotPicHotWords(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
getHotPicJumpInfo(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
getEmojiResourcePath(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
JoinDragonGroupEmoji(JoinDragonGroupEmojiReq: any/*joinDragonGroupEmojiReq*/): unknown;
|
||||||
|
|
||||||
|
getMsgAbstracts(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
getMsgAbstract(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
getMsgAbstractList(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
getMsgAbstractListBySeqRange(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
refreshMsgAbstracts(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
refreshMsgAbstractsByGuildIds(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
getRichMediaElement(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
cancelGetRichMediaElement(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
refuseGetRichMediaElement(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
switchToOfflineGetRichMediaElement(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
downloadRichMedia(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
getFirstUnreadMsgSeq(args: {
|
||||||
|
peerUid: string
|
||||||
|
guildId: string
|
||||||
|
}): unknown;
|
||||||
|
|
||||||
|
getFirstUnreadCommonMsg(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
getFirstUnreadAtmeMsg(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
getFirstUnreadAtallMsg(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
getNavigateInfo(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
getChannelFreqLimitInfo(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
getRecentUseEmojiList(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
getRecentEmojiList(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
setMsgEmojiLikes(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
getMsgEmojiLikesList(peer: Peer, msgSeq: string, emojiId: string, emojiType: string, cookie: string, bForward: boolean, number: number): Promise<{
|
||||||
|
result: number,
|
||||||
|
errMsg: string,
|
||||||
|
emojiLikesList:
|
||||||
|
Array<{
|
||||||
|
tinyId: string,
|
||||||
|
nickName: string,
|
||||||
|
headUrl: string
|
||||||
|
}>,
|
||||||
|
cookie: string,
|
||||||
|
isLastPage: boolean,
|
||||||
|
isFirstPage: boolean
|
||||||
|
}>;
|
||||||
|
|
||||||
|
setMsgEmojiLikesForRole(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
clickInlineKeyboardButton(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
setCurOnScreenMsg(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
setCurOnScreenMsgForMsgEvent(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
getMiscData(key: string): unknown;
|
||||||
|
|
||||||
|
setMiscData(key: string, value: string): unknown;
|
||||||
|
|
||||||
|
getBookmarkData(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
setBookmarkData(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
sendShowInputStatusReq(ChatType: number, EventType: number, toUid: string): Promise<unknown>;
|
||||||
|
|
||||||
|
queryCalendar(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
queryFirstMsgSeq(peer: Peer, ...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
queryRoamCalendar(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
queryFirstRoamMsg(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
fetchLongMsg(peer: Peer, msgId: string): unknown;
|
||||||
|
|
||||||
|
fetchLongMsgWithCb(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
setIsStopKernelFetchLongMsg(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
insertGameResultAsMsgToDb(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
getMultiMsg(...args: unknown[]): Promise<GeneralCallResult & {
|
||||||
|
msgList: RawMessage[]
|
||||||
|
}>;
|
||||||
|
|
||||||
|
setDraft(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
getDraft(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
deleteDraft(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
getRecentHiddenSesionList(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
setRecentHiddenSession(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
delRecentHiddenSession(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
getCurHiddenSession(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
setCurHiddenSession(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
setReplyDraft(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
getReplyDraft(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
deleteReplyDraft(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
getFirstUnreadAtMsg(peer: Peer): unknown;
|
||||||
|
|
||||||
|
clearMsgRecords(...args: unknown[]): unknown;//设置已读后调用我觉得比较好 清理记录 现在别了
|
||||||
|
|
||||||
|
IsExistOldDb(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
canImportOldDbMsg(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
setPowerStatus(z: boolean): unknown;
|
||||||
|
|
||||||
|
canProcessDataMigration(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
importOldDbMsg(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
stopImportOldDbMsgAndroid(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
isMqqDataImportFinished(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
getMqqDataImportTableNames(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
getCurChatImportStatusByUin(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
getDataImportUserLevel(): unknown;
|
||||||
|
|
||||||
|
getMsgQRCode(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
getGuestMsgAbstracts(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
getGuestMsgByRange(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
getGuestMsgAbstractByRange(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
registerSysMsgNotification(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
unregisterSysMsgNotification(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
enterOrExitAio(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
// this.peerUid = "";
|
||||||
|
// this.peerNickname = "";
|
||||||
|
// this.fromGroupCode = "";
|
||||||
|
// this.sig = new byte[0];
|
||||||
|
// this.selfUid = "";
|
||||||
|
// this.selfPhone = "";
|
||||||
|
// this.chatType = i2;
|
||||||
|
// this.peerUid = str;
|
||||||
|
// this.peerNickname = str2;
|
||||||
|
// this.fromGroupCode = str3;
|
||||||
|
// this.sig = bArr;
|
||||||
|
// this.selfUid = str4;
|
||||||
|
// this.selfPhone = str5;
|
||||||
|
// this.gameSession = tempChatGameSession;
|
||||||
|
prepareTempChat(args: unknown): unknown;//主动临时消息 不做
|
||||||
|
|
||||||
|
sendSsoCmdReqByContend(cmd: string, param: string): Promise<unknown>;
|
||||||
|
|
||||||
|
//chattype,uid->Promise<any>
|
||||||
|
getTempChatInfo(ChatType: number, Uid: string): Promise<TmpChatInfoApi>;
|
||||||
|
|
||||||
|
setContactLocalTop(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
switchAnonymousChat(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
renameAnonyChatNick(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
getAnonymousInfo(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
updateAnonymousInfo(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
sendSummonMsg(peer: Peer, MsgElement: unknown, MsgAttributeInfo: unknown): Promise<unknown>;//频道的东西
|
||||||
|
|
||||||
|
outputGuildUnreadInfo(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
checkMsgWithUrl(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
checkTabListStatus(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
getABatchOfContactMsgBoxInfo(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
insertMsgToMsgBox(peer: Peer, msgId: string, arg: 2006): unknown;
|
||||||
|
|
||||||
|
isHitEmojiKeyword(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
getKeyWordRelatedEmoji(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
recordEmoji(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
fetchGetHitEmotionsByWord(args: Object): Promise<unknown>;//表情推荐?
|
||||||
|
|
||||||
|
deleteAllRoamMsgs(...args: unknown[]): unknown;//漫游消息?
|
||||||
|
|
||||||
|
packRedBag(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
grabRedBag(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
pullDetail(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
selectPasswordRedBag(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
pullRedBagPasswordList(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
requestTianshuAdv(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
tianshuReport(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
tianshuMultiReport(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
GetMsgSubType(a0: number, a1: number): unknown;
|
||||||
|
|
||||||
|
setIKernelPublicAccountAdapter(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
//tempChatGameSession有关
|
||||||
|
createUidFromTinyId(fromTinyId: string, toTinyId: string): unknown;
|
||||||
|
|
||||||
|
dataMigrationGetDataAvaiableContactList(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
dataMigrationGetMsgList(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
dataMigrationStopOperation(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
//新的希望
|
||||||
|
dataMigrationImportMsgPbRecord(DataMigrationMsgInfo: Array<{
|
||||||
|
extensionData: string//"Hex"
|
||||||
|
extraData: string //""
|
||||||
|
chatType: number
|
||||||
|
chatUin: string
|
||||||
|
msgType: number
|
||||||
|
msgTime: string
|
||||||
|
msgSeq: string
|
||||||
|
msgRandom: string
|
||||||
|
}>, DataMigrationResourceInfo: {
|
||||||
|
extraData: string
|
||||||
|
filePath: string
|
||||||
|
fileSize: string
|
||||||
|
msgRandom: string
|
||||||
|
msgSeq: string
|
||||||
|
msgSubType: number
|
||||||
|
msgType: number
|
||||||
|
}): unknown;
|
||||||
|
|
||||||
|
dataMigrationGetResourceLocalDestinyPath(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
dataMigrationSetIOSPathPrefix(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
getServiceAssistantSwitch(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
setServiceAssistantSwitch(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
setSubscribeFolderUsingSmallRedPoint(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
clearGuildNoticeRedPoint(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
clearFeedNoticeRedPoint(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
clearFeedSquareRead(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
IsC2CStyleChatType(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
IsTempChatType(uin: number): unknown;//猜的
|
||||||
|
|
||||||
|
getGuildInteractiveNotification(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
getGuildNotificationAbstract(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
setFocusOnBase(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
queryArkInfo(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
queryUserSecQuality(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
getGuildMsgAbFlag(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
getGroupMsgStorageTime(): unknown;//这是嘛啊
|
||||||
|
|
||||||
|
}
|
||||||
17
src/core/services/NodeIKernelNodeMiscService.ts
Normal file
17
src/core/services/NodeIKernelNodeMiscService.ts
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import { GeneralCallResult } from './common';
|
||||||
|
|
||||||
|
//没扒干净 因为用不着
|
||||||
|
export interface NodeIKernelNodeMiscService {
|
||||||
|
getMiniAppPath(): unknown;
|
||||||
|
|
||||||
|
setMiniAppVersion(version: string): unknown;
|
||||||
|
|
||||||
|
wantWinScreenOCR(imagepath: string): Promise<GeneralCallResult>;
|
||||||
|
|
||||||
|
SendMiniAppMsg(arg1: string, arg2: string, arg3: string): unknown;
|
||||||
|
|
||||||
|
startNewMiniApp(appfile: string, params: string): unknown;
|
||||||
|
|
||||||
|
// 我的计划是转发给一个新程序避免吃掉Electron_AS_Node的环境 然后重写启动MiniApp 挂载相应JS脚本 这样有个问题
|
||||||
|
// 需要自己转发ipc参数 然后必须处在gui环境 且完成校验破解 才能实现发包 有点抽象了
|
||||||
|
}
|
||||||
36
src/core/services/NodeIKernelOnlineStatusService.ts
Normal file
36
src/core/services/NodeIKernelOnlineStatusService.ts
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
export interface NodeIKernelOnlineStatusService {
|
||||||
|
|
||||||
|
addKernelOnlineStatusListener(listener: unknown): void;
|
||||||
|
|
||||||
|
removeKernelOnlineStatusListener(listenerId: unknown): void;
|
||||||
|
|
||||||
|
getShouldShowAIOStatusAnimation(arg: unknown): unknown;
|
||||||
|
|
||||||
|
setReadLikeList(arg: unknown): unknown;
|
||||||
|
|
||||||
|
getLikeList(arg: unknown): unknown;
|
||||||
|
|
||||||
|
setLikeStatus(arg: unknown): unknown;
|
||||||
|
|
||||||
|
getAggregationPageEntrance(): unknown;
|
||||||
|
|
||||||
|
didClickAggregationPageEntrance(): unknown;
|
||||||
|
|
||||||
|
getAggregationGroupModels(): unknown;
|
||||||
|
|
||||||
|
// {
|
||||||
|
// "businessType": 1,
|
||||||
|
// "uins": [
|
||||||
|
// "1627126029",
|
||||||
|
// "66600000",
|
||||||
|
// "71702575"
|
||||||
|
// ]
|
||||||
|
// }
|
||||||
|
|
||||||
|
checkLikeStatus(param: {
|
||||||
|
businessType: number,
|
||||||
|
uins: string[]
|
||||||
|
}): Promise<any>;
|
||||||
|
|
||||||
|
isNull(): boolean;
|
||||||
|
}
|
||||||
21
src/core/services/NodeIKernelProfileLikeService.ts
Normal file
21
src/core/services/NodeIKernelProfileLikeService.ts
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
import { BuddyProfileLikeReq, GeneralCallResult } from '@/core';
|
||||||
|
|
||||||
|
export interface NodeIKernelProfileLikeService {
|
||||||
|
addKernelProfileLikeListener(listener: NodeIKernelProfileLikeService): void;
|
||||||
|
|
||||||
|
removeKernelProfileLikeListener(listener: unknown): void;
|
||||||
|
|
||||||
|
setBuddyProfileLike(...args: unknown[]): { result: number, errMsg: string, succCounts: number };
|
||||||
|
|
||||||
|
getBuddyProfileLike(req: BuddyProfileLikeReq): Promise<GeneralCallResult & {
|
||||||
|
'info': {
|
||||||
|
'userLikeInfos': Array<any>,
|
||||||
|
'friendMaxVotes': number,
|
||||||
|
'start': number
|
||||||
|
}
|
||||||
|
}>;
|
||||||
|
|
||||||
|
getProfileLikeScidResourceInfo(...args: unknown[]): void;
|
||||||
|
|
||||||
|
isNull(): boolean;
|
||||||
|
}
|
||||||
109
src/core/services/NodeIKernelProfileService.ts
Normal file
109
src/core/services/NodeIKernelProfileService.ts
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
import { AnyCnameRecord } from 'node:dns';
|
||||||
|
import { BizKey, ModifyProfileParams, SimpleInfo, UserDetailInfoByUin } from '../entities';
|
||||||
|
import { NodeIKernelProfileListener } from '../listeners';
|
||||||
|
import { GeneralCallResult } from '@/core/services/common';
|
||||||
|
|
||||||
|
export enum UserDetailSource {
|
||||||
|
KDB,
|
||||||
|
KSERVER
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum ProfileBizType {
|
||||||
|
KALL,
|
||||||
|
KBASEEXTEND,
|
||||||
|
KVAS,
|
||||||
|
KQZONE,
|
||||||
|
KOTHER
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface NodeIKernelProfileService {
|
||||||
|
|
||||||
|
getUidByUinV2(callfrom: string, uin: Array<string>): Promise<Map<string, string>>;//uin->uid
|
||||||
|
|
||||||
|
getUinByUid(callfrom: string, uid: Array<string>): Promise<Map<string, string>>;
|
||||||
|
|
||||||
|
// {
|
||||||
|
// coreInfo: CoreInfo,
|
||||||
|
// baseInfo: BaseInfo,
|
||||||
|
// status: null,
|
||||||
|
// vasInfo: null,
|
||||||
|
// relationFlags: null,
|
||||||
|
// otherFlags: null,
|
||||||
|
// intimate: null
|
||||||
|
// }
|
||||||
|
getCoreAndBaseInfo(callfrom: string, uids: string[]): Promise<Map<string, SimpleInfo>>;
|
||||||
|
|
||||||
|
fetchUserDetailInfo(trace: string, uids: string[], arg2: number, arg3: number[]): Promise<unknown>;
|
||||||
|
|
||||||
|
addKernelProfileListener(listener: NodeIKernelProfileListener): number;
|
||||||
|
|
||||||
|
removeKernelProfileListener(listenerId: number): void;
|
||||||
|
|
||||||
|
prepareRegionConfig(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
getLocalStrangerRemark(): Promise<AnyCnameRecord>;
|
||||||
|
|
||||||
|
enumCountryOptions(): Array<string>;
|
||||||
|
|
||||||
|
enumProvinceOptions(Country: string): Array<string>;
|
||||||
|
|
||||||
|
enumCityOptions(Country: string, Province: string): unknown;
|
||||||
|
|
||||||
|
enumAreaOptions(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
//SimpleInfo
|
||||||
|
// this.uid = "";
|
||||||
|
// this.uid = str;
|
||||||
|
// this.uin = j2;
|
||||||
|
// this.isBuddy = z;
|
||||||
|
// this.coreInfo = coreInfo;
|
||||||
|
// this.baseInfo = baseInfo;
|
||||||
|
// this.status = statusInfo;
|
||||||
|
// this.vasInfo = vasInfo;
|
||||||
|
// this.relationFlags = relationFlag;
|
||||||
|
// this.otherFlags = otherFlag;
|
||||||
|
// this.intimate = intimate;
|
||||||
|
|
||||||
|
modifySelfProfile(...args: unknown[]): Promise<unknown>;
|
||||||
|
|
||||||
|
modifyDesktopMiniProfile(param: ModifyProfileParams): Promise<GeneralCallResult>;
|
||||||
|
|
||||||
|
setNickName(NickName: string): Promise<unknown>;
|
||||||
|
|
||||||
|
setLongNick(longNick: string): Promise<unknown>;
|
||||||
|
|
||||||
|
setBirthday(...args: unknown[]): Promise<unknown>;
|
||||||
|
|
||||||
|
setGander(...args: unknown[]): Promise<unknown>;
|
||||||
|
|
||||||
|
setHeader(arg: string): Promise<unknown>;
|
||||||
|
|
||||||
|
setRecommendImgFlag(...args: unknown[]): Promise<unknown>;
|
||||||
|
|
||||||
|
getUserSimpleInfo(force: boolean, uids: string[]): Promise<unknown>;
|
||||||
|
|
||||||
|
getUserDetailInfo(uid: string): Promise<unknown>;
|
||||||
|
|
||||||
|
getUserDetailInfoWithBizInfo(uid: string, Biz: BizKey[]): Promise<GeneralCallResult>;
|
||||||
|
|
||||||
|
getUserDetailInfoByUin(uin: string): Promise<UserDetailInfoByUin>;
|
||||||
|
|
||||||
|
getZplanAvatarInfos(args: string[]): Promise<unknown>;
|
||||||
|
|
||||||
|
getStatus(uid: string): Promise<unknown>;
|
||||||
|
|
||||||
|
startStatusPolling(isForceReset: boolean): Promise<unknown>;
|
||||||
|
|
||||||
|
getSelfStatus(): Promise<unknown>;
|
||||||
|
|
||||||
|
//
|
||||||
|
setdisableEmojiShortCuts(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
getProfileQzonePicInfo(uid: string, type: number, force: boolean): Promise<unknown>;
|
||||||
|
|
||||||
|
//profileService.getCoreInfo("UserRemarkServiceImpl::getStrangerRemarkByUid", arrayList);
|
||||||
|
getCoreInfo(name: string, arg: any[]): unknown;
|
||||||
|
|
||||||
|
//m429253e12.getOtherFlag("FriendListInfoCache_getKernelDataAndPutCache", new ArrayList<>());
|
||||||
|
isNull(): boolean;
|
||||||
|
}
|
||||||
103
src/core/services/NodeIKernelRecentContactService.ts
Normal file
103
src/core/services/NodeIKernelRecentContactService.ts
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
import { ChatType, Peer } from '../entities';
|
||||||
|
import { NodeIKernelRecentContactListener } from '../listeners/NodeIKernelRecentContactListener';
|
||||||
|
import { GeneralCallResult } from './common';
|
||||||
|
|
||||||
|
export interface FSABRecentContactParams {
|
||||||
|
anchorPointContact: {
|
||||||
|
contactId: string;
|
||||||
|
sortField: string;
|
||||||
|
pos: number;
|
||||||
|
},
|
||||||
|
relativeMoveCount: number;
|
||||||
|
listType: number;
|
||||||
|
count: number;
|
||||||
|
fetchOld: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
// {
|
||||||
|
// "anchorPointContact": {
|
||||||
|
// "contactId": "",
|
||||||
|
// "sortField": "",
|
||||||
|
// "pos": 0
|
||||||
|
// },
|
||||||
|
// "relativeMoveCount": 0,
|
||||||
|
// "listType": 1,
|
||||||
|
// "count": 200,
|
||||||
|
// "fetchOld": true
|
||||||
|
// }
|
||||||
|
export interface NodeIKernelRecentContactService {
|
||||||
|
setGuildDisplayStatus(...args: unknown[]): unknown; // 2 arguments
|
||||||
|
|
||||||
|
setContactListTop(...args: unknown[]): unknown; // 2 arguments
|
||||||
|
|
||||||
|
updateRecentContactExtBufForUI(...args: unknown[]): unknown; // 2 arguments
|
||||||
|
|
||||||
|
upsertRecentContactManually(...args: unknown[]): unknown; // 1 arguments
|
||||||
|
|
||||||
|
enterOrExitMsgList(...args: unknown[]): unknown; // 1 arguments
|
||||||
|
|
||||||
|
/*!---!*/
|
||||||
|
getRecentContactListSnapShot(count: number): Promise<GeneralCallResult & {
|
||||||
|
info: {
|
||||||
|
errCode: number,
|
||||||
|
errMsg: string,
|
||||||
|
sortedContactList: Array<number>,
|
||||||
|
changedList: Array<{
|
||||||
|
remark: any;
|
||||||
|
peerName: any;
|
||||||
|
sendMemberName: any;
|
||||||
|
sendNickName: any;
|
||||||
|
peerUid: string; peerUin: string, msgTime: string, chatType: ChatType, msgId: string
|
||||||
|
}>
|
||||||
|
}
|
||||||
|
}>; // 1 arguments
|
||||||
|
|
||||||
|
clearMsgUnreadCount(...args: unknown[]): unknown; // 1 arguments
|
||||||
|
|
||||||
|
getRecentContactListSyncLimit(count: number): unknown;
|
||||||
|
|
||||||
|
jumpToSpecifyRecentContact(...args: unknown[]): unknown; // 1 arguments
|
||||||
|
|
||||||
|
/*!---!*/
|
||||||
|
fetchAndSubscribeABatchOfRecentContact(params: FSABRecentContactParams): unknown; // 1 arguments
|
||||||
|
|
||||||
|
addRecentContact(peer: Peer): unknown;
|
||||||
|
|
||||||
|
deleteRecentContacts(peer: Peer): unknown; // 猜测
|
||||||
|
|
||||||
|
getContacts(peers: Peer[]): Promise<unknown>;
|
||||||
|
|
||||||
|
setThirdPartyBusinessInfos(...args: unknown[]): unknown; // 1 arguments
|
||||||
|
|
||||||
|
updateGameMsgConfigs(...args: unknown[]): unknown; // 1 arguments
|
||||||
|
|
||||||
|
removeKernelRecentContactListener(listenerid: number): unknown; // 1 arguments
|
||||||
|
|
||||||
|
addKernelRecentContactListener(listener: NodeIKernelRecentContactListener): void;
|
||||||
|
|
||||||
|
clearRecentContactsByChatType(...args: unknown[]): unknown; // 1 arguments
|
||||||
|
|
||||||
|
upInsertModule(...args: unknown[]): unknown; // 1 arguments
|
||||||
|
|
||||||
|
jumpToSpecifyRecentContactVer2(...args: unknown[]): unknown; // 1 arguments
|
||||||
|
|
||||||
|
deleteRecentContactsVer2(...args: unknown[]): unknown; // 1 arguments
|
||||||
|
|
||||||
|
getRecentContactList(): Promise<any>;
|
||||||
|
|
||||||
|
getMsgUnreadCount(): unknown;
|
||||||
|
|
||||||
|
clearRecentContacts(): unknown;
|
||||||
|
|
||||||
|
getServiceAssistantRecentContactInfos(): unknown;
|
||||||
|
|
||||||
|
getRecentContactInfos(): unknown;
|
||||||
|
|
||||||
|
getUnreadDetailsInfos(): unknown;
|
||||||
|
|
||||||
|
cleanAllModule(): unknown;
|
||||||
|
|
||||||
|
setAllGameMsgRead(): unknown;
|
||||||
|
|
||||||
|
getRecentContactListSync(): unknown;
|
||||||
|
}
|
||||||
283
src/core/services/NodeIKernelRichMediaService.ts
Normal file
283
src/core/services/NodeIKernelRichMediaService.ts
Normal file
@@ -0,0 +1,283 @@
|
|||||||
|
import { GetFileListParam, MessageElement, Peer } from '../entities';
|
||||||
|
import { GeneralCallResult } from './common';
|
||||||
|
|
||||||
|
export enum UrlFileDownloadType {
|
||||||
|
KUNKNOWN,
|
||||||
|
KURLFILEDOWNLOADPRIVILEGEICON,
|
||||||
|
KURLFILEDOWNLOADPHOTOWALL,
|
||||||
|
KURLFILEDOWNLOADQZONE,
|
||||||
|
KURLFILEDOWNLOADCOMMON,
|
||||||
|
KURLFILEDOWNLOADINSTALLAPP
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum RMBizTypeEnum {
|
||||||
|
KUNKNOWN,
|
||||||
|
KC2CFILE,
|
||||||
|
KGROUPFILE,
|
||||||
|
KC2CPIC,
|
||||||
|
KGROUPPIC,
|
||||||
|
KDISCPIC,
|
||||||
|
KC2CVIDEO,
|
||||||
|
KGROUPVIDEO,
|
||||||
|
KC2CPTT,
|
||||||
|
KGROUPPTT,
|
||||||
|
KFEEDCOMMENTPIC,
|
||||||
|
KGUILDFILE,
|
||||||
|
KGUILDPIC,
|
||||||
|
KGUILDPTT,
|
||||||
|
KGUILDVIDEO
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CommonFileInfo {
|
||||||
|
bizType: number;
|
||||||
|
chatType: number;
|
||||||
|
elemId: string;
|
||||||
|
favId: string;
|
||||||
|
fileModelId: string;
|
||||||
|
fileName: string;
|
||||||
|
fileSize: string;
|
||||||
|
md5: string;
|
||||||
|
md510m: string;
|
||||||
|
msgId: string;
|
||||||
|
msgTime: string;
|
||||||
|
parent: string;
|
||||||
|
peerUid: string;
|
||||||
|
picThumbPath: Array<string>;
|
||||||
|
sha: string;
|
||||||
|
sha3: string;
|
||||||
|
subId: string;
|
||||||
|
uuid: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface NodeIKernelRichMediaService {
|
||||||
|
//getVideoPlayUrl(peer, msgId, elemId, videoCodecFormat, VideoRequestWay.KHAND, cb);
|
||||||
|
// public enum VideoCodecFormatType {
|
||||||
|
// KCODECFORMATH264,
|
||||||
|
// KCODECFORMATH265,
|
||||||
|
// KCODECFORMATH266,
|
||||||
|
// KCODECFORMATAV1
|
||||||
|
// }
|
||||||
|
// public enum VideoRequestWay {
|
||||||
|
// KUNKNOW,
|
||||||
|
// KHAND,
|
||||||
|
// KAUTO
|
||||||
|
// }
|
||||||
|
getVideoPlayUrl(peer: Peer, msgId: string, elemId: string, videoCodecFormat: number, VideoRequestWay: number): Promise<unknown>;
|
||||||
|
|
||||||
|
//exParams (RMReqExParams)
|
||||||
|
// this.downSourceType = i2;
|
||||||
|
// this.triggerType = i3;
|
||||||
|
//peer, msgId, elemId, videoCodecFormat, exParams
|
||||||
|
// 1 0 频道在用
|
||||||
|
// 1 1
|
||||||
|
// 0 2
|
||||||
|
|
||||||
|
// public static final int KCOMMONREDENVELOPEMSGTYPEINMSGBOX = 1007;
|
||||||
|
// public static final int KDOWNSOURCETYPEAIOINNER = 1;
|
||||||
|
// public static final int KDOWNSOURCETYPEBIGSCREEN = 2;
|
||||||
|
// public static final int KDOWNSOURCETYPEHISTORY = 3;
|
||||||
|
// public static final int KDOWNSOURCETYPEUNKNOWN = 0;
|
||||||
|
|
||||||
|
// public static final int KTRIGGERTYPEAUTO = 1;
|
||||||
|
// public static final int KTRIGGERTYPEMANUAL = 0;
|
||||||
|
|
||||||
|
getVideoPlayUrlV2(peer: Peer, msgId: string, elemId: string, videoCodecFormat: number, exParams: {
|
||||||
|
downSourceType: number,
|
||||||
|
triggerType: number
|
||||||
|
}): Promise<GeneralCallResult & {
|
||||||
|
urlResult: {
|
||||||
|
v4IpUrl: [],
|
||||||
|
v6IpUrl: [],
|
||||||
|
domainUrl: Array<{
|
||||||
|
url: string,
|
||||||
|
isHttps: boolean,
|
||||||
|
httpsDomain: string
|
||||||
|
}>,
|
||||||
|
videoCodecFormat: number
|
||||||
|
}
|
||||||
|
}>;
|
||||||
|
|
||||||
|
getRichMediaFileDir(elementType: number, downType: number, isTemp: boolean): unknown;
|
||||||
|
|
||||||
|
// this.senderUid = "";
|
||||||
|
// this.peerUid = "";
|
||||||
|
// this.guildId = "";
|
||||||
|
// this.elem = new MsgElement();
|
||||||
|
// this.downloadType = i2;
|
||||||
|
// this.thumbSize = i3;
|
||||||
|
// this.msgId = j2;
|
||||||
|
// this.msgRandom = j3;
|
||||||
|
// this.msgSeq = j4;
|
||||||
|
// this.msgTime = j5;
|
||||||
|
// this.chatType = i4;
|
||||||
|
// this.senderUid = str;
|
||||||
|
// this.peerUid = str2;
|
||||||
|
// this.guildId = str3;
|
||||||
|
// this.elem = msgElement;
|
||||||
|
// this.useHttps = num;
|
||||||
|
|
||||||
|
getVideoPlayUrlInVisit(arg: {
|
||||||
|
downloadType: number,
|
||||||
|
thumbSize: number,
|
||||||
|
msgId: string,
|
||||||
|
msgRandom: string,
|
||||||
|
msgSeq: string,
|
||||||
|
msgTime: string,
|
||||||
|
chatType: number,
|
||||||
|
senderUid: string,
|
||||||
|
peerUid: string,
|
||||||
|
guildId: string,
|
||||||
|
ele: MessageElement,
|
||||||
|
useHttps: boolean
|
||||||
|
}): Promise<unknown>;
|
||||||
|
|
||||||
|
//arg双端number
|
||||||
|
isFileExpired(arg: number): unknown;
|
||||||
|
|
||||||
|
deleteGroupFolder(GroupCode: string, FolderId: string): Promise<GeneralCallResult & {
|
||||||
|
groupFileCommonResult: { retCode: number, retMsg: string, clientWording: string }
|
||||||
|
}>;
|
||||||
|
|
||||||
|
//参数与getVideoPlayUrlInVisit一样
|
||||||
|
downloadRichMediaInVisit(arg: {
|
||||||
|
downloadType: number,
|
||||||
|
thumbSize: number,
|
||||||
|
msgId: string,
|
||||||
|
msgRandom: string,
|
||||||
|
msgSeq: string,
|
||||||
|
msgTime: string,
|
||||||
|
chatType: number,
|
||||||
|
senderUid: string,
|
||||||
|
peerUid: string,
|
||||||
|
guildId: string,
|
||||||
|
ele: MessageElement,
|
||||||
|
useHttps: boolean
|
||||||
|
}): unknown;
|
||||||
|
|
||||||
|
//arg3为“”
|
||||||
|
downloadFileForModelId(peer: Peer, ModelId: string[], arg3: string): unknown;
|
||||||
|
|
||||||
|
//第三个参数 Array<Type>
|
||||||
|
// this.fileId = "";
|
||||||
|
// this.fileName = "";
|
||||||
|
// this.fileId = str;
|
||||||
|
// this.fileName = str2;
|
||||||
|
// this.fileSize = j2;
|
||||||
|
// this.fileModelId = j3;
|
||||||
|
|
||||||
|
downloadFileForFileUuid(peer: Peer, uuid: string, arg3: {
|
||||||
|
fileId: string,
|
||||||
|
fileName: string,
|
||||||
|
fileSize: string,
|
||||||
|
fileModelId: string
|
||||||
|
}[]): Promise<unknown>;
|
||||||
|
|
||||||
|
downloadFileByUrlList(fileDownloadTyp: UrlFileDownloadType, urlList: Array<string>): unknown;
|
||||||
|
|
||||||
|
downloadFileForFileInfo(fileInfo: CommonFileInfo[], savePath: string): unknown;
|
||||||
|
|
||||||
|
createGroupFolder(GroupCode: string, FolderName: string): Promise<GeneralCallResult & {
|
||||||
|
resultWithGroupItem: { result: any, groupItem: Array<any> }
|
||||||
|
}>;
|
||||||
|
|
||||||
|
downloadFile(commonFile: CommonFileInfo, arg2: unknown, arg3: unknown, savePath: string): unknown;
|
||||||
|
|
||||||
|
createGroupFolder(arg1: unknown, arg2: unknown): unknown;
|
||||||
|
|
||||||
|
downloadGroupFolder(arg1: unknown, arg2: unknown, arg3: unknown): unknown;
|
||||||
|
|
||||||
|
renameGroupFolder(arg1: unknown, arg2: unknown, arg3: unknown): unknown;
|
||||||
|
|
||||||
|
deleteGroupFolder(arg1: unknown, arg2: unknown): unknown;
|
||||||
|
|
||||||
|
deleteTransferInfo(arg1: unknown, arg2: unknown): unknown;
|
||||||
|
|
||||||
|
cancelTransferTask(arg1: unknown, arg2: unknown, arg3: unknown): unknown;
|
||||||
|
|
||||||
|
cancelUrlDownload(arg: unknown): unknown;
|
||||||
|
|
||||||
|
updateOnlineVideoElemStatus(arg: unknown): unknown;
|
||||||
|
|
||||||
|
getGroupSpace(arg: unknown): unknown;
|
||||||
|
|
||||||
|
getGroupFileList(groupCode: string, params: GetFileListParam): Promise<GeneralCallResult & {
|
||||||
|
groupSpaceResult: {
|
||||||
|
retCode: number
|
||||||
|
retMsg: string
|
||||||
|
clientWording: string
|
||||||
|
totalSpace: number
|
||||||
|
usedSpace: number
|
||||||
|
allUpload: boolean
|
||||||
|
}
|
||||||
|
}>;
|
||||||
|
|
||||||
|
getGroupFileInfo(arg1: unknown, arg2: unknown): unknown;
|
||||||
|
|
||||||
|
getGroupTransferList(arg1: unknown, arg2: unknown): unknown;
|
||||||
|
|
||||||
|
renameGroupFile(arg1: unknown, arg2: unknown, arg3: unknown, arg4: unknown, arg5: unknown): unknown;
|
||||||
|
|
||||||
|
moveGroupFile(arg1: unknown, arg2: unknown, arg3: unknown, arg4: unknown, arg5: unknown): unknown;
|
||||||
|
|
||||||
|
transGroupFile(arg1: unknown, arg2: unknown): unknown;
|
||||||
|
|
||||||
|
searchGroupFile(
|
||||||
|
keywords: Array<string>,
|
||||||
|
param: {
|
||||||
|
groupIds: Array<string>,
|
||||||
|
fileType: number,
|
||||||
|
context: string,
|
||||||
|
count: number,
|
||||||
|
sortType: number,
|
||||||
|
groupNames: Array<string>
|
||||||
|
}): Promise<unknown>;
|
||||||
|
|
||||||
|
searchGroupFileByWord(arg1: unknown, arg2: unknown, arg3: unknown, arg4: unknown, arg5: unknown): unknown;
|
||||||
|
|
||||||
|
deleteGroupFile(GroupCode: string, params: Array<number>, Files: Array<string>): Promise<GeneralCallResult & {
|
||||||
|
transGroupFileResult: {
|
||||||
|
result: any
|
||||||
|
successFileIdList: Array<any>
|
||||||
|
failFileIdList: Array<any>
|
||||||
|
}
|
||||||
|
}>;
|
||||||
|
|
||||||
|
translateEnWordToZn(words: string[]): Promise<GeneralCallResult & { words: string[] }>;
|
||||||
|
|
||||||
|
getScreenOCR(path: string): Promise<unknown>;
|
||||||
|
|
||||||
|
batchGetGroupFileCount(Gids: Array<string>): Promise<GeneralCallResult & {
|
||||||
|
groupCodes: Array<string>,
|
||||||
|
groupFileCounts: Array<number>
|
||||||
|
}>;
|
||||||
|
|
||||||
|
queryPicDownloadSize(arg: unknown): unknown;
|
||||||
|
|
||||||
|
searchGroupFile(arg1: unknown, arg2: unknown): unknown;
|
||||||
|
|
||||||
|
searchMoreGroupFile(arg: unknown): unknown;
|
||||||
|
|
||||||
|
cancelSearcheGroupFile(arg1: unknown, arg2: unknown, arg3: unknown): unknown;
|
||||||
|
|
||||||
|
onlyDownloadFile(peer: Peer, arg2: unknown, arg3: Array<{
|
||||||
|
fileId: string,
|
||||||
|
fileName: string,
|
||||||
|
fileSize: string,
|
||||||
|
fileModelId: string
|
||||||
|
}
|
||||||
|
>): unknown;
|
||||||
|
|
||||||
|
onlyUploadFile(arg1: unknown, arg2: unknown): unknown;
|
||||||
|
|
||||||
|
isExtraLargePic(arg1: unknown, arg2: unknown, arg3: unknown): unknown;
|
||||||
|
|
||||||
|
uploadRMFileWithoutMsg(arg: {
|
||||||
|
bizType: RMBizTypeEnum,
|
||||||
|
filePath: string,
|
||||||
|
peerUid: string,
|
||||||
|
transferId: string
|
||||||
|
useNTV2: string
|
||||||
|
}): Promise<unknown>;
|
||||||
|
|
||||||
|
isNull(): boolean;
|
||||||
|
}
|
||||||
35
src/core/services/NodeIKernelRobotService.ts
Normal file
35
src/core/services/NodeIKernelRobotService.ts
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
import { NodeIKernelRobotListener } from '@/core/listeners';
|
||||||
|
|
||||||
|
export interface NodeIKernelRobotService {
|
||||||
|
fetchGroupRobotStoreDiscovery(arg: unknown): unknown;
|
||||||
|
|
||||||
|
sendGroupRobotStoreSearch(arg: unknown): unknown;
|
||||||
|
|
||||||
|
fetchGroupRobotStoreCategoryList(arg: unknown): unknown;
|
||||||
|
|
||||||
|
FetchSubscribeMsgTemplate(arg: unknown): unknown;
|
||||||
|
|
||||||
|
FetchSubcribeMsgTemplateStatus(arg: unknown): unknown;
|
||||||
|
|
||||||
|
SubscribeMsgTemplateSet(arg1: unknown, arg2: unknown): unknown;
|
||||||
|
|
||||||
|
fetchRecentUsedRobots(arg: unknown): unknown;
|
||||||
|
|
||||||
|
fetchShareArkInfo(arg: unknown): unknown;
|
||||||
|
|
||||||
|
addKernelRobotListener(Listener: NodeIKernelRobotListener): number;
|
||||||
|
|
||||||
|
removeKernelRobotListener(ListenerId: number): unknown;
|
||||||
|
|
||||||
|
getAllRobotFriendsFromCache(): Promise<unknown>;
|
||||||
|
|
||||||
|
fetchAllRobots(arg1: unknown, arg2: unknown): unknown;
|
||||||
|
|
||||||
|
removeAllRecommendCache(): unknown;
|
||||||
|
|
||||||
|
setRobotPickTts(arg1: unknown, arg2: unknown): unknown;
|
||||||
|
|
||||||
|
getRobotUinRange(data: any): Promise<{ response: { robotUinRanges: any } }>;
|
||||||
|
|
||||||
|
isNull(): boolean;
|
||||||
|
}
|
||||||
128
src/core/services/NodeIKernelSearchService.ts
Normal file
128
src/core/services/NodeIKernelSearchService.ts
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
import { ChatType } from '../entities';
|
||||||
|
|
||||||
|
export interface NodeIKernelSearchService {
|
||||||
|
addKernelSearchListener(...args: any[]): unknown;// needs 1 arguments
|
||||||
|
|
||||||
|
removeKernelSearchListener(...args: any[]): unknown;// needs 1 arguments
|
||||||
|
|
||||||
|
searchStranger(...args: any[]): unknown;// needs 3 arguments
|
||||||
|
|
||||||
|
searchGroup(...args: any[]): unknown;// needs 1 arguments
|
||||||
|
|
||||||
|
searchLocalInfo(keywords: string, unknown: number/*4*/): unknown;
|
||||||
|
|
||||||
|
cancelSearchLocalInfo(...args: any[]): unknown;// needs 3 arguments
|
||||||
|
|
||||||
|
searchBuddyChatInfo(...args: any[]): unknown;// needs 2 arguments
|
||||||
|
|
||||||
|
searchMoreBuddyChatInfo(...args: any[]): unknown;// needs 1 arguments
|
||||||
|
|
||||||
|
cancelSearchBuddyChatInfo(...args: any[]): unknown;// needs 3 arguments
|
||||||
|
|
||||||
|
searchContact(...args: any[]): unknown;// needs 2 arguments
|
||||||
|
|
||||||
|
searchMoreContact(...args: any[]): unknown;// needs 1 arguments
|
||||||
|
|
||||||
|
cancelSearchContact(...args: any[]): unknown;// needs 3 arguments
|
||||||
|
|
||||||
|
searchGroupChatInfo(...args: any[]): unknown;// needs 3 arguments
|
||||||
|
|
||||||
|
resetSearchGroupChatInfoSortType(...args: any[]): unknown;// needs 3 arguments
|
||||||
|
|
||||||
|
resetSearchGroupChatInfoFilterMembers(...args: any[]): unknown;// needs 3 arguments
|
||||||
|
|
||||||
|
searchMoreGroupChatInfo(...args: any[]): unknown;// needs 1 arguments
|
||||||
|
|
||||||
|
cancelSearchGroupChatInfo(...args: any[]): unknown;// needs 3 arguments
|
||||||
|
|
||||||
|
searchChatsWithKeywords(...args: any[]): unknown;// needs 3 arguments
|
||||||
|
|
||||||
|
searchMoreChatsWithKeywords(...args: any[]): unknown;// needs 1 arguments
|
||||||
|
|
||||||
|
cancelSearchChatsWithKeywords(...args: any[]): unknown;// needs 3 arguments
|
||||||
|
|
||||||
|
searchChatMsgs(...args: any[]): unknown;// needs 2 arguments
|
||||||
|
|
||||||
|
searchMoreChatMsgs(...args: any[]): unknown;// needs 1 arguments
|
||||||
|
|
||||||
|
cancelSearchChatMsgs(...args: any[]): unknown;// needs 3 arguments
|
||||||
|
|
||||||
|
searchMsgWithKeywords(...args: any[]): unknown;// needs 2 arguments
|
||||||
|
|
||||||
|
searchMoreMsgWithKeywords(...args: any[]): unknown;// needs 1 arguments
|
||||||
|
|
||||||
|
cancelSearchMsgWithKeywords(...args: any[]): unknown;// needs 3 arguments
|
||||||
|
|
||||||
|
searchFileWithKeywords(keywords: string[], source: number): Promise<string>;// needs 2 arguments
|
||||||
|
|
||||||
|
searchMoreFileWithKeywords(...args: any[]): unknown;// needs 1 arguments
|
||||||
|
|
||||||
|
cancelSearchFileWithKeywords(...args: any[]): unknown;// needs 3 arguments
|
||||||
|
|
||||||
|
searchAtMeChats(...args: any[]): unknown;// needs 3 arguments
|
||||||
|
|
||||||
|
searchMoreAtMeChats(...args: any[]): unknown;// needs 1 arguments
|
||||||
|
|
||||||
|
cancelSearchAtMeChats(...args: any[]): unknown;// needs 3 arguments
|
||||||
|
|
||||||
|
searchChatAtMeMsgs(...args: any[]): unknown;// needs 1 arguments
|
||||||
|
|
||||||
|
searchMoreChatAtMeMsgs(...args: any[]): unknown;// needs 1 arguments
|
||||||
|
|
||||||
|
cancelSearchChatAtMeMsgs(...args: any[]): unknown;// needs 3 arguments
|
||||||
|
|
||||||
|
addSearchHistory(param: {
|
||||||
|
type: number,//4
|
||||||
|
contactList: [],
|
||||||
|
id: number,//-1
|
||||||
|
groupInfos: [],
|
||||||
|
msgs: [],
|
||||||
|
fileInfos: [
|
||||||
|
{
|
||||||
|
chatType: ChatType,
|
||||||
|
buddyChatInfo: Array<{ category_name: string, peerUid: string, peerUin: string, remark: string }>,
|
||||||
|
discussChatInfo: [],
|
||||||
|
groupChatInfo: Array<
|
||||||
|
{
|
||||||
|
groupCode: string,
|
||||||
|
isConf: boolean,
|
||||||
|
hasModifyConfGroupFace: boolean,
|
||||||
|
hasModifyConfGroupName: boolean,
|
||||||
|
groupName: string,
|
||||||
|
remark: string
|
||||||
|
}>,
|
||||||
|
dataLineChatInfo: [],
|
||||||
|
tmpChatInfo: [],
|
||||||
|
msgId: string,
|
||||||
|
msgSeq: string,
|
||||||
|
msgTime: string,
|
||||||
|
senderUid: string,
|
||||||
|
senderNick: string,
|
||||||
|
senderRemark: string,
|
||||||
|
senderCard: string,
|
||||||
|
elemId: string,
|
||||||
|
elemType: string,//3
|
||||||
|
fileSize: string,
|
||||||
|
filePath: string,
|
||||||
|
fileName: string,
|
||||||
|
hits: Array<
|
||||||
|
{
|
||||||
|
start: 12,
|
||||||
|
end: 14
|
||||||
|
}
|
||||||
|
>
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
}): Promise<{
|
||||||
|
result: number,
|
||||||
|
errMsg: string,
|
||||||
|
id?: number
|
||||||
|
}>;
|
||||||
|
|
||||||
|
removeSearchHistory(...args: any[]): unknown;// needs 1 arguments
|
||||||
|
|
||||||
|
searchCache(...args: any[]): unknown;// needs 3 arguments
|
||||||
|
|
||||||
|
clearSearchCache(...args: any[]): unknown;// needs 1 arguments
|
||||||
|
}
|
||||||
41
src/core/services/NodeIKernelStorageCleanService.ts
Normal file
41
src/core/services/NodeIKernelStorageCleanService.ts
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
import { NodeIKernelStorageCleanListener } from '@/core/listeners';
|
||||||
|
import { GeneralCallResult } from './common';
|
||||||
|
|
||||||
|
export interface NodeIKernelStorageCleanService {
|
||||||
|
|
||||||
|
addKernelStorageCleanListener(Listener: NodeIKernelStorageCleanListener): number;
|
||||||
|
|
||||||
|
removeKernelStorageCleanListener(ListenerId: number): void;
|
||||||
|
|
||||||
|
addCacheScanedPaths(arg: unknown): unknown;
|
||||||
|
|
||||||
|
addFilesScanedPaths(arg: unknown): unknown;
|
||||||
|
|
||||||
|
scanCache(): Promise<GeneralCallResult & {
|
||||||
|
size: string[]
|
||||||
|
}>;
|
||||||
|
|
||||||
|
addReportData(arg: unknown): unknown;
|
||||||
|
|
||||||
|
reportData(): unknown;
|
||||||
|
|
||||||
|
getChatCacheInfo(arg1: unknown, arg2: unknown, arg3: unknown, arg4: unknown): unknown;
|
||||||
|
|
||||||
|
getFileCacheInfo(arg1: unknown, arg2: unknown, arg3: unknown, arg44: unknown, args5: unknown): unknown;
|
||||||
|
|
||||||
|
clearChatCacheInfo(arg1: unknown, arg2: unknown): unknown;
|
||||||
|
|
||||||
|
clearCacheDataByKeys(arg: unknown): unknown;
|
||||||
|
|
||||||
|
setSilentScan(arg: unknown): unknown;
|
||||||
|
|
||||||
|
closeCleanWindow(): unknown;
|
||||||
|
|
||||||
|
clearAllChatCacheInfo(): unknown;
|
||||||
|
|
||||||
|
endScan(arg: unknown): unknown;
|
||||||
|
|
||||||
|
addNewDownloadOrUploadFile(arg: unknown): unknown;
|
||||||
|
|
||||||
|
isNull(): boolean;
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user