mirror of
https://github.com/NapNeko/NapCatQQ.git
synced 2025-07-19 12:03:37 +00:00
Compare commits
1706 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
75f67caa1b | ||
![]() |
d760ce54b7 | ||
![]() |
956976ebd5 | ||
![]() |
f9c2d4ca6c | ||
![]() |
dd5cc3c38c | ||
![]() |
daed4cc13e | ||
![]() |
6ff614dd18 | ||
![]() |
eb70ac4266 | ||
![]() |
a3a431adb7 | ||
![]() |
e12c72ab98 | ||
![]() |
9f8549b831 | ||
![]() |
b2de256f87 | ||
![]() |
7f32a5cf9e | ||
![]() |
56f8314d29 | ||
![]() |
4ceb2a8669 | ||
![]() |
c778d3b699 | ||
![]() |
47eda9cdf2 | ||
![]() |
dcaec4d356 | ||
![]() |
aee4f349c6 | ||
![]() |
daa2c39902 | ||
![]() |
5770fc02a1 | ||
![]() |
47cafd295b | ||
![]() |
3296f2daf8 | ||
![]() |
962616545c | ||
![]() |
11ea92c078 | ||
![]() |
1d64fa4817 | ||
![]() |
c46f2956c2 | ||
![]() |
8f6d4298be | ||
![]() |
3bce81326e | ||
![]() |
2ae9f6d0fe | ||
![]() |
9266828278 | ||
![]() |
a8a2ffc33e | ||
![]() |
27c4543471 | ||
![]() |
50a02cb59e | ||
![]() |
50579bb9e6 | ||
![]() |
50512ca63c | ||
![]() |
8b3577b216 | ||
![]() |
7553aab932 | ||
![]() |
5dacdcfe5e | ||
![]() |
8645a412b7 | ||
![]() |
acad07a588 | ||
![]() |
51bbb480bb | ||
![]() |
f0306cd10a | ||
![]() |
25253ad9e7 | ||
![]() |
51f2fb8e8b | ||
![]() |
9e8d650cbd | ||
![]() |
d222ccfa58 | ||
![]() |
9a05aaa906 | ||
![]() |
00fdce8876 | ||
![]() |
29b51adf7d | ||
![]() |
8e14b39969 | ||
![]() |
d9fb4d6c4d | ||
![]() |
fcf2f4c5f2 | ||
![]() |
4e97501690 | ||
![]() |
b0402391fb | ||
![]() |
f05a862cf9 | ||
![]() |
3ea92d57c2 | ||
![]() |
254b85fbd8 | ||
![]() |
16371c0cc4 | ||
![]() |
2256d67e2b | ||
![]() |
0af0bdede6 | ||
![]() |
379f31b9de | ||
![]() |
771a524734 | ||
![]() |
560e18a610 | ||
![]() |
147bdfab95 | ||
![]() |
36b1b0f663 | ||
![]() |
91511e4c3f | ||
![]() |
6a72056b25 | ||
![]() |
62e0c57a50 | ||
![]() |
9d92270931 | ||
![]() |
f61321d5a6 | ||
![]() |
08ab2f8649 | ||
![]() |
82962c4b42 | ||
![]() |
bd24e8a4ad | ||
![]() |
6224d9a292 | ||
![]() |
bbc58f3671 | ||
![]() |
fcd620283f | ||
![]() |
a78def3d2d | ||
![]() |
43e94a5db0 | ||
![]() |
e77bcc1267 | ||
![]() |
9b458958b8 | ||
![]() |
35419ade29 | ||
![]() |
15bd2ee887 | ||
![]() |
9394bafa8e | ||
![]() |
94150a0c48 | ||
![]() |
8955fdfc23 | ||
![]() |
c13aa6a545 | ||
![]() |
c73b50bd4a | ||
![]() |
0a17a38bf1 | ||
![]() |
0f7bfe1d66 | ||
![]() |
cf3f488663 | ||
![]() |
5f536fdb73 | ||
![]() |
99d0b13cce | ||
![]() |
b04937f012 | ||
![]() |
91c9b059cf | ||
![]() |
35cc643440 | ||
![]() |
b23bb8c46a | ||
![]() |
64fdf62c4b | ||
![]() |
1c8a808571 | ||
![]() |
d8f0295032 | ||
![]() |
d59771ac2f | ||
![]() |
45df093fac | ||
![]() |
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 | ||
![]() |
d66ab7d389 |
@@ -15,7 +15,7 @@ charset = utf-8
|
||||
# 2 space indentation
|
||||
[*.{cjs,mjs,js,jsx,ts,tsx,css,scss,sass,html,json}]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
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.
|
||||
|
@@ -1 +0,0 @@
|
||||
VITE_BUILD_TYPE = Development
|
2
.env.framework
Normal file
2
.env.framework
Normal file
@@ -0,0 +1,2 @@
|
||||
VITE_BUILD_TYPE = Production
|
||||
VITE_BUILD_PLATFORM = Framework
|
@@ -1 +0,0 @@
|
||||
VITE_BUILD_TYPE = Production
|
2
.env.shell
Normal file
2
.env.shell
Normal file
@@ -0,0 +1,2 @@
|
||||
VITE_BUILD_TYPE = Production
|
||||
VITE_BUILD_PLATFORM = Shell
|
121
.eslintrc.cjs
121
.eslintrc.cjs
@@ -1,67 +1,64 @@
|
||||
module.exports = {
|
||||
'env': {
|
||||
'es2021': true,
|
||||
'node': true
|
||||
},
|
||||
'ignorePatterns': ['src/core/', 'src/core.lib/'],
|
||||
'extends': [
|
||||
'eslint:recommended',
|
||||
'plugin:@typescript-eslint/recommended'
|
||||
],
|
||||
'overrides': [
|
||||
{
|
||||
'env': {
|
||||
'env': {
|
||||
'browser': true,
|
||||
'es2021': true,
|
||||
'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
|
||||
}
|
||||
'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'],
|
||||
}
|
||||
},
|
||||
'rules': {
|
||||
'indent': [
|
||||
'error',
|
||||
2
|
||||
],
|
||||
'linebreak-style': [
|
||||
'error',
|
||||
'unix'
|
||||
],
|
||||
'quotes': [
|
||||
'error',
|
||||
'single'
|
||||
],
|
||||
'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'],
|
||||
}
|
||||
};
|
||||
|
45
.github/workflows/build.yml
vendored
45
.github/workflows/build.yml
vendored
@@ -1,67 +1,66 @@
|
||||
name: "Build"
|
||||
name: "Build Action"
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
permissions: write-all
|
||||
|
||||
jobs:
|
||||
build-linux:
|
||||
Build-LiteLoader:
|
||||
if: ${{ startsWith(github.event.head_commit.message, 'build:') }}
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
target_platform: [linux,darwin]
|
||||
target_arch: [x64, arm64]
|
||||
steps:
|
||||
- name: Clone Main Repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: 'NapNeko/NapCatQQ'
|
||||
submodules: true
|
||||
ref: main
|
||||
token: ${{ secrets.NAPCAT_BUILD }}
|
||||
- name: Use Node.js 20.X
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20.x
|
||||
- name: Build NuCat Linux
|
||||
- name: Build NuCat Framework
|
||||
run: |
|
||||
npm i --arch=${{ matrix.target_arch }} --platform=${{ matrix.target_platform }}
|
||||
npm run build:prod
|
||||
npm i
|
||||
npm run build:framework
|
||||
cd dist
|
||||
npm i --omit=dev --arch=${{ matrix.target_arch }} --platform=${{ matrix.target_platform }}
|
||||
npm i --omit=dev
|
||||
rm package-lock.json
|
||||
cd ..
|
||||
- name: Upload Artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: NapCat.${{ matrix.target_platform }}.${{ matrix.target_arch }}
|
||||
name: NapCat.Framework
|
||||
path: dist
|
||||
build-win32:
|
||||
Build-Shell:
|
||||
if: ${{ startsWith(github.event.head_commit.message, 'build:') }}
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
target_platform: [win32]
|
||||
target_arch: [x64]
|
||||
steps:
|
||||
- name: Clone Main Repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: 'NapNeko/NapCatQQ'
|
||||
submodules: true
|
||||
ref: main
|
||||
token: ${{ secrets.NAPCAT_BUILD }}
|
||||
- name: Use Node.js 20.X
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20.x
|
||||
- name: Build NuCat Linux
|
||||
- name: Build NuCat LiteLoader
|
||||
run: |
|
||||
npm i --arch=${{ matrix.target_arch }} --platform=${{ matrix.target_platform }}
|
||||
npm run build:prod
|
||||
npm i
|
||||
npm run build:shell
|
||||
cd dist
|
||||
npm i --omit=dev --arch=${{ matrix.target_arch }} --platform=${{ matrix.target_platform }}
|
||||
npm i --omit=dev
|
||||
rm package-lock.json
|
||||
cd ..
|
||||
- name: Upload Artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: NapCat.${{ matrix.target_platform }}.${{ matrix.target_arch }}
|
||||
name: NapCat.Shell
|
||||
path: dist
|
106
.github/workflows/release.yml
vendored
106
.github/workflows/release.yml
vendored
@@ -1,72 +1,96 @@
|
||||
name: "release"
|
||||
name: "Build Release"
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- "v*"
|
||||
|
||||
permissions: write-all
|
||||
|
||||
jobs:
|
||||
build-linux:
|
||||
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
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
target_platform: [linux,darwin]
|
||||
target_arch: [x64, arm64]
|
||||
steps:
|
||||
- name: Clone Main Repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: 'NapNeko/NapCatQQ'
|
||||
submodules: true
|
||||
ref: main
|
||||
token: ${{ secrets.NAPCAT_BUILD }}
|
||||
- name: Use Node.js 20.X
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20.x
|
||||
- name: Build NuCat Linux
|
||||
node-version: 20.x
|
||||
|
||||
- name: Build NuCat Framework
|
||||
run: |
|
||||
npm i --arch=${{ matrix.target_arch }} --platform=${{ matrix.target_platform }}
|
||||
npm run build:prod
|
||||
npm i
|
||||
npm run build:framework
|
||||
cd dist
|
||||
npm i --omit=dev --arch=${{ matrix.target_arch }} --platform=${{ matrix.target_platform }}
|
||||
npm i --omit=dev
|
||||
cd ..
|
||||
- name: Upload Artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: NapCat.${{ matrix.target_platform }}.${{ matrix.target_arch }}
|
||||
name: NapCat.Framework
|
||||
path: dist
|
||||
build-win32:
|
||||
Build-Shell:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
target_platform: [win32]
|
||||
target_arch: [x64]
|
||||
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 Linux
|
||||
|
||||
- name: Build NuCat Shell
|
||||
run: |
|
||||
npm i --arch=${{ matrix.target_arch }} --platform=${{ matrix.target_platform }}
|
||||
npm run build:prod
|
||||
npm i
|
||||
npm run build:shell
|
||||
cd dist
|
||||
npm i --omit=dev --arch=${{ matrix.target_arch }} --platform=${{ matrix.target_platform }}
|
||||
npm i --omit=dev
|
||||
cd ..
|
||||
|
||||
- name: Upload Artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: NapCat.${{ matrix.target_platform }}.${{ matrix.target_arch }}
|
||||
name: NapCat.Shell
|
||||
path: dist
|
||||
|
||||
release-napcat:
|
||||
needs: [build-win32,build-linux]
|
||||
needs: [Build-LiteLoader,Build-Shell]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Download All Artifact
|
||||
@@ -74,19 +98,29 @@ jobs:
|
||||
|
||||
- name: Compress subdirectories
|
||||
run: |
|
||||
for dir in */; do
|
||||
base=$(basename "$dir")
|
||||
zip -r "${base}.zip" "$dir"
|
||||
done
|
||||
cd ./NapCat.Shell/
|
||||
zip -q -r NapCat.Shell.zip *
|
||||
cd ..
|
||||
cd ./NapCat.Framework/
|
||||
zip -q -r NapCat.Framework.zip *
|
||||
cd ..
|
||||
rm ./NapCat.Shell.zip -rf
|
||||
rm ./NapCat.Framework.zip -rf
|
||||
mv ./NapCat.Shell/NapCat.Shell.zip ./
|
||||
mv ./NapCat.Framework/NapCat.Framework.zip ./
|
||||
- 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 V0.0.0
|
||||
token: ${{ secrets.NAPCAT_BUILD }}
|
||||
name: NapCat V${{ env.VERSION }}
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
body_path: CHANGELOG.md
|
||||
files: |
|
||||
NapCat.win32.x64.zip
|
||||
NapCat.linux.x64.zip
|
||||
NapCat.linux.arm64.zip
|
||||
NapCat.darwin.x64.zip
|
||||
NapCat.darwin.arm64.zip
|
||||
NapCat.Framework.zip
|
||||
NapCat.Shell.zip
|
||||
draft: true
|
||||
|
8
.gitignore
vendored
8
.gitignore
vendored
@@ -1,11 +1,11 @@
|
||||
# Logs
|
||||
|
||||
# Develop
|
||||
node_modules/
|
||||
package-lock.json
|
||||
pnpm-lock.yaml
|
||||
out/
|
||||
dist/
|
||||
src/core.lib/common/
|
||||
/src/core.lib/common/
|
||||
/localdebug/
|
||||
|
||||
# Editor
|
||||
.vscode/*
|
||||
@@ -14,3 +14,5 @@ src/core.lib/common/
|
||||
|
||||
# Build
|
||||
*.db
|
||||
checkVersion.sh
|
||||
bun.lockb
|
||||
|
4
.gitmodules
vendored
4
.gitmodules
vendored
@@ -1,4 +0,0 @@
|
||||
[submodule "src/core"]
|
||||
path = src/core
|
||||
url = https://github.com/NapNeko/core.git
|
||||
branch = master
|
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.
|
170
README.md
170
README.md
@@ -2,164 +2,34 @@
|
||||
<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>
|
||||
|
||||
---
|
||||
|
||||
## 项目介绍
|
||||
NapCatQQ 是现代化的基于 NTQQ 的 Bot 协议端实现。
|
||||
|
||||
NapCatQQ(瞌睡猫QQ,不准叫我NCQQ!),像睡着了一样在后台低占用运行的无头(没有界面)的NTQQ
|
||||
## 项目优势
|
||||
- [x] **多种启动方式**:支持以无头、LiteLoader 插件、仅 QQ GUI 三种方式启动
|
||||
- [x] **低占用**:无头模式占用资源极低,适合在服务器上运行
|
||||
- [x] **WebUI**:自带 WebUI 支持,远程管理更加便捷
|
||||
|
||||
目前测试在 Windows 上表现优秀,最低可达只占用内存 **20M**左右
|
||||
## 如何使用
|
||||
|
||||
由于 Linux 上的 QQ 图形依赖较多,会导致内存占用小高,大约 **100+M**,目前正在研究如何优化
|
||||
可前往 [Release](https://github.com/NapNeko/NapCatQQ/releases/) 页面下载最新版本
|
||||
|
||||
具体占用会因人而异,QQ 群、好友越多占用越高
|
||||
**首次使用**请务必前往[官方文档](https://napneko.github.io/)查看使用教程。
|
||||
|
||||
## 下载
|
||||
|
||||
前往 Release 页面下载最新版本
|
||||
|
||||
## 启动
|
||||
|
||||
NapCat 是基于 官方NTQQ 实现的Bot框架,因此先需要安装官方QQ,**注意同个账号不能同时登录原版 QQ 和 NapCatQQ**
|
||||
|
||||
*如果没有安装 QQ 请往后翻查看安装方法*
|
||||
|
||||
修改 `config/onebot11.json`内容,并重名为 `onebot11_<你的QQ号>.json`,如`onebot11_1234567.json`
|
||||
|
||||
json 配置内容参数解释:
|
||||
|
||||
```json5
|
||||
{
|
||||
// 是否启用http服务,如果启用,可以通过http接口发送消息
|
||||
"enableHttp": false,
|
||||
// http服务端口
|
||||
"httpPort": 3000,
|
||||
// 是否启用正向websocket服务
|
||||
"enableWs": false,
|
||||
// 正向websocket服务端口
|
||||
"wsPort": 3001,
|
||||
// 是否启用反向websocket服务
|
||||
"enableWsReverse": false,
|
||||
// 反向websocket对接的地址, 如["ws://127.0.0.1:8080/onebot/v11/ws"]
|
||||
"wsReverseUrls": [],
|
||||
// 是否启用http上报服务
|
||||
"enableHttpPost": false,
|
||||
// http上报地址, 如["http://127.0.0.1:8080/onebot/v11/http"]
|
||||
"httpPostUrls": [],
|
||||
// http上报密钥,可为空
|
||||
"httpSecret": "",
|
||||
// 消息上报格式,array为消息组,string为cq码字符串
|
||||
"messagePostFormat": "array",
|
||||
// 是否上报自己发送的消息
|
||||
"reportSelfMessage": false,
|
||||
// 是否开启调试模式,开启后上报消息会携带一个raw字段,为原始消息内容
|
||||
"debug": false,
|
||||
// 调用get_file接口时如果获取不到url则使用base64字段返回文件内容
|
||||
"enableLocalFile2Url": true,
|
||||
// ws心跳间隔,单位毫秒
|
||||
"heartInterval": 30000,
|
||||
// access_token,可以为空
|
||||
"token": ""
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### Windows 启动
|
||||
|
||||
运行`powershell ./napcat.ps1`, 或者 `napcat.bat`,如果出现乱码,可以尝试运行`napcat-utf8.ps1` 或 `napcat-utf8.bat`
|
||||
|
||||
*如果出现 powershell 运行不了,以管理员身份打开 powershell,输入 `Set-ExecutionPolicy RemoteSigned`*
|
||||
|
||||
### Linux 启动
|
||||
|
||||
运行`napcat.sh`
|
||||
|
||||
## 使用无需扫码快速登录
|
||||
|
||||
前提是你已经成功登录过QQ,可以加参数` -q <你的QQ>` 进行登录,如`napcat.sh -q 1234567`
|
||||
|
||||
## 安装
|
||||
|
||||
### Linux安装
|
||||
|
||||
#### 安装 Linux QQ(22741),已经安装了的可以跳过
|
||||
|
||||
目前还在研究怎么精简安装,暂时只能安装官方QQ整体依赖
|
||||
|
||||
下载QQ
|
||||
|
||||
[deb x86版本](https://dldir1.qq.com/qqfile/qq/QQNT/Linux/QQ_3.2.7_240403_amd64_01.deb)
|
||||
[deb arm版本](https://dldir1.qq.com/qqfile/qq/QQNT/Linux/QQ_3.2.7_240403_arm64_01.deb)
|
||||
|
||||
[rpm x86版本](https://dldir1.qq.com/qqfile/qq/QQNT/Linux/QQ_3.2.7_240403_x86_64_01.rpm)
|
||||
[rpm arm版本](https://dldir1.qq.com/qqfile/qq/QQNT/Linux/QQ_3.2.7_240403_aarch64_01.rpm)
|
||||
|
||||
```bash
|
||||
sudo apt install ./qq.deb
|
||||
```
|
||||
|
||||
```bash
|
||||
安装QQ的依赖
|
||||
sudo apt install libgbm1 libasound2
|
||||
```
|
||||
|
||||
### Windows 安装
|
||||
|
||||
#### 安装Windows QQ(22741),已经安装了的可以跳过
|
||||
|
||||
[Windows版本QQ下载](https://dldir1.qq.com/qqfile/qq/QQNT/Windows/QQ_9.9.9_240403_x64_01.exe)
|
||||
|
||||
### 编译安装 NapCat
|
||||
|
||||
**如果你是直接下载编译好的版本,可以跳过这一步**
|
||||
|
||||
准备环境 [node18.18](https://nodejs.org/download/release/v18.18.2/)
|
||||
|
||||
```
|
||||
export NODE_ENV=production
|
||||
npm install
|
||||
```
|
||||
|
||||
## 常见问题
|
||||
|
||||
### 二维码无法扫描
|
||||
|
||||
NapCat 会自动保存二维码到目录,可以手动打开图片扫描
|
||||
|
||||
如果没有条件访问本地目录,可以将二维码解析的 url 复制到二维码生成网站上生成二维码,然后手机QQ扫描
|
||||
|
||||
### 语音、视频发送失败
|
||||
|
||||
需要配置 ffmpeg,将 ffmpeg 目录加入环境变量,如果仍未生效,可以修改 napcat 启动脚本加入 FFMPEG_PATH 变量指定到 ffmpeg
|
||||
程序的完整路径
|
||||
|
||||
如 Windows 上修改 napcat.ps1,在第一行加入
|
||||
|
||||
```powershell
|
||||
$env:FFMPEG_PATH="d:\ffmpeg\bin\ffmpeg.exe"
|
||||
```
|
||||
|
||||
### 出现 error code v2:-1 之类的提示
|
||||
|
||||
不用管,这是正常现象,是因为 QQ 本身的问题,不影响使用
|
||||
|
||||
## API 文档
|
||||
|
||||
参考 [LLOneBot](https://llonebot.github.io/zh-CN/develop/api) 的文档
|
||||
|
||||
<!--
|
||||
QQ群:545402644
|
||||
-->
|
||||
|
||||
## 声明
|
||||
|
||||
* 请不要在无关地方宣传NapCatQQ,本项目只是用于学习 node 相关知识,切勿用于违法用途
|
||||
|
||||
* NapCat 不会收集用户隐私信息,但是未来可能会为了更好的利于 NapCat 的优化会收集一些设备信息,如 cpu 架构,系统版本等
|
||||
|
||||
## 相关链接
|
||||
[QQ Group](https://qm.qq.com/q/VfjAq5HIMS)
|
||||
|
||||
[TG群](https://t.me/+nLZEnpne-pQ1OWFl)
|
||||
[Telegram Link](https://t.me/+nLZEnpne-pQ1OWFl)
|
||||
|
||||
## 鸣谢名单
|
||||
[OpenShamrock]()
|
||||
感谢 [LLOneBot](https://github.com/LLOneBot/LLOneBot) 提供初始版本基础
|
||||
|
||||
[Lagrange]()
|
||||
感谢 [Lagrange](https://github.com/LagrangeDev/Lagrange.Core) 对本项目的大力支持
|
||||
|
||||
---
|
||||
|
||||
## 使用许可
|
||||
|
||||
任何使用本仓库代码的地方,都应当严格遵守[本仓库开源许可](./LICENSE)。**此外,禁止任何项目未经授权二次分发或基于 [core](./src/core) 部分代码开发。**
|
||||
|
33
manifest.json
Normal file
33
manifest.json
Normal file
@@ -0,0 +1,33 @@
|
||||
{
|
||||
"manifest_version": 4,
|
||||
"type": "extension",
|
||||
"name": "NapCatQQ",
|
||||
"slug": "NapCat.Framework",
|
||||
"description": "高性能的 OneBot 11 协议实现",
|
||||
"version": "2.0.23",
|
||||
"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"
|
||||
}
|
||||
}
|
128
package.json
128
package.json
@@ -1,63 +1,65 @@
|
||||
{
|
||||
"name": "napcat",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"version": "1.0.2",
|
||||
"scripts": {
|
||||
"watch:dev": "vite --mode development",
|
||||
"watch:prod": "vite --mode production",
|
||||
"build:dev": "vite build --mode development",
|
||||
"build:prod": "vite build --mode production",
|
||||
"build": "npm run build:dev",
|
||||
"build:core": "cd ./src/core && vite build --mode production",
|
||||
"watch": "npm run watch:dev",
|
||||
"debug-win": "powershell dist/napcat.ps1",
|
||||
"lint": "eslint --fix src/**/*.{js,ts}",
|
||||
"release": "npm run build:prod",
|
||||
"depend": "cd dist && npm install --omit=dev"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@log4js-node/log4js-api": "^1.0.2",
|
||||
"@rollup/plugin-commonjs": "^25.0.7",
|
||||
"@rollup/plugin-node-resolve": "^15.2.3",
|
||||
"@rollup/plugin-typescript": "^11.1.6",
|
||||
"@types/express": "^4.17.21",
|
||||
"@types/figlet": "^1.5.8",
|
||||
"@types/fluent-ffmpeg": "^2.1.24",
|
||||
"@types/node": "^20.11.30",
|
||||
"@types/qrcode-terminal": "^0.12.2",
|
||||
"@types/uuid": "^9.0.8",
|
||||
"@types/ws": "^8.5.10",
|
||||
"@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",
|
||||
"protobufjs-cli": "^1.1.2",
|
||||
"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-cp": "^4.0.8",
|
||||
"vite-plugin-dts": "^3.8.2",
|
||||
"vite-tsconfig-paths": "^4.3.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"commander": "^12.0.0",
|
||||
"express": "^5.0.0-beta.2",
|
||||
"file-type": "^19.0.0",
|
||||
"fluent-ffmpeg": "^2.1.2",
|
||||
"image-size": "^1.1.1",
|
||||
"log4js": "^6.9.1",
|
||||
"protobufjs": "^7.2.6",
|
||||
"qrcode-terminal": "^0.12.0",
|
||||
"silk-wasm": "^3.3.4",
|
||||
"sqlite3": "^5.1.7",
|
||||
"uuid": "^9.0.1",
|
||||
"ws": "^8.16.0",
|
||||
"yaml": "^2.4.1"
|
||||
}
|
||||
}
|
||||
{
|
||||
"name": "napcat",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"version": "2.0.23",
|
||||
"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 %*
|
77
script/BootWay05_init.bat
Normal file
77
script/BootWay05_init.bat
Normal file
@@ -0,0 +1,77 @@
|
||||
@echo off
|
||||
REM Check if the script is running as administrator
|
||||
openfiles >nul 2>&1
|
||||
if %errorlevel% neq 0 (
|
||||
REM If not, restart the script in administrator mode
|
||||
echo Requesting administrator privileges...
|
||||
powershell -Command "Start-Process cmd -ArgumentList '/c %~f0 %*' -Verb RunAs"
|
||||
exit /b
|
||||
)
|
||||
|
||||
cd /d %~dp0
|
||||
|
||||
set currentPath=%cd%
|
||||
set currentPath=%currentPath:\=/%
|
||||
|
||||
REM Generate JavaScript code
|
||||
set "jsCode=(async () =^>await import('file:///%currentPath%/napcat.mjs'))();"
|
||||
|
||||
REM Save JavaScript code to a file
|
||||
echo %jsCode% > loadScript.js
|
||||
echo JavaScript code has been generated and saved to loadScript.js
|
||||
|
||||
REM Set NAPCAT_PATH environment variable to the address of loadScript.js in the current directory
|
||||
set NAPCAT_PATH=%cd%\loadScript.js
|
||||
|
||||
REM Get QQ path and cache it
|
||||
: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"
|
||||
)
|
||||
|
||||
set "pathWithoutUninstall=%RetString:Uninstall.exe=%"
|
||||
|
||||
SET QQPath=%pathWithoutUninstall%QQ.exe
|
||||
echo %QQPath%>qq_path_cache.txt
|
||||
echo QQ path %QQPath% has been cached to qq_path_cache.txt
|
||||
|
||||
REM Exit if QQ path is invalid
|
||||
if not exist "%QQpath%" (
|
||||
echo provided QQ path is invalid: %QQpath%
|
||||
pause
|
||||
exit /b
|
||||
)
|
||||
|
||||
REM Collect dbghelp.dll path and HASH information
|
||||
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 Compare the HASH of the old and new dbghelp.dll, and replace the old one if they are different
|
||||
if "%oldDllHash%" neq "%newDllHash%" (
|
||||
tasklist /fi "imagename eq QQ.exe" 2>nul | find /i "QQ.exe" >nul
|
||||
if %errorlevel% equ 0 (
|
||||
REM If the file is in use, prompt the user to close QQ
|
||||
echo dbghelp.dll is in use, please close QQ first.
|
||||
) else (
|
||||
copy /y "%newDllPath%" "%oldDllPath%"
|
||||
if %errorlevel% neq 0 (
|
||||
echo Copy dbghelp.dll failed, please check and try again.
|
||||
pause
|
||||
exit /b
|
||||
) else (
|
||||
echo dbghelp.dll has been updated.
|
||||
echo Please run BootWay05_run.bat to start QQ.
|
||||
echo If you update QQ in the future, please run BootWay05_init.bat again.
|
||||
pause
|
||||
exit /b
|
||||
)
|
||||
)
|
||||
)
|
10
script/BootWay05_run.bat
Normal file
10
script/BootWay05_run.bat
Normal file
@@ -0,0 +1,10 @@
|
||||
@echo off
|
||||
set /p QQPath=<qq_path_cache.txt
|
||||
echo QQ path %QQPath% has been read from qq_path_cache.txt
|
||||
echo If failed to start QQ, please try running this script in administrator mode.
|
||||
|
||||
set NAPCAT_PATH=%cd%\loadScript.js
|
||||
|
||||
REM Launch QQ.exe with params provided
|
||||
|
||||
"%QQPath%" --enable-logging %*
|
13
script/BootWay05_run.utf8.bat
Normal file
13
script/BootWay05_run.utf8.bat
Normal file
@@ -0,0 +1,13 @@
|
||||
@echo off
|
||||
|
||||
chcp 65001
|
||||
|
||||
set /p QQPath=<qq_path_cache.txt
|
||||
echo QQ path %QQPath% has been read from qq_path_cache.txt
|
||||
echo If failed to start QQ, please try running this script in administrator mode.
|
||||
|
||||
set NAPCAT_PATH=%cd%\loadScript.js
|
||||
|
||||
REM Launch QQ.exe with params provided
|
||||
|
||||
"%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.
@@ -1,15 +0,0 @@
|
||||
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 "Error getting UninstallString: $_"
|
||||
}
|
||||
}
|
||||
$params = $args -join " "
|
||||
$QQpath = Get-QQpath
|
||||
$Bootfile = Join-Path (Get-Location) "\dist\inject.cjs"
|
||||
$env:ELECTRON_RUN_AS_NODE = 1
|
||||
Start-Process powershell -ArgumentList "-noexit", "-noprofile", "-command &{& '$QQpath' --expose-gc $Bootfile $params}"
|
@@ -1,17 +0,0 @@
|
||||
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 {
|
||||
return "D:\QQ.exe"
|
||||
}
|
||||
}
|
||||
$params = $args -join " "
|
||||
$QQpath = Get-QQpath
|
||||
$Bootfile = Join-Path $PSScriptRoot "napcat.cjs"
|
||||
$env:ELECTRON_RUN_AS_NODE = 1
|
||||
$argumentList = '-noexit', '-noprofile', "-command `"$QQpath`" `"$Bootfile`" $params"
|
||||
Start-Process powershell -ArgumentList $argumentList -RedirectStandardOutput "log.txt" -RedirectStandardError "error.txt"
|
||||
powershell Get-Content -Wait -Encoding UTF8 log.txt
|
@@ -1,18 +0,0 @@
|
||||
@echo off
|
||||
setlocal enabledelayedexpansion
|
||||
chcp 65001
|
||||
:loop_read
|
||||
for /f "tokens=2*" %%a in ('reg query "HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\QQ" /v "UninstallString"') do (
|
||||
set "RetString=%%b"
|
||||
goto :napcat_boot
|
||||
)
|
||||
|
||||
:napcat_boot
|
||||
for %%a in ("!RetString!") do (
|
||||
set "pathWithoutUninstall=%%~dpa"
|
||||
)
|
||||
|
||||
set "QQPath=!pathWithoutUninstall!QQ.exe"
|
||||
set ELECTRON_RUN_AS_NODE=1
|
||||
echo !QQPath!
|
||||
"!QQPath!" ./napcat.cjs %*
|
@@ -1,15 +0,0 @@
|
||||
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 {
|
||||
return "D:\QQ.exe"
|
||||
}
|
||||
}
|
||||
$params = $args -join " "
|
||||
$QQpath = Get-QQpath
|
||||
$Bootfile = Join-Path $PSScriptRoot "napcat.cjs"
|
||||
$env:ELECTRON_RUN_AS_NODE = 1
|
||||
Start-Process powershell -ArgumentList "-noexit", "-noprofile", "-command &{& chcp 65001;& '$QQpath' $Bootfile $params}"
|
@@ -1,17 +0,0 @@
|
||||
@echo off
|
||||
setlocal enabledelayedexpansion
|
||||
:loop_read
|
||||
for /f "tokens=2*" %%a in ('reg query "HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\QQ" /v "UninstallString"') do (
|
||||
set "RetString=%%b"
|
||||
goto :napcat_boot
|
||||
)
|
||||
|
||||
:napcat_boot
|
||||
for %%a in ("!RetString!") do (
|
||||
set "pathWithoutUninstall=%%~dpa"
|
||||
)
|
||||
|
||||
set "QQPath=!pathWithoutUninstall!QQ.exe"
|
||||
set ELECTRON_RUN_AS_NODE=1
|
||||
echo !QQPath!
|
||||
"!QQPath!" ./napcat.cjs %*
|
@@ -1,15 +0,0 @@
|
||||
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 {
|
||||
return "D:\QQ.exe"
|
||||
}
|
||||
}
|
||||
$params = $args -join " "
|
||||
$QQpath = Get-QQpath
|
||||
$Bootfile = Join-Path $PSScriptRoot "napcat.cjs"
|
||||
$env:ELECTRON_RUN_AS_NODE = 1
|
||||
Start-Process powershell -ArgumentList "-noexit", "-noprofile", "-command &{& '$QQpath' $Bootfile $params}"
|
@@ -1,4 +0,0 @@
|
||||
#!/bin/bash
|
||||
SCRIPT_DIR=$(realpath $(dirname "${BASH_SOURCE[0]}"))
|
||||
export ELECTRON_RUN_AS_NODE=1
|
||||
/opt/QQ/qq ${SCRIPT_DIR}/napcat.cjs $@
|
@@ -1,85 +0,0 @@
|
||||
import {
|
||||
type Friend,
|
||||
type FriendRequest,
|
||||
type Group,
|
||||
type GroupMember, GroupNotify,
|
||||
type SelfInfo
|
||||
} from '@/core/qqnt/entities';
|
||||
import { isNumeric } from './utils/helper';
|
||||
import { log } from '@/common/utils/log';
|
||||
|
||||
export const selfInfo: SelfInfo = {
|
||||
uid: '',
|
||||
uin: '',
|
||||
nick: '',
|
||||
online: true
|
||||
};
|
||||
|
||||
// groupCode -> Group
|
||||
export const groups: Map<string, Group> = new Map<string, Group>();
|
||||
|
||||
// 群号 -> 群成员map(uid=>GroupMember)
|
||||
export const groupMembers: Map<string, Map<string, GroupMember>> = new Map<string, Map<string, GroupMember>>();
|
||||
|
||||
// uid -> Friend
|
||||
export const friends: Map<string, Friend> = new Map<string, Friend>();
|
||||
|
||||
export const friendRequests: Record<string, FriendRequest> = {}; // flag->FriendRequest
|
||||
export const groupNotifies: Record<string, GroupNotify> = {}; // flag->GroupNotify
|
||||
|
||||
export const napCatError = {
|
||||
ffmpegError: '',
|
||||
httpServerError: '',
|
||||
wsServerError: '',
|
||||
otherError: 'NapCat未能正常启动,请检查日志查看错误'
|
||||
};
|
||||
|
||||
export async function getFriend(uinOrUid: string): Promise<Friend | undefined> {
|
||||
uinOrUid = uinOrUid.toString();
|
||||
if (isNumeric(uinOrUid)) {
|
||||
const friendList = Array.from(friends.values());
|
||||
return friendList.find(friend => friend.uin === uinOrUid);
|
||||
} else {
|
||||
return friends.get(uinOrUid);
|
||||
}
|
||||
}
|
||||
|
||||
export async function getGroup(qq: string): Promise<Group | undefined> {
|
||||
const group = groups.get(qq.toString());
|
||||
return group;
|
||||
}
|
||||
|
||||
export async function getGroupMember(groupQQ: string | number, memberUinOrUid: string | number) {
|
||||
groupQQ = groupQQ.toString();
|
||||
memberUinOrUid = memberUinOrUid.toString();
|
||||
const members = groupMembers.get(groupQQ);
|
||||
if (!members) {
|
||||
return null;
|
||||
}
|
||||
// log('getGroupMember', members);
|
||||
if (isNumeric(memberUinOrUid)) {
|
||||
return Array.from(members.values()).find(member => member.uin === memberUinOrUid);
|
||||
} else {
|
||||
return members.get(memberUinOrUid);
|
||||
}
|
||||
}
|
||||
|
||||
export async function refreshGroupMembers(groupQQ: string) {
|
||||
// const group = groups.find(group => group.groupCode === groupQQ)
|
||||
// if (group) {
|
||||
// group.members = await NTQQGroupApi.getGroupMembers(groupQQ)
|
||||
// }
|
||||
}
|
||||
|
||||
export const uid2UinMap: Record<string, string> = {}; // 一串加密的字符串(uid) -> qq号
|
||||
|
||||
export function getUidByUin(uin: string) {
|
||||
for (const uid in uid2UinMap) {
|
||||
if (uid2UinMap[uid] === uin) {
|
||||
return uid;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const tempGroupCodeMap: Record<string, string> = {}; // peerUid => 群号
|
||||
|
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.23';
|
||||
|
||||
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 });
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,113 +0,0 @@
|
||||
import express, { Express, Request, Response } from 'express';
|
||||
import http from 'http';
|
||||
import { log } from '../utils/log';
|
||||
import { ob11Config } from '@/onebot11/config';
|
||||
|
||||
type RegisterHandler = (res: Response, payload: any) => Promise<any>
|
||||
|
||||
export abstract class HttpServerBase {
|
||||
name: string = 'LLOneBot';
|
||||
private readonly expressAPP: Express;
|
||||
private server: http.Server | null = null;
|
||||
|
||||
constructor() {
|
||||
this.expressAPP = express();
|
||||
this.expressAPP.use(express.urlencoded({ extended: true, limit: '5000mb' }));
|
||||
this.expressAPP.use((req, res, next) => {
|
||||
// 兼容处理没有带content-type的请求
|
||||
// log("req.headers['content-type']", req.headers['content-type'])
|
||||
req.headers['content-type'] = 'application/json';
|
||||
const originalJson = express.json({ limit: '5000mb' });
|
||||
// 调用原始的express.json()处理器
|
||||
originalJson(req, res, (err) => {
|
||||
if (err) {
|
||||
log('Error parsing JSON:', err);
|
||||
return res.status(400).send('Invalid JSON');
|
||||
}
|
||||
next();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
authorize(req: Request, res: Response, next: () => void) {
|
||||
const serverToken = ob11Config.token;
|
||||
let clientToken = '';
|
||||
const authHeader = req.get('authorization');
|
||||
if (authHeader) {
|
||||
clientToken = authHeader.split('Bearer ').pop() || '';
|
||||
log('receive http header token', clientToken);
|
||||
} else if (req.query.access_token) {
|
||||
if (Array.isArray(req.query.access_token)) {
|
||||
clientToken = req.query.access_token[0].toString();
|
||||
} else {
|
||||
clientToken = req.query.access_token.toString();
|
||||
}
|
||||
log('receive http url token', clientToken);
|
||||
}
|
||||
|
||||
if (serverToken && clientToken != serverToken) {
|
||||
return res.status(403).send(JSON.stringify({ message: 'token verify failed!' }));
|
||||
}
|
||||
next();
|
||||
}
|
||||
|
||||
start(port: number) {
|
||||
try {
|
||||
this.expressAPP.get('/', (req: Request, res: Response) => {
|
||||
res.send(`${this.name}已启动`);
|
||||
});
|
||||
this.listen(port);
|
||||
} catch (e: any) {
|
||||
log('HTTP服务启动失败', e.toString());
|
||||
// llonebotError.httpServerError = "HTTP服务启动失败, " + e.toString()
|
||||
}
|
||||
}
|
||||
|
||||
stop() {
|
||||
// llonebotError.httpServerError = ""
|
||||
if (this.server) {
|
||||
this.server.close();
|
||||
this.server = null;
|
||||
}
|
||||
}
|
||||
|
||||
restart(port: number) {
|
||||
this.stop();
|
||||
this.start(port);
|
||||
}
|
||||
|
||||
abstract handleFailed(res: Response, payload: any, err: any): void
|
||||
|
||||
registerRouter(method: 'post' | 'get' | string, url: string, handler: RegisterHandler) {
|
||||
if (!url.startsWith('/')) {
|
||||
url = '/' + url;
|
||||
}
|
||||
|
||||
// @ts-expect-error wait fix
|
||||
if (!this.expressAPP[method]) {
|
||||
const err = `${this.name} register router failed,${method} not exist`;
|
||||
log(err);
|
||||
throw err;
|
||||
}
|
||||
// @ts-expect-error wait fix
|
||||
this.expressAPP[method](url, this.authorize, async (req: Request, res: Response) => {
|
||||
let payload = req.body;
|
||||
if (method == 'get') {
|
||||
payload = req.query;
|
||||
}
|
||||
log('收到http请求', url, payload);
|
||||
try {
|
||||
res.send(await handler(res, payload));
|
||||
} catch (e: any) {
|
||||
this.handleFailed(res, payload, e.stack.toString());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected listen(port: number) {
|
||||
this.server = this.expressAPP.listen(port, '0.0.0.0', () => {
|
||||
const info = `${this.name} started 0.0.0.0:${port}`;
|
||||
log(info);
|
||||
});
|
||||
}
|
||||
}
|
@@ -1,99 +0,0 @@
|
||||
import { WebSocket, WebSocketServer } from 'ws';
|
||||
import urlParse from 'url';
|
||||
import { IncomingMessage } from 'node:http';
|
||||
import { log } from '@/common/utils/log';
|
||||
|
||||
class WebsocketClientBase {
|
||||
private wsClient: WebSocket | undefined;
|
||||
|
||||
constructor() {
|
||||
}
|
||||
|
||||
send(msg: string) {
|
||||
if (this.wsClient && this.wsClient.readyState == WebSocket.OPEN) {
|
||||
this.wsClient.send(msg);
|
||||
}
|
||||
}
|
||||
|
||||
onMessage(msg: string) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
export class WebsocketServerBase {
|
||||
private ws: WebSocketServer | null = null;
|
||||
public token: string = '';
|
||||
|
||||
constructor() {
|
||||
}
|
||||
|
||||
start(port: number) {
|
||||
try {
|
||||
this.ws = new WebSocketServer({ port });
|
||||
} catch (e: any) {
|
||||
throw Error('ws服务启动失败, ' + e.toString());
|
||||
}
|
||||
this.ws.on('connection', (wsClient, req) => {
|
||||
const url: string = req.url!.split('?').shift() || '/';
|
||||
this.authorize(wsClient, req);
|
||||
this.onConnect(wsClient, url, req);
|
||||
wsClient.on('message', async (msg) => {
|
||||
this.onMessage(wsClient, url, msg.toString());
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
stop() {
|
||||
this.ws && this.ws.close((err) => {
|
||||
log('ws server close failed!', err);
|
||||
});
|
||||
this.ws = null;
|
||||
}
|
||||
|
||||
restart(port: number) {
|
||||
this.stop();
|
||||
this.start(port);
|
||||
}
|
||||
|
||||
authorize(wsClient: WebSocket, req: IncomingMessage) {
|
||||
const url = req.url!.split('?').shift();
|
||||
log('ws connect', url);
|
||||
let clientToken: string = '';
|
||||
const authHeader = req.headers['authorization'];
|
||||
if (authHeader) {
|
||||
clientToken = authHeader.split('Bearer ').pop() || '';
|
||||
log('receive ws header token', clientToken);
|
||||
} else {
|
||||
const parsedUrl = urlParse.parse(req.url || '/', true);
|
||||
const urlToken = parsedUrl.query.access_token;
|
||||
if (urlToken) {
|
||||
if (Array.isArray(urlToken)) {
|
||||
clientToken = urlToken[0];
|
||||
} else {
|
||||
clientToken = urlToken;
|
||||
}
|
||||
log('receive ws url token', clientToken);
|
||||
}
|
||||
}
|
||||
if (this.token && clientToken != this.token) {
|
||||
this.authorizeFailed(wsClient);
|
||||
return wsClient.close();
|
||||
}
|
||||
}
|
||||
|
||||
authorizeFailed(wsClient: WebSocket) {
|
||||
|
||||
}
|
||||
|
||||
onConnect(wsClient: WebSocket, url: string, req: IncomingMessage) {
|
||||
|
||||
}
|
||||
|
||||
onMessage(wsClient: WebSocket, url: string, msg: string) {
|
||||
|
||||
}
|
||||
|
||||
sendHeart() {
|
||||
|
||||
}
|
||||
}
|
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();
|
@@ -1,60 +1,81 @@
|
||||
import path from 'node:path';
|
||||
import fs from 'node:fs';
|
||||
import os from 'node:os';
|
||||
import { systemPlatform } from '@/common/utils/system';
|
||||
import { getDefaultQQVersionConfigInfo, getQQVersionConfigPath } from './helper';
|
||||
import AppidTable from '@/core/external/appid.json';
|
||||
import { LogWrapper } from './log';
|
||||
|
||||
export const exePath = process.execPath;
|
||||
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 };
|
||||
|
||||
export const pkgInfoPath = path.join(path.dirname(exePath), 'resources', 'app', 'package.json');
|
||||
let configVersionInfoPath;
|
||||
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);
|
||||
|
||||
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');
|
||||
//基础信息获取 无快更则启用默认模板填充
|
||||
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() };
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof configVersionInfoPath !== 'string') {
|
||||
throw new Error('Something went wrong when load QQ info path');
|
||||
}
|
||||
|
||||
export { configVersionInfoPath };
|
||||
|
||||
type QQPkgInfo = {
|
||||
version: string;
|
||||
buildVersion: string;
|
||||
platform: string;
|
||||
eleArch: string;
|
||||
}
|
||||
type QQVersionConfigInfo = {
|
||||
baseVersion: string;
|
||||
curVersion: string;
|
||||
prevVersion: string;
|
||||
onErrorVersions: Array<any>;
|
||||
buildId: string;
|
||||
}
|
||||
|
||||
let _qqVersionConfigInfo: QQVersionConfigInfo = {
|
||||
'baseVersion': '9.9.9-22578',
|
||||
'curVersion': '9.9.9-22578',
|
||||
'prevVersion': '',
|
||||
'onErrorVersions': [],
|
||||
'buildId': '22578'
|
||||
};
|
||||
|
||||
if (fs.existsSync(configVersionInfoPath)) {
|
||||
_qqVersionConfigInfo = JSON.parse(fs.readFileSync(configVersionInfoPath).toString());
|
||||
}
|
||||
|
||||
export const qqVersionConfigInfo: QQVersionConfigInfo = _qqVersionConfigInfo;
|
||||
|
||||
export const qqPkgInfo: QQPkgInfo = require(pkgInfoPath);
|
||||
|
||||
let _appid: string = '537213335'; // 默认为 Windows 平台的 appid
|
||||
if (systemPlatform === 'linux') {
|
||||
_appid = '537213710';
|
||||
}
|
||||
// todo: mac 平台的 appid
|
||||
export const appid = _appid;
|
||||
export let QQBasicInfo: QQBasicInfoWrapper | undefined;
|
||||
|
@@ -1,135 +1,90 @@
|
||||
import fs from 'fs';
|
||||
import { encode, getDuration, getWavFileInfo, isWav } from 'silk-wasm';
|
||||
import { encode, getDuration, getWavFileInfo, isSilk, isWav } from 'silk-wasm';
|
||||
import fsPromise from 'fs/promises';
|
||||
import { log } from './log';
|
||||
import path from 'node:path';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import { randomUUID } from 'crypto';
|
||||
import { spawn } from 'node:child_process';
|
||||
import { getTempDir } from '@/common/utils/file';
|
||||
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;
|
||||
}
|
||||
|
||||
let TEMP_DIR = './';
|
||||
setTimeout(() => {
|
||||
TEMP_DIR = getTempDir();
|
||||
}, 100);
|
||||
export async function encodeSilk(filePath: string) {
|
||||
function getFileHeader(filePath: string) {
|
||||
// 定义要读取的字节数
|
||||
const bytesToRead = 7;
|
||||
try {
|
||||
const buffer = fs.readFileSync(filePath, {
|
||||
encoding: null,
|
||||
flag: 'r',
|
||||
});
|
||||
|
||||
const fileHeader = buffer.toString('hex', 0, bytesToRead);
|
||||
return fileHeader;
|
||||
} catch (err) {
|
||||
console.error('读取文件错误:', err);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
async function isWavFile(filePath: string) {
|
||||
return isWav(fs.readFileSync(filePath));
|
||||
}
|
||||
|
||||
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);
|
||||
log('通过文件大小估算语音的时长:', duration);
|
||||
return duration;
|
||||
}
|
||||
|
||||
// function verifyDuration(oriDuration: number, guessDuration: number) {
|
||||
// // 单位都是秒
|
||||
// if (oriDuration - guessDuration > 10) {
|
||||
// return guessDuration
|
||||
// }
|
||||
// oriDuration = Math.max(1, oriDuration)
|
||||
// return oriDuration
|
||||
// }
|
||||
// async function getAudioSampleRate(filePath: string) {
|
||||
// try {
|
||||
// const mm = await import('music-metadata');
|
||||
// const metadata = await mm.parseFile(filePath);
|
||||
// log(`${filePath}采样率`, metadata.format.sampleRate);
|
||||
// return metadata.format.sampleRate;
|
||||
// } catch (error) {
|
||||
// log(`${filePath}采样率获取失败`, error.stack);
|
||||
// // console.error(error);
|
||||
// }
|
||||
// }
|
||||
|
||||
try {
|
||||
const pttPath = path.join(TEMP_DIR, uuidv4());
|
||||
if (getFileHeader(filePath) !== '02232153494c4b') {
|
||||
log(`语音文件${filePath}需要转换成silk`);
|
||||
const _isWav = await isWavFile(filePath);
|
||||
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 => {
|
||||
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);
|
||||
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);
|
||||
}
|
||||
log(`FFmpeg exit: code=${code ?? 'unknown'} sig=${signal ?? 'unknown'}`);
|
||||
reject(Error('FFmpeg处理转换失败'));
|
||||
});
|
||||
});
|
||||
};
|
||||
let input: Buffer;
|
||||
if (!_isWav) {
|
||||
input = await convert();
|
||||
} else {
|
||||
input = fs.readFileSync(filePath);
|
||||
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);
|
||||
log(`语音文件${filePath}转换成功!`, pttPath, '时长:', silk.duration);
|
||||
return {
|
||||
converted: true,
|
||||
path: pttPath,
|
||||
duration: silk.duration / 1000
|
||||
};
|
||||
} else {
|
||||
const silk = fs.readFileSync(filePath);
|
||||
let duration = 0;
|
||||
try {
|
||||
duration = getDuration(silk) / 1000;
|
||||
} catch (e: any) {
|
||||
log('获取语音文件时长失败, 使用文件大小推测时长', filePath, e.stack);
|
||||
duration = await guessDuration(filePath);
|
||||
}
|
||||
|
||||
return {
|
||||
converted: false,
|
||||
path: filePath,
|
||||
duration,
|
||||
};
|
||||
return {
|
||||
converted: false,
|
||||
path: filePath,
|
||||
duration,
|
||||
};
|
||||
}
|
||||
} catch (error: any) {
|
||||
logger.logError('convert silk failed', error.stack);
|
||||
return {};
|
||||
}
|
||||
} catch (error: any) {
|
||||
log('convert silk failed', error.stack);
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
@@ -1,323 +0,0 @@
|
||||
import { ElementType, FileElement, PicElement, PttElement, RawMessage, VideoElement } from '@/core/qqnt/entities';
|
||||
|
||||
import sqlite3 from 'sqlite3';
|
||||
import { log } from '@/common/utils/log';
|
||||
|
||||
type DBMsg = {
|
||||
id: number,
|
||||
longId: string,
|
||||
seq: number,
|
||||
peerUid: string,
|
||||
msg: string
|
||||
}
|
||||
|
||||
type DBFile = {
|
||||
name: string; // 文件名
|
||||
path: string;
|
||||
url: string;
|
||||
size: number;
|
||||
uuid: string;
|
||||
msgId: string;
|
||||
elementId: string;
|
||||
element: PicElement | VideoElement | FileElement | PttElement;
|
||||
elementType: ElementType.PIC | ElementType.VIDEO | ElementType.FILE | ElementType.PTT;
|
||||
}
|
||||
|
||||
|
||||
class DBUtilBase {
|
||||
protected db: sqlite3.Database | undefined;
|
||||
|
||||
createConnection(dbPath: string) {
|
||||
if (this.db) {
|
||||
return;
|
||||
}
|
||||
this.db = new sqlite3.Database(dbPath, sqlite3.OPEN_READWRITE | sqlite3.OPEN_CREATE, (err) => {
|
||||
if (err) {
|
||||
log('Could not connect to database', err);
|
||||
return;
|
||||
}
|
||||
this.createTable();
|
||||
});
|
||||
}
|
||||
|
||||
protected createTable() {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
|
||||
close() {
|
||||
this.db?.close();
|
||||
}
|
||||
}
|
||||
|
||||
class DBUtil extends DBUtilBase {
|
||||
private msgCache: Map<string, RawMessage> = new Map<string, RawMessage>();
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
const interval = 1000 * 60 * 10; // 10分钟清理一次缓存
|
||||
setInterval(() => {
|
||||
log('清理消息缓存');
|
||||
this.msgCache.forEach((msg, key) => {
|
||||
if ((Date.now() - parseInt(msg.msgTime) * 1000) > interval) {
|
||||
this.msgCache.delete(key);
|
||||
}
|
||||
});
|
||||
}, interval);
|
||||
}
|
||||
|
||||
|
||||
protected createTable() {
|
||||
// 消息记录
|
||||
const createTableSQL = `
|
||||
CREATE TABLE IF NOT EXISTS msgs (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
long_id TEXT NOT NULL UNIQUE,
|
||||
seq INTEGER NOT NULL,
|
||||
peer_uid TEXT NOT NULL,
|
||||
msg TEXT NOT NULL
|
||||
)`;
|
||||
this.db!.run(createTableSQL, function (err) {
|
||||
if (err) {
|
||||
log('Could not create table', err);
|
||||
}
|
||||
});
|
||||
|
||||
// 文件缓存
|
||||
const createFileTableSQL = `
|
||||
CREATE TABLE IF NOT EXISTS files (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
name TEXT NOT NULL,
|
||||
path TEXT NOT NULL,
|
||||
url TEXT,
|
||||
size INTEGER NOT NULL,
|
||||
uuid TEXT,
|
||||
elementType INTEGER,
|
||||
element TEXT NOT NULL,
|
||||
elementId TEXT NOT NULL,
|
||||
msgId TEXT NOT NULL
|
||||
)`;
|
||||
this.db!.run(createFileTableSQL, function (err) {
|
||||
if (err) {
|
||||
log('Could not create table files', err);
|
||||
}
|
||||
});
|
||||
|
||||
// 接收到的临时会话消息uid
|
||||
const createTempUinTableSQL = `
|
||||
CREATE TABLE IF NOT EXISTS temp_uins (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
uid TEXT,
|
||||
uin TEXT
|
||||
)`;
|
||||
this.db!.run(createTempUinTableSQL, function (err) {
|
||||
if (err) {
|
||||
log('Could not create table temp_uins', err);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private async getMsg(query: string, params: any[]) {
|
||||
const stmt = this.db!.prepare(query);
|
||||
return new Promise<RawMessage | null>((resolve, reject) => {
|
||||
stmt.get(...params, (err: any, row: DBMsg) => {
|
||||
// log("getMsg", row, err);
|
||||
if (err) {
|
||||
log('Could not get msg by short id', err);
|
||||
resolve(null);
|
||||
}
|
||||
try {
|
||||
const msg = JSON.parse(row.msg);
|
||||
msg.id = row.id;
|
||||
return resolve(msg);
|
||||
} catch (e) {
|
||||
return resolve(null);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async getMsgByShortId(shortId: number): Promise<RawMessage | null> {
|
||||
const getStmt = 'SELECT * FROM msgs WHERE id = ?';
|
||||
return this.getMsg(getStmt, [shortId]);
|
||||
}
|
||||
|
||||
async getMsgByLongId(longId: string): Promise<RawMessage | null> {
|
||||
if (this.msgCache.has(longId)) {
|
||||
return this.msgCache.get(longId)!;
|
||||
}
|
||||
return this.getMsg('SELECT * FROM msgs WHERE long_id = ?', [longId]);
|
||||
}
|
||||
|
||||
async getMsgBySeq(peerUid: string, seq: string): Promise<RawMessage | null> {
|
||||
const stmt = 'SELECT * FROM msgs WHERE peer_uid = ? AND seq = ?';
|
||||
return this.getMsg(stmt, [peerUid, seq]);
|
||||
}
|
||||
|
||||
async addMsg(msg: RawMessage, update = true): Promise<number> {
|
||||
log('正在记录消息到数据库', msg.msgId);
|
||||
const existMsg = await this.getMsgByLongId(msg.msgId);
|
||||
if (existMsg) {
|
||||
// log('消息已存在,更新数据库', msg.msgId);
|
||||
if (update) this.updateMsg(msg).then();
|
||||
return existMsg.id!;
|
||||
}
|
||||
const stmt = this.db!.prepare('INSERT INTO msgs (long_id, seq, peer_uid, msg) VALUES (?, ?, ?, ?)');
|
||||
|
||||
// const runAsync = promisify(stmt.run.bind(stmt));
|
||||
return new Promise((resolve, reject) => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
||||
const dbInstance = this;
|
||||
stmt.run(msg.msgId, msg.msgSeq, msg.peerUid, JSON.stringify(msg), function (err: any) {
|
||||
if (err) {
|
||||
if (err.errno === 19) {
|
||||
// log('消息已存在,更新数据库', msg.msgId);
|
||||
dbInstance.getMsgByLongId(msg.msgId).then((msg: RawMessage | null) => {
|
||||
if (msg) {
|
||||
dbInstance.msgCache.set(msg.msgId, msg);
|
||||
// log('获取消息短id成功', msg.id);
|
||||
resolve(msg.id!);
|
||||
} else {
|
||||
log('db could not get msg by long id', err);
|
||||
resolve(-1);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
log('db could not add msg', err);
|
||||
resolve(-1);
|
||||
}
|
||||
} else {
|
||||
// log("addMsg", this);
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-expect-error
|
||||
msg.id = this.lastID;
|
||||
dbInstance.msgCache.set(msg.msgId, msg);
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// log('获取消息短id成功', this.lastID);
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-expect-error
|
||||
resolve(this.lastID);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async updateMsg(msg: RawMessage) {
|
||||
const existMsg = this.msgCache.get(msg.msgId);
|
||||
if (existMsg) {
|
||||
Object.assign(existMsg, msg);
|
||||
}
|
||||
const stmt = this.db!.prepare('UPDATE msgs SET msg = ?, seq = ? WHERE long_id = ?');
|
||||
try {
|
||||
stmt.run(JSON.stringify(msg), msg.msgSeq, msg.msgId);
|
||||
} catch (e) {
|
||||
log('updateMsg db error', e);
|
||||
}
|
||||
}
|
||||
|
||||
async addFileCache(file: DBFile) {
|
||||
const stmt = this.db!.prepare('INSERT INTO files (name, path, url, size, uuid, elementType ,element, elementId, msgId) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)');
|
||||
return new Promise((resolve, reject) => {
|
||||
stmt.run(file.name, file.path, file.url, file.size, file.uuid,
|
||||
file.elementType,
|
||||
JSON.stringify(file.element),
|
||||
file.elementId,
|
||||
file.msgId,
|
||||
function (err: any) {
|
||||
if (err) {
|
||||
log('db could not add file', err);
|
||||
reject(err);
|
||||
}
|
||||
resolve(null);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private async getFileCache(query: string, params: any[]) {
|
||||
const stmt = this.db!.prepare(query);
|
||||
return new Promise<DBFile | null>((resolve, reject) => {
|
||||
stmt.get(...params, (err: any, row: DBFile & { element: string }) => {
|
||||
if (err) {
|
||||
log('db could not get file cache', err);
|
||||
reject(err);
|
||||
}
|
||||
if (row) {
|
||||
row.element = JSON.parse(row.element);
|
||||
}
|
||||
resolve(row);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async getFileCacheByName(name: string): Promise<DBFile | null> {
|
||||
return this.getFileCache('SELECT * FROM files WHERE name = ?', [name]);
|
||||
}
|
||||
|
||||
async getFileCacheByUuid(uuid: string): Promise<DBFile | null> {
|
||||
return this.getFileCache('SELECT * FROM files WHERE uuid = ?', [uuid]);
|
||||
}
|
||||
|
||||
// todo: 是否所有的文件都有uuid?语音消息有没有uuid?
|
||||
async updateFileCache(file: DBFile) {
|
||||
const stmt = this.db!.prepare('UPDATE files SET path = ?, url = ? WHERE uuid = ?');
|
||||
return new Promise((resolve, reject) => {
|
||||
stmt.run(file.path, file.url, function (err: any) {
|
||||
if (err) {
|
||||
log('db could not update file cache', err);
|
||||
reject(err);
|
||||
}
|
||||
resolve(null);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// 被动收到的临时会话消息uin->uid
|
||||
async getReceivedTempUinMap() {
|
||||
const stmt = 'SELECT * FROM temp_uins';
|
||||
return new Promise<Record<string, string>>((resolve, reject) => {
|
||||
this.db!.all(stmt, (err, rows: { uin: string, uid: string }[]) => {
|
||||
if (err) {
|
||||
log('db could not get temp uin map', err);
|
||||
reject(err);
|
||||
}
|
||||
const map: Record<string, string> = {};
|
||||
rows.forEach(row => {
|
||||
map[row.uin] = row.uid;
|
||||
});
|
||||
resolve(map);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// 通过uin获取临时会话消息uid
|
||||
async getUidByTempUin(uid: string) {
|
||||
const stmt = 'SELECT * FROM temp_uins WHERE uin = ?';
|
||||
return new Promise<string>((resolve, reject) => {
|
||||
this.db!.get(stmt, [uid], (err, row: { uin: string, uid: string }) => {
|
||||
if (err) {
|
||||
log('db could not get temp uin map', err);
|
||||
reject(err);
|
||||
}
|
||||
resolve(row?.uid);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async addTempUin(uin: string, uid: string) {
|
||||
const existUid = await this.getUidByTempUin(uin);
|
||||
if (!existUid) {
|
||||
const stmt = this.db!.prepare('INSERT INTO temp_uins (uin, uid) VALUES (?, ?)');
|
||||
return new Promise((resolve, reject) => {
|
||||
stmt.run(uin, uid, function (err: any) {
|
||||
if (err) {
|
||||
log('db could not add temp uin', err);
|
||||
reject(err);
|
||||
}
|
||||
resolve(null);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export const dbUtil = new DBUtil();
|
@@ -1,273 +1,281 @@
|
||||
import fs from 'fs';
|
||||
import fsPromise from 'fs/promises';
|
||||
import crypto from 'crypto';
|
||||
import fsPromise, { stat } from 'fs/promises';
|
||||
import crypto, { randomUUID } from 'crypto';
|
||||
import util from 'util';
|
||||
import path from 'node:path';
|
||||
import { log } from './log';
|
||||
import { dbUtil } from './db';
|
||||
import * as fileType from 'file-type';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import { napCatCore } from '@/core';
|
||||
import os from 'node:os';
|
||||
|
||||
export const getNapCatDir = () => {
|
||||
const p = path.join(napCatCore.wrapper.dataPath, 'NapCat');
|
||||
fs.mkdirSync(p, { recursive: true });
|
||||
return p;
|
||||
};
|
||||
export const getTempDir = () => {
|
||||
const p = path.join(getNapCatDir(), 'temp');
|
||||
// 创建临时目录
|
||||
if (!fs.existsSync(p)) {
|
||||
fs.mkdirSync(p, { recursive: true });
|
||||
}
|
||||
return p;
|
||||
};
|
||||
|
||||
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';
|
||||
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();
|
||||
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);
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
check();
|
||||
});
|
||||
// 如果文件存在,则无需做任何事情,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\\");
|
||||
// }
|
||||
const readFile = util.promisify(fs.readFile);
|
||||
const result = {
|
||||
err: '',
|
||||
data: '',
|
||||
};
|
||||
try {
|
||||
await checkFileReceived(path, 5000);
|
||||
} catch (e: any) {
|
||||
result.err = e.toString();
|
||||
return result;
|
||||
// 读取文件内容
|
||||
// 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();
|
||||
}
|
||||
const data = await readFile(path);
|
||||
// 转换为Base64编码
|
||||
result.data = data.toString('base64');
|
||||
} catch (err: any) {
|
||||
result.err = err.toString();
|
||||
}
|
||||
return result;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
export function calculateFileMD5(filePath: string): Promise<string> {
|
||||
return new Promise((resolve, reject) => {
|
||||
// 创建一个流式读取器
|
||||
const stream = fs.createReadStream(filePath);
|
||||
const hash = crypto.createHash('md5');
|
||||
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('data', (data: Buffer) => {
|
||||
// 当读取到数据时,更新哈希对象的状态
|
||||
hash.update(data);
|
||||
});
|
||||
|
||||
stream.on('end', () => {
|
||||
// 文件读取完成,计算哈希
|
||||
const md5 = hash.digest('hex');
|
||||
resolve(md5);
|
||||
});
|
||||
stream.on('end', () => {
|
||||
// 文件读取完成,计算哈希
|
||||
const md5 = hash.digest('hex');
|
||||
resolve(md5);
|
||||
});
|
||||
|
||||
stream.on('error', (err: Error) => {
|
||||
// 处理可能的读取错误
|
||||
reject(err);
|
||||
stream.on('error', (err: Error) => {
|
||||
// 处理可能的读取错误
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export interface HttpDownloadOptions {
|
||||
url: string;
|
||||
headers?: Record<string, string> | string;
|
||||
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;
|
||||
} else {
|
||||
url = options.url;
|
||||
if (options.headers) {
|
||||
if (typeof options.headers === 'string') {
|
||||
headers = JSON.parse(options.headers);
|
||||
} else {
|
||||
headers = options.headers;
|
||||
}
|
||||
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);
|
||||
if (!fetchRes.ok) throw new Error(`下载文件失败: ${fetchRes.statusText}`);
|
||||
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);
|
||||
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
|
||||
success: boolean,
|
||||
errMsg: string,
|
||||
fileName: string,
|
||||
ext: string,
|
||||
path: string,
|
||||
isLocal: boolean
|
||||
}
|
||||
|
||||
export async function uri2local(uri: string, fileName: string | null = null): Promise<Uri2LocalRes> {
|
||||
const res = {
|
||||
success: false,
|
||||
errMsg: '',
|
||||
fileName: '',
|
||||
ext: '',
|
||||
path: '',
|
||||
isLocal: false
|
||||
};
|
||||
if (!fileName) {
|
||||
fileName = uuidv4();
|
||||
}
|
||||
let filePath = path.join(getTempDir(), fileName);
|
||||
let url = null;
|
||||
try {
|
||||
url = new URL(uri);
|
||||
} catch (e: any) {
|
||||
res.errMsg = `uri ${uri} 解析失败,` + e.toString() + ` 可能${uri}不存在`;
|
||||
return res;
|
||||
}
|
||||
|
||||
// log("uri protocol", url.protocol, uri);
|
||||
if (url.protocol == 'base64:') {
|
||||
// base64转成文件
|
||||
const base64Data = uri.split('base64://')[1];
|
||||
export async function checkFileV2(filePath: string) {
|
||||
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(uri);
|
||||
} 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
|
||||
const ext: string | undefined = (await fileType.fileTypeFromFile(filePath))?.ext;
|
||||
if (ext) {
|
||||
fs.renameSync(filePath, filePath + `.${ext}`);
|
||||
filePath += `.${ext}`;
|
||||
return { success: true, ext: ext, path: filePath };
|
||||
}
|
||||
}
|
||||
res.fileName = fileName;
|
||||
filePath = path.join(getTempDir(), uuidv4() + 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 {
|
||||
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) {
|
||||
log('获取文件类型', ext, filePath);
|
||||
fs.renameSync(filePath, filePath + `.${ext}`);
|
||||
filePath += `.${ext}`;
|
||||
res.fileName += `.${ext}`;
|
||||
res.ext = ext;
|
||||
}
|
||||
} catch (e) {
|
||||
// log("获取文件类型失败", filePath,e.stack)
|
||||
// log("获取文件类型失败", filePath,e.stack)
|
||||
}
|
||||
}
|
||||
res.success = true;
|
||||
res.path = filePath;
|
||||
return res;
|
||||
return { success: false, ext: '', path: filePath };
|
||||
}
|
||||
|
||||
export async function copyFolder(sourcePath: string, destPath: string) {
|
||||
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);
|
||||
} else {
|
||||
try {
|
||||
await fsPromise.copyFile(srcPath, dstPath);
|
||||
} catch (error) {
|
||||
console.error(`无法复制文件 '${srcPath}' 到 '${dstPath}': ${error}`);
|
||||
// 这里可以决定是否要继续复制其他文件
|
||||
export enum FileUriType {
|
||||
Unknown = 0,
|
||||
Local = 1,
|
||||
Remote = 2,
|
||||
Base64 = 3
|
||||
}
|
||||
export async function checkUriType(Uri: string) {
|
||||
//先判断是否是本地文件
|
||||
try {
|
||||
if (fs.existsSync(Uri)) return { Uri: Uri, Type: FileUriType.Local };
|
||||
} catch (error) {
|
||||
}
|
||||
try {
|
||||
//再判断是否是Http
|
||||
if (Uri.startsWith('http://') || Uri.startsWith('https://')) {
|
||||
return { Uri: Uri, Type: FileUriType.Remote };
|
||||
}
|
||||
}
|
||||
//再判断是否是Base64
|
||||
if (Uri.startsWith('base64://')) {
|
||||
return { Uri: Uri, Type: FileUriType.Base64 };
|
||||
}
|
||||
if (Uri.startsWith('file://')) {
|
||||
let pathname: string;
|
||||
let filePath: string;
|
||||
// await fs.copyFile(url.pathname, filePath);
|
||||
pathname = decodeURIComponent(new URL(Uri).pathname);
|
||||
if (process.platform === 'win32') {
|
||||
filePath = pathname.slice(1);
|
||||
} else {
|
||||
filePath = pathname;
|
||||
}
|
||||
return { Uri: filePath, Type: FileUriType.Local };
|
||||
}
|
||||
} catch (error) {
|
||||
}
|
||||
return { Uri: Uri, Type: FileUriType.Unknown };
|
||||
}
|
||||
export async function uri2local(dir: string, uri: string, filename: string | undefined = undefined): Promise<Uri2LocalRes> {
|
||||
let { Uri: HandledUri, Type: UriType } = await checkUriType(uri);
|
||||
//解析失败
|
||||
|
||||
if (UriType == FileUriType.Unknown) {
|
||||
return { success: false, errMsg: '未知文件类型', fileName: '', ext: '', path: '', isLocal: false };
|
||||
}
|
||||
//解析File协议和本地文件
|
||||
if (UriType == FileUriType.Local) {
|
||||
const fileExt = path.extname(HandledUri);
|
||||
const filename = path.basename(HandledUri, fileExt);
|
||||
return { success: true, errMsg: '', fileName: filename, ext: fileExt, path: HandledUri, isLocal: true };
|
||||
}
|
||||
//接下来都要有文件名
|
||||
if (!filename) filename = randomUUID();
|
||||
//解析Http和Https协议
|
||||
if (UriType == FileUriType.Remote) {
|
||||
const fileExt = path.extname(HandledUri);
|
||||
|
||||
const fileName = filename + fileExt;
|
||||
const filePath = path.join(dir, fileName);
|
||||
const buffer = await httpDownload(HandledUri);
|
||||
fs.writeFileSync(filePath, buffer);
|
||||
return { success: true, errMsg: '', fileName: fileName, ext: fileExt, path: filePath, isLocal: true };
|
||||
}
|
||||
//解析Base64
|
||||
if (UriType == FileUriType.Base64) {
|
||||
const base64 = HandledUri.replace(/^base64:\/\//, '');
|
||||
const buffer = Buffer.from(base64, 'base64');
|
||||
let filePath = path.join(dir, filename);
|
||||
let fileExt = '';
|
||||
fs.writeFileSync(filePath, buffer);
|
||||
const { success, ext, path: fileTypePath } = await checkFileV2(filePath);
|
||||
if (success) {
|
||||
filePath = fileTypePath;
|
||||
fileExt = ext;
|
||||
filename = path.basename(filePath, fileExt);
|
||||
}
|
||||
return { success: true, errMsg: '', fileName: filename, ext: fileExt, path: filePath, isLocal: true };
|
||||
}
|
||||
return { success: false, errMsg: '未知文件类型', fileName: '', ext: '', path: '', isLocal: false };
|
||||
}
|
||||
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);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('复制文件夹时出错:', error);
|
||||
}
|
||||
}
|
||||
|
@@ -1,21 +1,165 @@
|
||||
import crypto from 'node:crypto';
|
||||
import { resolve } from 'dns';
|
||||
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));
|
||||
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');
|
||||
const h = crypto.createHash('md5');
|
||||
h.update(s);
|
||||
return h.digest('hex');
|
||||
}
|
||||
|
||||
export function isNull(value: any) {
|
||||
return value === undefined || value === null;
|
||||
return value === undefined || value === null;
|
||||
}
|
||||
|
||||
export function isNumeric(str: string) {
|
||||
return /^\d+$/.test(str);
|
||||
return /^\d+$/.test(str);
|
||||
}
|
||||
|
||||
export function truncateString(obj: any, maxLength = 500) {
|
||||
if (obj !== null && typeof obj === 'object') {
|
||||
Object.keys(obj).forEach((key) => {
|
||||
if (typeof obj[key] === 'string') {
|
||||
// 如果是字符串且超过指定长度,则截断
|
||||
if (obj[key].length > maxLength) {
|
||||
obj[key] = obj[key].substring(0, maxLength) + '...';
|
||||
}
|
||||
} else if (typeof obj[key] === 'object') {
|
||||
// 如果是对象或数组,则递归调用
|
||||
truncateString(obj[key], maxLength);
|
||||
}
|
||||
});
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
export function 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;
|
||||
}
|
||||
|
@@ -1,4 +1,239 @@
|
||||
import log4js, { Configuration } from 'log4js';
|
||||
import { truncateString } from '@/common/utils/helper';
|
||||
import path from 'node:path';
|
||||
import chalk from 'chalk';
|
||||
import { AtType, ChatType, ElementType, ElementWrapper, RawMessage, SelfInfo } from '@/core';
|
||||
|
||||
export function log(...args: any[]) {
|
||||
console.log(...args);
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
logMessage(msg: RawMessage, selfInfo: SelfInfo) {
|
||||
const isSelfSent = msg.senderUin === selfInfo.uin;
|
||||
this.log(`${
|
||||
isSelfSent ? '发送 ->' : '接收 <-'
|
||||
} ${rawMessageToText(msg)}`);
|
||||
}
|
||||
}
|
||||
|
||||
export function rawMessageToText(msg: RawMessage, recursiveLevel = 0): string {
|
||||
if (recursiveLevel > 2) {
|
||||
return '...';
|
||||
}
|
||||
|
||||
const tokens: string[] = [];
|
||||
|
||||
if (msg.chatType == ChatType.friend) {
|
||||
tokens.push(`私聊 (${msg.peerUin})`);
|
||||
} else if (msg.chatType == ChatType.group) {
|
||||
tokens.push(`群聊 (群 ${msg.peerUin} 的 ${msg.senderUin})`);
|
||||
} else if (msg.chatType == ChatType.chatDevice) {
|
||||
tokens.push('移动设备');
|
||||
} else /* temp */ {
|
||||
tokens.push(`临时消息 (${msg.peerUin})`);
|
||||
}
|
||||
|
||||
// message content
|
||||
|
||||
function msgElementToText(element: ElementWrapper) {
|
||||
if (element.textElement) {
|
||||
if (element.textElement.atType === AtType.notAt) {
|
||||
return element.textElement.content;
|
||||
} else if (element.textElement.atType === AtType.atAll) {
|
||||
return `@全体成员`;
|
||||
} else if (element.textElement.atType === AtType.atUser) {
|
||||
return `${element.textElement.content} (${element.textElement.atUid})`;
|
||||
}
|
||||
}
|
||||
|
||||
if (element.replyElement) {
|
||||
const recordMsgOrNull = msg.records.find(
|
||||
record => element.replyElement!.sourceMsgIdInRecords === record.msgId
|
||||
);
|
||||
return `[回复消息 ${
|
||||
recordMsgOrNull &&
|
||||
recordMsgOrNull.peerUin != '284840486' // 非转发消息; 否则定位不到
|
||||
?
|
||||
rawMessageToText(recordMsgOrNull, recursiveLevel + 1) :
|
||||
`未找到消息记录 (MsgId = ${element.replyElement.sourceMsgIdInRecords})`
|
||||
}]`;
|
||||
}
|
||||
|
||||
if (element.picElement) {
|
||||
return `[图片 ${element.picElement.fileName}]`;
|
||||
}
|
||||
|
||||
if (element.fileElement) {
|
||||
return `[文件 ${element.fileElement.fileName}]`;
|
||||
}
|
||||
|
||||
if (element.videoElement) {
|
||||
return `[视频 ${element.videoElement.fileName}]`;
|
||||
}
|
||||
|
||||
if (element.pttElement) {
|
||||
return `[语音 ${element.pttElement.duration}s]`;
|
||||
}
|
||||
|
||||
if (element.arkElement) {
|
||||
return `[卡片消息 ${element.arkElement.bytesData}]`;
|
||||
}
|
||||
|
||||
if (element.faceElement) {
|
||||
return `[表情 ${element.faceElement.faceText ?? ''}]`;
|
||||
}
|
||||
|
||||
if (element.marketFaceElement) {
|
||||
return `[商城表情 ${element.marketFaceElement.faceName}]`;
|
||||
}
|
||||
|
||||
if (element.markdownElement) {
|
||||
return `[Markdown ${element.markdownElement.content}]`;
|
||||
}
|
||||
|
||||
if (element.multiForwardMsgElement) {
|
||||
return `[转发消息]`;
|
||||
}
|
||||
|
||||
if (element.elementType === ElementType.GreyTip) {
|
||||
return `[灰条消息]`; // TODO: resolve the text
|
||||
}
|
||||
|
||||
return `[未实现 (ElementType = ${element.elementType})]`;
|
||||
}
|
||||
|
||||
for (const element of msg.elements) {
|
||||
tokens.push(msgElementToText(element));
|
||||
}
|
||||
|
||||
return tokens.join(' ');
|
||||
}
|
||||
|
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));
|
||||
}
|
@@ -1,7 +0,0 @@
|
||||
// QQ等级换算
|
||||
import { QQLevel } from '../../ntqqapi/types';
|
||||
|
||||
export function calcQQLevel(level: QQLevel) {
|
||||
const { crownNum, sunNum, moonNum, starNum } = level;
|
||||
return crownNum * 64 + sunNum * 16 + moonNum * 4 + starNum;
|
||||
}
|
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;
|
||||
});
|
||||
}
|
||||
}
|
@@ -1,9 +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 = os.hostname();
|
||||
const homeDir = os.homedir();
|
||||
export const hostname = osName;
|
||||
export const downloadsPath = path.join(homeDir, 'Downloads');
|
||||
export const systemName = os.type();
|
||||
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 };
|
||||
}
|
@@ -1,38 +0,0 @@
|
||||
import { request } from 'https';
|
||||
export function noifyLoginStatus() {
|
||||
const req = request(
|
||||
{
|
||||
hostname: 'napcat.wumiao.wang',
|
||||
path: '/api/send',
|
||||
port: 443,
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36 Edg/124.0.0.0'
|
||||
}
|
||||
},
|
||||
(res) => {
|
||||
//let data = '';
|
||||
res.on('data', (chunk) => {
|
||||
//data += chunk;
|
||||
});
|
||||
res.on('end', () => {
|
||||
//console.log('Response:', data);
|
||||
});
|
||||
}
|
||||
);
|
||||
const StatesData = {
|
||||
type: 'event',
|
||||
payload: {
|
||||
'website': '952bf82f-8f49-4456-aec5-e17db5f27f7e',
|
||||
'hostname': 'napcat.demo.cn',
|
||||
'screen': '1920x1080',
|
||||
'language': 'zh-CN',
|
||||
'title': 'OneBot.Login',
|
||||
'url': '/login/onebot11',
|
||||
'referrer': 'https://napcat.demo.cn/login?type=onebot11'
|
||||
}
|
||||
};
|
||||
req.write(JSON.stringify(StatesData));
|
||||
req.end();
|
||||
}
|
@@ -1,44 +0,0 @@
|
||||
import { get as httpsGet } from 'node:https';
|
||||
function requestMirror(url: string): Promise<string | undefined> {
|
||||
return new Promise((resolve, reject) => {
|
||||
httpsGet(url, (response) => {
|
||||
let data = '';
|
||||
response.on('data', (chunk) => {
|
||||
data += chunk;
|
||||
});
|
||||
|
||||
response.on('end', () => {
|
||||
try {
|
||||
const parsedData = JSON.parse(data);
|
||||
const version = parsedData.version;
|
||||
resolve(version);
|
||||
} catch (error) {
|
||||
// 解析失败或无法访问域名,跳过
|
||||
resolve(undefined);
|
||||
}
|
||||
});
|
||||
}).on('error', (error) => {
|
||||
// 请求失败,跳过
|
||||
resolve(undefined);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export async function checkVersion(): Promise<string> {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
const MirrorList =
|
||||
[
|
||||
'https://fastly.jsdelivr.net/gh/NapNeko/NapCatQQ@main/package.json',
|
||||
'https://gcore.jsdelivr.net/gh/NapNeko/NapCatQQ@main/package.json',
|
||||
'https://cdn.jsdelivr.us/gh/NapNeko/NapCatQQ@main/package.json',
|
||||
'https://jsd.cdn.zzko.cn/gh/NapNeko/NapCatQQ@main/package.json'
|
||||
];
|
||||
for (const url of MirrorList) {
|
||||
const version = await requestMirror(url);
|
||||
if (version) {
|
||||
resolve(version);
|
||||
}
|
||||
}
|
||||
reject('get verison error!');
|
||||
});
|
||||
}
|
File diff suppressed because one or more lines are too long
@@ -1,22 +0,0 @@
|
||||
{
|
||||
"name": "@napneko/core",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"type": "module",
|
||||
"main": "./index.js",
|
||||
"files": [
|
||||
"lib"
|
||||
],
|
||||
"scripts": {
|
||||
"lint": "eslint --fix ./src/**/*.ts",
|
||||
"build:dev": "vite build --mode development",
|
||||
"build:prod": "vite build --mode production",
|
||||
"build": "npm run build:dev"
|
||||
},
|
||||
"author": "NapNeko",
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/NapNeko/NapCatQQ/issues"
|
||||
},
|
||||
"homepage": "https://github.com/NapNeko/NapCatQQ#readme"
|
||||
}
|
27
src/core.lib/src/index.d.ts
vendored
27
src/core.lib/src/index.d.ts
vendored
@@ -1,27 +0,0 @@
|
||||
/// <reference types="node" />
|
||||
import { GlobalAdapter } from './qqnt/adapters';
|
||||
import { QRCodeLoginSucceedType } from './qqnt/services';
|
||||
import { NapCatCoreWrapper } from './wrapper';
|
||||
import { NapCatCoreLogin } from './login';
|
||||
import { NapCatCoreSession } from './session';
|
||||
import { NapCatCoreService } from './service';
|
||||
import { EventEmitter } from 'node:events';
|
||||
import * as log4js from '@log4js-node/log4js-api';
|
||||
export interface LoginSuccessCallback {
|
||||
(): void | Promise<void>;
|
||||
}
|
||||
export declare class NapCatCore extends EventEmitter {
|
||||
readonly log: log4js.Logger;
|
||||
readonly adapter: GlobalAdapter;
|
||||
readonly wrapper: NapCatCoreWrapper;
|
||||
readonly login: NapCatCoreLogin;
|
||||
readonly session: NapCatCoreSession;
|
||||
readonly service: NapCatCoreService;
|
||||
private loginSuccessCbList;
|
||||
constructor();
|
||||
initPostLogin(args: QRCodeLoginSucceedType): Promise<void>;
|
||||
private onLoginSuccess;
|
||||
private onMessage;
|
||||
addLoginSuccessCallback(cb: LoginSuccessCallback): void;
|
||||
}
|
||||
export declare const napCatCore: NapCatCore;
|
File diff suppressed because one or more lines are too long
48
src/core.lib/src/login.d.ts
vendored
48
src/core.lib/src/login.d.ts
vendored
@@ -1,48 +0,0 @@
|
||||
import { LoginListener } from './qqnt/listeners';
|
||||
import { LoginInitConfig, NodeIKernelLoginService } from './qqnt/services';
|
||||
import { NapCatCore } from '.';
|
||||
/**
|
||||
* NapCat 登录相关核心类
|
||||
*
|
||||
* **【注意】**:只有在调用 `init` 方法后才会被真正初始化!
|
||||
*/
|
||||
export declare class NapCatCoreLogin {
|
||||
readonly core: NapCatCore;
|
||||
readonly service: NodeIKernelLoginService;
|
||||
readonly listener: LoginListener;
|
||||
constructor(core: NapCatCore);
|
||||
/**
|
||||
* 初始化 `NodeIKernelLoginService`
|
||||
* @param {LoginInitConfig} config `NodeIKernelLoginService` 初始化配置
|
||||
* @returns {void}
|
||||
*/
|
||||
init(config: LoginInitConfig): void;
|
||||
/**
|
||||
* 初始化监听器,用于向父级 `NapCatCore` 发送事件
|
||||
*/
|
||||
private initListener;
|
||||
/**
|
||||
* 获取在此客户端上登录过的账号列表
|
||||
* @returns {Promise<{ result: number, LocalLoginInfoList: LoginListItem[] }>}
|
||||
*/
|
||||
private getLoginList;
|
||||
/**
|
||||
* 使用二维码方式登录账号,获取到的二维码链接可通过 `system.login.qrcode` 事件获取。
|
||||
*/
|
||||
qrcode(): Promise<void>;
|
||||
/**
|
||||
* 使用快速登录方式登录账号,欲登录的账号必须在此客户端上登录过
|
||||
* @param {string} uin 欲登录账户的 Uin
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
quick(uin: string): Promise<void>;
|
||||
/**
|
||||
* 使用账号密码方式登录,需要滑块验证会发送 `system.login.slider` 事件,登录错误会发送 `system.login.error` 事件。
|
||||
* @param {string} uin 登录账号
|
||||
* @param {string} password 登录密码
|
||||
* @param {string} [proofSig] 验证码返回的 ticket
|
||||
* @param {string} [proofRand] 验证码返回的随机字符串值
|
||||
* @param {string} [proofSid] 验证码的 sid
|
||||
*/
|
||||
password(uin: string, password: string, proofSig?: string, proofRand?: string, proofSid?: string): Promise<void>;
|
||||
}
|
File diff suppressed because one or more lines are too long
@@ -1,14 +0,0 @@
|
||||
interface IDependsAdapter {
|
||||
onMSFStatusChange(args: unknown): void;
|
||||
onMSFSsoError(args: unknown): void;
|
||||
getGroupCode(args: unknown): void;
|
||||
}
|
||||
export interface NodeIDependsAdapter extends IDependsAdapter {
|
||||
new (adapter: IDependsAdapter): NodeIDependsAdapter;
|
||||
}
|
||||
export declare class DependsAdapter implements IDependsAdapter {
|
||||
onMSFStatusChange(args: unknown): void;
|
||||
onMSFSsoError(args: unknown): void;
|
||||
getGroupCode(args: unknown): void;
|
||||
}
|
||||
export {};
|
@@ -1 +0,0 @@
|
||||
var _0x57b317=_0x36a8;function _0x36a8(_0x362896,_0x32c0b4){var _0xa26f0=_0xa26f();return _0x36a8=function(_0x36a8be,_0x3cc4a8){_0x36a8be=_0x36a8be-0x98;var _0x5c3910=_0xa26f0[_0x36a8be];return _0x5c3910;},_0x36a8(_0x362896,_0x32c0b4);}(function(_0x4bfc95,_0x27bdf2){var _0x2d33b2=_0x36a8,_0x2e43ac=_0x4bfc95();while(!![]){try{var _0x101bef=-parseInt(_0x2d33b2(0xa2))/0x1+-parseInt(_0x2d33b2(0xa0))/0x2+-parseInt(_0x2d33b2(0x9e))/0x3+parseInt(_0x2d33b2(0x9d))/0x4*(-parseInt(_0x2d33b2(0x9f))/0x5)+-parseInt(_0x2d33b2(0x98))/0x6+parseInt(_0x2d33b2(0xa1))/0x7+-parseInt(_0x2d33b2(0x99))/0x8*(-parseInt(_0x2d33b2(0x9b))/0x9);if(_0x101bef===_0x27bdf2)break;else _0x2e43ac['push'](_0x2e43ac['shift']());}catch(_0x2e66e0){_0x2e43ac['push'](_0x2e43ac['shift']());}}}(_0xa26f,0x390fc));export class DependsAdapter{[_0x57b317(0x9a)](_0x1a4f3d){}[_0x57b317(0x9c)](_0x160db6){}['getGroupCode'](_0x134d12){}}function _0xa26f(){var _0x1fb9a3=['1770958DwPkqM','174548cPvLOd','912234yRWXqs','88tYhZtg','onMSFStatusChange','728127LcGGeG','onMSFSsoError','4sPbEYq','892254AVBpCc','1155685djlRYh','108122wQMDgr'];_0xa26f=function(){return _0x1fb9a3;};return _0xa26f();}
|
@@ -1,14 +0,0 @@
|
||||
interface IDispatcherAdapter {
|
||||
dispatchRequest(arg: unknown): void;
|
||||
dispatchCall(arg: unknown): void;
|
||||
dispatchCallWithJson(arg: unknown): void;
|
||||
}
|
||||
export interface NodeIDispatcherAdapter extends IDispatcherAdapter {
|
||||
new (adapter: IDispatcherAdapter): NodeIDispatcherAdapter;
|
||||
}
|
||||
export declare class DispatcherAdapter implements IDispatcherAdapter {
|
||||
dispatchRequest(arg: unknown): void;
|
||||
dispatchCall(arg: unknown): void;
|
||||
dispatchCallWithJson(arg: unknown): void;
|
||||
}
|
||||
export {};
|
@@ -1 +0,0 @@
|
||||
function _0x4645(_0x29d00c,_0x944e8a){var _0x4a794b=_0x4a79();return _0x4645=function(_0x464531,_0x6a25e2){_0x464531=_0x464531-0xf2;var _0x468ff7=_0x4a794b[_0x464531];return _0x468ff7;},_0x4645(_0x29d00c,_0x944e8a);}function _0x4a79(){var _0x26cc01=['dispatchRequest','311920KeGlva','535480XzIKhH','2056210YksXki','192516eVLrcf','3265535aYTtGW','dispatchCallWithJson','81lllQjj','2970342zDVsGP','4WJlAhF','dispatchCall','108422nvfYww'];_0x4a79=function(){return _0x26cc01;};return _0x4a79();}var _0x569cde=_0x4645;(function(_0x2c0bf7,_0x545202){var _0x2d129d=_0x4645,_0x3a277a=_0x2c0bf7();while(!![]){try{var _0x51083b=parseInt(_0x2d129d(0xfb))/0x1+-parseInt(_0x2d129d(0xf9))/0x2+parseInt(_0x2d129d(0xf2))/0x3*(parseInt(_0x2d129d(0xf7))/0x4)+-parseInt(_0x2d129d(0xfd))/0x5+parseInt(_0x2d129d(0xf6))/0x6+parseInt(_0x2d129d(0xf3))/0x7+parseInt(_0x2d129d(0xfc))/0x8*(-parseInt(_0x2d129d(0xf5))/0x9);if(_0x51083b===_0x545202)break;else _0x3a277a['push'](_0x3a277a['shift']());}catch(_0x2c2a74){_0x3a277a['push'](_0x3a277a['shift']());}}}(_0x4a79,0x41dda));export class DispatcherAdapter{[_0x569cde(0xfa)](_0xde046){}[_0x569cde(0xf8)](_0x3b470d){}[_0x569cde(0xf4)](_0x199a33){}}
|
@@ -1,24 +0,0 @@
|
||||
interface IGlobalAdapter {
|
||||
onLog(...args: unknown[]): void;
|
||||
onGetSrvCalTime(...args: unknown[]): void;
|
||||
onShowErrUITips(...args: unknown[]): void;
|
||||
fixPicImgType(...args: unknown[]): void;
|
||||
getAppSetting(...args: unknown[]): void;
|
||||
onInstallFinished(...args: unknown[]): void;
|
||||
onUpdateGeneralFlag(...args: unknown[]): void;
|
||||
onGetOfflineMsg(...args: unknown[]): void;
|
||||
}
|
||||
export interface NodeIGlobalAdapter extends IGlobalAdapter {
|
||||
new (adapter: IGlobalAdapter): NodeIGlobalAdapter;
|
||||
}
|
||||
export declare class GlobalAdapter implements IGlobalAdapter {
|
||||
onLog(...args: unknown[]): void;
|
||||
onGetSrvCalTime(...args: unknown[]): void;
|
||||
onShowErrUITips(...args: unknown[]): void;
|
||||
fixPicImgType(...args: unknown[]): void;
|
||||
getAppSetting(...args: unknown[]): void;
|
||||
onInstallFinished(...args: unknown[]): void;
|
||||
onUpdateGeneralFlag(...args: unknown[]): void;
|
||||
onGetOfflineMsg(...args: unknown[]): void;
|
||||
}
|
||||
export {};
|
@@ -1 +0,0 @@
|
||||
var _0x25a6e4=_0x19d4;(function(_0x17fa2a,_0x5a61f7){var _0x153e10=_0x19d4,_0x335668=_0x17fa2a();while(!![]){try{var _0xacf79e=-parseInt(_0x153e10(0x1ae))/0x1+parseInt(_0x153e10(0x1a2))/0x2*(-parseInt(_0x153e10(0x1a8))/0x3)+-parseInt(_0x153e10(0x19f))/0x4*(parseInt(_0x153e10(0x1a7))/0x5)+-parseInt(_0x153e10(0x1a5))/0x6+-parseInt(_0x153e10(0x1a9))/0x7*(parseInt(_0x153e10(0x1aa))/0x8)+-parseInt(_0x153e10(0x1a4))/0x9+parseInt(_0x153e10(0x1a1))/0xa;if(_0xacf79e===_0x5a61f7)break;else _0x335668['push'](_0x335668['shift']());}catch(_0x3a454c){_0x335668['push'](_0x335668['shift']());}}}(_0x2518,0x4e7b5));function _0x2518(){var _0x273672=['5ylrcJh','26799lvrnMh','2458862bUpwCR','8HhILZy','getAppSetting','onInstallFinished','onGetOfflineMsg','369889pZOrDs','804872ifFlmX','onShowErrUITips','21505450cWWtjO','50cWKjWB','onUpdateGeneralFlag','2306016LtyBae','2562972jZIHTW','fixPicImgType'];_0x2518=function(){return _0x273672;};return _0x2518();}function _0x19d4(_0x2acdec,_0x24fa31){var _0x251832=_0x2518();return _0x19d4=function(_0x19d485,_0x552415){_0x19d485=_0x19d485-0x19f;var _0x18198c=_0x251832[_0x19d485];return _0x18198c;},_0x19d4(_0x2acdec,_0x24fa31);}export class GlobalAdapter{['onLog'](..._0x11fd3b){}['onGetSrvCalTime'](..._0x93f2a8){}[_0x25a6e4(0x1a0)](..._0x537250){}[_0x25a6e4(0x1a6)](..._0x8bd0e2){}[_0x25a6e4(0x1ab)](..._0x40e232){}[_0x25a6e4(0x1ac)](..._0x479fcd){}[_0x25a6e4(0x1a3)](..._0x20cb52){}[_0x25a6e4(0x1ad)](..._0x381ed0){}}
|
@@ -1 +0,0 @@
|
||||
(function(_0x3f0548,_0x4fbec7){var _0x30f47c=_0x5520,_0x23c841=_0x3f0548();while(!![]){try{var _0x6e452c=-parseInt(_0x30f47c(0xd0))/0x1*(-parseInt(_0x30f47c(0xd3))/0x2)+-parseInt(_0x30f47c(0xd5))/0x3*(-parseInt(_0x30f47c(0xd4))/0x4)+parseInt(_0x30f47c(0xd7))/0x5*(-parseInt(_0x30f47c(0xd1))/0x6)+-parseInt(_0x30f47c(0xcd))/0x7*(parseInt(_0x30f47c(0xcc))/0x8)+parseInt(_0x30f47c(0xd6))/0x9*(-parseInt(_0x30f47c(0xd2))/0xa)+parseInt(_0x30f47c(0xcf))/0xb+parseInt(_0x30f47c(0xce))/0xc;if(_0x6e452c===_0x4fbec7)break;else _0x23c841['push'](_0x23c841['shift']());}catch(_0x49d4c9){_0x23c841['push'](_0x23c841['shift']());}}}(_0x490d,0x33833));export*from'./NodeIDependsAdapter';export*from'./NodeIDispatcherAdapter';export*from'./NodeIGlobalAdapter';function _0x5520(_0x6df29c,_0x34b267){var _0x490de6=_0x490d();return _0x5520=function(_0x552034,_0x30d8c9){_0x552034=_0x552034-0xcc;var _0x1349c1=_0x490de6[_0x552034];return _0x1349c1;},_0x5520(_0x6df29c,_0x34b267);}function _0x490d(){var _0x14daaf=['246HEKUvN','7016JTzFAQ','51ToMDrp','598194CIeOFq','17745KHyvbm','6376SvVkGK','3647qlxjSN','9452604ZWfenL','1399618PDbghS','2082QsnFYw','522UnokdV','40fvQpoj'];_0x490d=function(){return _0x14daaf;};return _0x490d();}
|
17
src/core.lib/src/qqnt/apis/file.d.ts
vendored
17
src/core.lib/src/qqnt/apis/file.d.ts
vendored
@@ -1,17 +0,0 @@
|
||||
import { ChatType, ElementType } from '@/core/qqnt/entities';
|
||||
import * as fileType from 'file-type';
|
||||
import { ISizeCalculationResult } from 'image-size/dist/types/interface';
|
||||
export declare class NTQQFileApi {
|
||||
static getFileType(filePath: string): Promise<fileType.FileTypeResult | undefined>;
|
||||
static copyFile(filePath: string, destPath: string): Promise<void>;
|
||||
static getFileSize(filePath: string): Promise<number>;
|
||||
static uploadFile(filePath: string, elementType?: ElementType, elementSubType?: number): Promise<{
|
||||
md5: string;
|
||||
fileName: string;
|
||||
path: string;
|
||||
fileSize: number;
|
||||
ext: string;
|
||||
}>;
|
||||
static downloadMedia(msgId: string, chatType: ChatType, peerUid: string, elementId: string, thumbPath: string, sourcePath: string, timeout?: number): Promise<string>;
|
||||
static getImageSize(filePath: string): Promise<ISizeCalculationResult | undefined>;
|
||||
}
|
File diff suppressed because one or more lines are too long
5
src/core.lib/src/qqnt/apis/friend.d.ts
vendored
5
src/core.lib/src/qqnt/apis/friend.d.ts
vendored
@@ -1,5 +0,0 @@
|
||||
import { FriendRequest } from '@/core/qqnt/entities';
|
||||
export declare class NTQQFriendApi {
|
||||
static getFriends(forced?: boolean): Promise<void>;
|
||||
static handleFriendRequest(request: FriendRequest, accept: boolean): Promise<void>;
|
||||
}
|
@@ -1 +0,0 @@
|
||||
function _0x136f(){var _0x487389=['buddy','reqTime','968708hLjUkA','approvalFriendRequest','getFriends','69114EccZVi','198608lLINWk','4380687kTlPFA','2988805qbOqnA','4638628mkIttd','friendUid','2XjXKaA','kernelService','handleFriendRequest','598971OjXYSX','343kfsSKf'];_0x136f=function(){return _0x487389;};return _0x136f();}var _0x1641d3=_0x5d32;function _0x5d32(_0x1b1354,_0x5a83c8){var _0x136ff0=_0x136f();return _0x5d32=function(_0x5d32ee,_0x10806c){_0x5d32ee=_0x5d32ee-0x1c9;var _0x410c9d=_0x136ff0[_0x5d32ee];return _0x410c9d;},_0x5d32(_0x1b1354,_0x5a83c8);}(function(_0x2c89ca,_0xc3cd97){var _0x19bb7f=_0x5d32,_0x4d1a50=_0x2c89ca();while(!![]){try{var _0x37ca2f=-parseInt(_0x19bb7f(0x1d8))/0x1*(-parseInt(_0x19bb7f(0x1cf))/0x2)+-parseInt(_0x19bb7f(0x1cb))/0x3+-parseInt(_0x19bb7f(0x1d6))/0x4+-parseInt(_0x19bb7f(0x1d5))/0x5+-parseInt(_0x19bb7f(0x1d2))/0x6+-parseInt(_0x19bb7f(0x1cc))/0x7*(-parseInt(_0x19bb7f(0x1d3))/0x8)+parseInt(_0x19bb7f(0x1d4))/0x9;if(_0x37ca2f===_0xc3cd97)break;else _0x4d1a50['push'](_0x4d1a50['shift']());}catch(_0x138c78){_0x4d1a50['push'](_0x4d1a50['shift']());}}}(_0x136f,0xabb63));import{napCatCore}from'@/core';export class NTQQFriendApi{static async[_0x1641d3(0x1d1)](_0x416ae3=![]){}static async[_0x1641d3(0x1ca)](_0x11ef3f,_0x21193e){var _0x57156a=_0x1641d3;napCatCore['service'][_0x57156a(0x1cd)][_0x57156a(0x1c9)]?.[_0x57156a(0x1d0)]({'friendUid':_0x11ef3f[_0x57156a(0x1d7)],'reqTime':_0x11ef3f[_0x57156a(0x1ce)],'accept':_0x21193e});}}
|
20
src/core.lib/src/qqnt/apis/group.d.ts
vendored
20
src/core.lib/src/qqnt/apis/group.d.ts
vendored
@@ -1,20 +0,0 @@
|
||||
import { GroupMember, GroupRequestOperateTypes, GroupMemberRole, GroupNotify } from '../entities';
|
||||
export declare class NTQQGroupApi {
|
||||
static getGroups(forced?: boolean): Promise<void>;
|
||||
static getGroupMembers(groupQQ: string, num?: number): Promise<void | GroupMember[]>;
|
||||
static getGroupNotifies(): Promise<void>;
|
||||
static getGroupIgnoreNotifies(): Promise<void>;
|
||||
static handleGroupRequest(notify: GroupNotify, operateType: GroupRequestOperateTypes, reason?: string): Promise<void | undefined>;
|
||||
static quitGroup(groupQQ: string): Promise<void | undefined>;
|
||||
static kickMember(groupQQ: string, kickUids: string[], refuseForever?: boolean, kickReason?: string): Promise<void | undefined>;
|
||||
static banMember(groupQQ: string, memList: Array<{
|
||||
uid: string;
|
||||
timeStamp: number;
|
||||
}>): Promise<void | undefined>;
|
||||
static banGroup(groupQQ: string, shutUp: boolean): Promise<void | undefined>;
|
||||
static setMemberCard(groupQQ: string, memberUid: string, cardName: string): Promise<void | undefined>;
|
||||
static setMemberRole(groupQQ: string, memberUid: string, role: GroupMemberRole): Promise<void | undefined>;
|
||||
static setGroupName(groupQQ: string, groupName: string): Promise<void | undefined>;
|
||||
static setGroupTitle(groupQQ: string, uid: string, title: string): Promise<void>;
|
||||
static publishGroupBulletin(groupQQ: string, title: string, content: string): void;
|
||||
}
|
@@ -1 +0,0 @@
|
||||
function _0x2e16(_0x1fe738,_0x24e634){var _0x176ed5=_0x176e();return _0x2e16=function(_0x2e16b5,_0x1efc2a){_0x2e16b5=_0x2e16b5-0xd8;var _0x966900=_0x176ed5[_0x2e16b5];return _0x966900;},_0x2e16(_0x1fe738,_0x24e634);}function _0x176e(){var _0x3c3558=['110BRfNSO','publishGroupBulletin','setMemberRole','setGroupShutUp','setGroupName','setMemberCard','getGroupMembers','24XrbdEg','8MALKOK','groupCode','79758ZtgTwZ','6rFdEQw','3625821rZFgFC','783936QZfBKE','getGroupNotifies','kickMember','kernelService','387321sYhpce','banGroup','2804400pQTOdP','quitGroup','303455eSqRtV','modifyMemberRole','group','service','type','getGroupIgnoreNotifies','handleGroupRequest','getGroups','95346qOipUk','setGroupTitle'];_0x176e=function(){return _0x3c3558;};return _0x176e();}var _0x49231b=_0x2e16;(function(_0x38b36f,_0x2862d4){var _0x806a51=_0x2e16,_0x478a07=_0x38b36f();while(!![]){try{var _0x539c5b=-parseInt(_0x806a51(0xe3))/0x1*(-parseInt(_0x806a51(0xf0))/0x2)+parseInt(_0x806a51(0xf2))/0x3+parseInt(_0x806a51(0xed))/0x4*(parseInt(_0x806a51(0xdb))/0x5)+-parseInt(_0x806a51(0xd9))/0x6+parseInt(_0x806a51(0xef))/0x7*(parseInt(_0x806a51(0xec))/0x8)+parseInt(_0x806a51(0xf1))/0x9+-parseInt(_0x806a51(0xe5))/0xa*(parseInt(_0x806a51(0xf6))/0xb);if(_0x539c5b===_0x2862d4)break;else _0x478a07['push'](_0x478a07['shift']());}catch(_0x9a9237){_0x478a07['push'](_0x478a07['shift']());}}}(_0x176e,0x3d4b6));import{napCatCore}from'@/core';export class NTQQGroupApi{static async[_0x49231b(0xe2)](_0xc23cc8=![]){}static async[_0x49231b(0xeb)](_0x517356,_0x581b3a=0xbb8){}static async[_0x49231b(0xf3)](){}static async[_0x49231b(0xe0)](){}static async[_0x49231b(0xe1)](_0x59e3a1,_0x2b8a38,_0x2426b9){var _0x48ad61=_0x49231b,_0x1e46a9={'ilRex':function(_0x364404,_0x3301a1){return _0x364404||_0x3301a1;}};return napCatCore['service'][_0x48ad61(0xdd)][_0x48ad61(0xf5)]?.['operateSysNotify'](![],{'operateType':_0x2b8a38,'targetMsg':{'seq':_0x59e3a1['seq'],'type':_0x59e3a1[_0x48ad61(0xdf)],'groupCode':_0x59e3a1[_0x48ad61(0xdd)][_0x48ad61(0xee)],'postscript':_0x1e46a9['ilRex'](_0x2426b9,'')}});}static async[_0x49231b(0xda)](_0x4c7963){var _0xd951c6=_0x49231b;return napCatCore[_0xd951c6(0xde)][_0xd951c6(0xdd)][_0xd951c6(0xf5)]?.[_0xd951c6(0xda)](_0x4c7963);}static async['kickMember'](_0x2ad7f0,_0x40077a,_0x27b012=![],_0x18bff2=''){var _0x29848f=_0x49231b;return napCatCore[_0x29848f(0xde)]['group'][_0x29848f(0xf5)]?.[_0x29848f(0xf4)](_0x2ad7f0,_0x40077a,_0x27b012,_0x18bff2);}static async['banMember'](_0x3199e0,_0x353d73){var _0x332877=_0x49231b;return napCatCore[_0x332877(0xde)][_0x332877(0xdd)][_0x332877(0xf5)]?.['setMemberShutUp'](_0x3199e0,_0x353d73);}static async[_0x49231b(0xd8)](_0x4f3b07,_0xb8aea4){var _0x3cd20c=_0x49231b;return napCatCore[_0x3cd20c(0xde)][_0x3cd20c(0xdd)][_0x3cd20c(0xf5)]?.[_0x3cd20c(0xe8)](_0x4f3b07,_0xb8aea4);}static async[_0x49231b(0xea)](_0x59c404,_0x3164c1,_0x3641bd){var _0x3d1f4b=_0x49231b;return napCatCore['service'][_0x3d1f4b(0xdd)][_0x3d1f4b(0xf5)]?.['modifyMemberCardName'](_0x59c404,_0x3164c1,_0x3641bd);}static async[_0x49231b(0xe7)](_0x222e29,_0x379e55,_0x431acd){var _0x4babe7=_0x49231b;return napCatCore[_0x4babe7(0xde)][_0x4babe7(0xdd)]['kernelService']?.[_0x4babe7(0xdc)](_0x222e29,_0x379e55,_0x431acd);}static async[_0x49231b(0xe9)](_0x2f1dc9,_0x29d198){var _0xe8c9a1=_0x49231b;return napCatCore[_0xe8c9a1(0xde)]['group'][_0xe8c9a1(0xf5)]?.['modifyGroupName'](_0x2f1dc9,_0x29d198,![]);}static async[_0x49231b(0xe4)](_0x15f265,_0x26d3f9,_0x146c6d){}static[_0x49231b(0xe6)](_0x5da3aa,_0x11eaab,_0x2124ec){}}
|
@@ -1 +0,0 @@
|
||||
function _0x55af(_0x3322b9,_0x4251af){var _0x4f1440=_0x4f14();return _0x55af=function(_0x55af60,_0xf92f9e){_0x55af60=_0x55af60-0x1a4;var _0x24d4ba=_0x4f1440[_0x55af60];return _0x24d4ba;},_0x55af(_0x3322b9,_0x4251af);}(function(_0x4482eb,_0x8e60a){var _0x194bce=_0x55af,_0x252dd6=_0x4482eb();while(!![]){try{var _0x4eb392=parseInt(_0x194bce(0x1ad))/0x1+parseInt(_0x194bce(0x1ac))/0x2*(parseInt(_0x194bce(0x1a8))/0x3)+-parseInt(_0x194bce(0x1a7))/0x4+-parseInt(_0x194bce(0x1a4))/0x5*(parseInt(_0x194bce(0x1aa))/0x6)+-parseInt(_0x194bce(0x1a6))/0x7+-parseInt(_0x194bce(0x1a5))/0x8*(-parseInt(_0x194bce(0x1ab))/0x9)+parseInt(_0x194bce(0x1a9))/0xa;if(_0x4eb392===_0x8e60a)break;else _0x252dd6['push'](_0x252dd6['shift']());}catch(_0x4ba48e){_0x252dd6['push'](_0x252dd6['shift']());}}}(_0x4f14,0xc57d0));export*from'./file';export*from'./friend';function _0x4f14(){var _0x340e14=['5482564mnDlZV','33JSzSaI','8898180tkZBEn','78aZQHzF','117DdOuYC','215362jwkVMI','1180535QjSBVq','404065odSirS','763304xkSfNs','8855637vsSKnT'];_0x4f14=function(){return _0x340e14;};return _0x4f14();}export*from'./group';export*from'./msg';export*from'./user';export*from'./webapi';export*from'./window';
|
19
src/core.lib/src/qqnt/apis/msg.d.ts
vendored
19
src/core.lib/src/qqnt/apis/msg.d.ts
vendored
@@ -1,19 +0,0 @@
|
||||
import { Peer, RawMessage, SendMessageElement } from '@/core/qqnt/entities';
|
||||
import { NapCatCore } from '@/core';
|
||||
import { GeneralCallResult } from '@/core/qqnt/services/common';
|
||||
export declare class NTQQMsgApi {
|
||||
static napCatCore: NapCatCore | null;
|
||||
static getMultiMsg(peer: Peer, rootMsgId: string, parentMsgId: string): Promise<GeneralCallResult & {
|
||||
msgList: RawMessage[];
|
||||
} | undefined>;
|
||||
static activateChat(peer: Peer): Promise<void>;
|
||||
static activateChatAndGetHistory(peer: Peer): Promise<void>;
|
||||
static getMsgHistory(peer: Peer, msgId: string, count: number): Promise<GeneralCallResult & {
|
||||
msgList: RawMessage[];
|
||||
}>;
|
||||
static fetchRecentContact(): Promise<void>;
|
||||
static recallMsg(peer: Peer, msgIds: string[]): Promise<void>;
|
||||
static sendMsg(peer: Peer, msgElements: SendMessageElement[], waitComplete?: boolean, timeout?: number): Promise<RawMessage>;
|
||||
static forwardMsg(srcPeer: Peer, destPeer: Peer, msgIds: string[]): Promise<void>;
|
||||
static multiForwardMsg(srcPeer: Peer, destPeer: Peer, msgIds: string[]): Promise<RawMessage>;
|
||||
}
|
File diff suppressed because one or more lines are too long
19
src/core.lib/src/qqnt/apis/user.d.ts
vendored
19
src/core.lib/src/qqnt/apis/user.d.ts
vendored
@@ -1,19 +0,0 @@
|
||||
import { User } from '@/core/qqnt/entities';
|
||||
export declare class NTQQUserApi {
|
||||
static like(uid: string, count?: number): Promise<{
|
||||
result: number;
|
||||
errMsg: string;
|
||||
succCounts: number;
|
||||
}>;
|
||||
static setQQAvatar(filePath: string): Promise<{
|
||||
result: number;
|
||||
errMsg: string;
|
||||
}>;
|
||||
static getSelfInfo(): Promise<void>;
|
||||
static getUserInfo(uid: string): Promise<void>;
|
||||
static getUserDetailInfo(uid: string): Promise<User>;
|
||||
static getPSkey(): Promise<void>;
|
||||
static getSkey(groupName: string, groupCode: string): Promise<void | {
|
||||
data: string;
|
||||
}>;
|
||||
}
|
@@ -1 +0,0 @@
|
||||
const _0x2fcf14=_0x51c8;(function(_0x4b2074,_0x262595){const _0x5f10c8=_0x51c8,_0x241f2a=_0x4b2074();while(!![]){try{const _0xcee58d=-parseInt(_0x5f10c8(0x1e2))/0x1+parseInt(_0x5f10c8(0x1ed))/0x2*(-parseInt(_0x5f10c8(0x1f6))/0x3)+parseInt(_0x5f10c8(0x1dd))/0x4*(-parseInt(_0x5f10c8(0x1da))/0x5)+-parseInt(_0x5f10c8(0x1f3))/0x6*(parseInt(_0x5f10c8(0x1e4))/0x7)+parseInt(_0x5f10c8(0x1e6))/0x8+parseInt(_0x5f10c8(0x1f2))/0x9*(-parseInt(_0x5f10c8(0x1de))/0xa)+parseInt(_0x5f10c8(0x1e9))/0xb*(parseInt(_0x5f10c8(0x1f8))/0xc);if(_0xcee58d===_0x262595)break;else _0x241f2a['push'](_0x241f2a['shift']());}catch(_0x365546){_0x241f2a['push'](_0x241f2a['shift']());}}}(_0x360d,0xcc3b8));function _0x51c8(_0x5155d8,_0x18859a){const _0x360d3f=_0x360d();return _0x51c8=function(_0x51c87e,_0x3b9dd2){_0x51c87e=_0x51c87e-0x1d6;let _0x2df455=_0x360d3f[_0x51c87e];return _0x2df455;},_0x51c8(_0x5155d8,_0x18859a);}import{napCatCore}from'@/core';import{ProfileListener}from'@/core/qqnt/listeners';import{randomUUID}from'crypto';const userInfoCache={},profileListener=new ProfileListener(),userDetailHandlers=new Map();function _0x360d(){const _0x1414b4=['service','getUserInfo','setHeader','1717745WdLtmA','then','forEach','4ZCzEjf','2105260tMnSQF','addProfileListener','uid','setBuddyProfileLike','631300wTXgrZ','set','126cFpJhN','numBP','1581472kNEuDG','profileLike','profile','461087WoGnak','sGOSk','onProfileDetailInfoChanged','result','2122txYGyQ','getUserDetailInfoWithBizInfo','sSbeu','setQQAvatar','getPSkey','54vjcoXS','196338fFqHqx','addLoginSuccessCallback','getSkey','393GwEKLv','getUserDetailInfo\x20timeout','1032hpzqUQ','kernelService'];_0x360d=function(){return _0x1414b4;};return _0x360d();}profileListener[_0x2fcf14(0x1eb)]=_0x4f43df=>{const _0x131fd3=_0x2fcf14;userInfoCache[_0x4f43df[_0x131fd3(0x1e0)]]=_0x4f43df,userDetailHandlers[_0x131fd3(0x1dc)](_0xc9895d=>_0xc9895d(_0x4f43df));},setTimeout(()=>{const _0x4993c6=_0x2fcf14;napCatCore[_0x4993c6(0x1f4)](()=>{const _0x1059b2=_0x4993c6;napCatCore[_0x1059b2(0x1d7)][_0x1059b2(0x1e8)][_0x1059b2(0x1df)](profileListener);});},0x64);export class NTQQUserApi{static async['like'](_0x3883a5,_0x4ac913=0x1){const _0x36aee2=_0x2fcf14;return napCatCore[_0x36aee2(0x1d7)][_0x36aee2(0x1e7)]['kernelService'][_0x36aee2(0x1e1)]({'friendUid':_0x3883a5,'sourceId':0x47,'doLikeCount':_0x4ac913,'doLikeTollCount':0x0});}static async[_0x2fcf14(0x1f0)](_0x59a957){const _0x336e7a=_0x2fcf14,_0x42da54=napCatCore[_0x336e7a(0x1d7)][_0x336e7a(0x1e8)][_0x336e7a(0x1d6)]?.[_0x336e7a(0x1d9)](_0x59a957);return{'result':_0x42da54?.[_0x336e7a(0x1ec)],'errMsg':_0x42da54?.['errMsg']};}static async['getSelfInfo'](){}static async[_0x2fcf14(0x1d8)](_0x37b088){}static async['getUserDetailInfo'](_0x5706fe){const _0x3646bb=_0x2fcf14,_0x31742b={'sGOSk':function(_0x300d49,_0x290712){return _0x300d49(_0x290712);},'OaVYQ':_0x3646bb(0x1f7),'sSbeu':function(_0x1be533,_0x2a30c2){return _0x1be533===_0x2a30c2;},'WLFVD':function(_0x55c6fe){return _0x55c6fe();},'numBP':function(_0x5e29f7,_0x47a6eb,_0x570b10){return _0x5e29f7(_0x47a6eb,_0x570b10);}},_0x579a72=napCatCore[_0x3646bb(0x1d7)]['profile'][_0x3646bb(0x1d6)];return new Promise((_0x1fb245,_0x2b7600)=>{const _0x15c507=_0x3646bb,_0x4bc433=_0x31742b['WLFVD'](randomUUID);let _0x1196ff=![];_0x31742b[_0x15c507(0x1e5)](setTimeout,()=>{const _0x32d6a2=_0x15c507;!_0x1196ff&&_0x31742b[_0x32d6a2(0x1ea)](_0x2b7600,_0x31742b['OaVYQ']);},0x1388),userDetailHandlers[_0x15c507(0x1e3)](_0x4bc433,_0x11b418=>{const _0x57c2a7=_0x15c507;_0x31742b[_0x57c2a7(0x1ef)](_0x11b418['uid'],_0x5706fe)&&(_0x1196ff=!![],userDetailHandlers['delete'](_0x4bc433),_0x1fb245(_0x11b418));}),_0x579a72[_0x15c507(0x1ee)](_0x5706fe,[0x0])[_0x15c507(0x1db)](_0x29bd1e=>{});});}static async[_0x2fcf14(0x1f1)](){}static async[_0x2fcf14(0x1f5)](_0x51814e,_0x1eb586){}}
|
13
src/core.lib/src/qqnt/apis/webapi.d.ts
vendored
13
src/core.lib/src/qqnt/apis/webapi.d.ts
vendored
@@ -1,13 +0,0 @@
|
||||
export declare class WebApi {
|
||||
private static bkn;
|
||||
private static skey;
|
||||
private static pskey;
|
||||
private static cookie;
|
||||
private defaultHeaders;
|
||||
constructor();
|
||||
addGroupDigest(groupCode: string, msgSeq: string): Promise<any>;
|
||||
getGroupDigest(groupCode: string): Promise<any>;
|
||||
private genBkn;
|
||||
private init;
|
||||
private request;
|
||||
}
|
@@ -1 +0,0 @@
|
||||
function _0x259a(_0x1b9711,_0x27a337){const _0x5d7d26=_0x5d7d();return _0x259a=function(_0x259aed,_0x51a275){_0x259aed=_0x259aed-0x1a6;let _0x49c585=_0x5d7d26[_0x259aed];return _0x49c585;},_0x259a(_0x1b9711,_0x27a337);}const _0x1d44f9=_0x259a;function _0x5d7d(){const _0x39f97c=['qrGuG','&msg_seq=','https://qun.qq.com/cgi-bin/group_digest/digest_list?random=665&X-CROSS-ORIGIN=fetch&group_code=','HRZJE','28nprdIM','16737147XokBSw','3162156lIVlJc','&msg_random=444021292','218354wIQpum','getGroupDigest','genBkn','pskey','RAglJ','nAOaX','json','headers','request','addGroupDigest','init','charCodeAt','2566616EeBMzW','SUnoY','include','GET','cookie','toString','Htqvv','bkn','defaultHeaders','4EdNICD','2641620XwewlF','4633585gGPEBp','lOhDm','615640HQEWdu','&bkn='];_0x5d7d=function(){return _0x39f97c;};return _0x5d7d();}(function(_0x29dbc2,_0x1f61fd){const _0x45bbf4=_0x259a,_0x58236a=_0x29dbc2();while(!![]){try{const _0x5d557f=parseInt(_0x45bbf4(0x1b7))/0x1+-parseInt(_0x45bbf4(0x1c3))/0x2+parseInt(_0x45bbf4(0x1b5))/0x3*(-parseInt(_0x45bbf4(0x1a9))/0x4)+parseInt(_0x45bbf4(0x1ab))/0x5+parseInt(_0x45bbf4(0x1aa))/0x6+-parseInt(_0x45bbf4(0x1b3))/0x7*(parseInt(_0x45bbf4(0x1ad))/0x8)+parseInt(_0x45bbf4(0x1b4))/0x9;if(_0x5d557f===_0x1f61fd)break;else _0x58236a['push'](_0x58236a['shift']());}catch(_0x4f27f8){_0x58236a['push'](_0x58236a['shift']());}}}(_0x5d7d,0xc3464));import{log}from'@/common/utils/log';export class WebApi{static [_0x1d44f9(0x1a7)];static ['skey'];static [_0x1d44f9(0x1ba)];static [_0x1d44f9(0x1c7)];[_0x1d44f9(0x1a8)]={'User-Agent':'QQ/8.9.28.635\x20CFNetwork/1312\x20Darwin/21.0.0'};constructor(){}async[_0x1d44f9(0x1c0)](_0x19bac9,_0x34b30e){const _0x5458ef=_0x1d44f9,_0x40814e='https://qun.qq.com/cgi-bin/group_digest/cancel_digest?random=665&X-CROSS-ORIGIN=fetch&group_code='+_0x19bac9+_0x5458ef(0x1b0)+_0x34b30e+_0x5458ef(0x1b6),_0x285b21=await this['request'](_0x40814e);return await _0x285b21[_0x5458ef(0x1bd)]();}async[_0x1d44f9(0x1b8)](_0x5f6465){const _0x2330c7=_0x1d44f9,_0x3e440a={'Htqvv':function(_0x1dee82,_0x3d4f6d){return _0x1dee82(_0x3d4f6d);}},_0x3af767=_0x2330c7(0x1b1)+_0x5f6465+'&page_start=0&page_limit=20',_0x5f000b=await this[_0x2330c7(0x1bf)](_0x3af767);return _0x3e440a[_0x2330c7(0x1a6)](log,_0x5f000b[_0x2330c7(0x1be)]),await _0x5f000b[_0x2330c7(0x1bd)]();}[_0x1d44f9(0x1b9)](_0x1d0bf6){const _0x59e03c=_0x1d44f9,_0x2cf436={'SUTmQ':function(_0x177bec,_0xdcbe98){return _0x177bec<_0xdcbe98;},'RAglJ':function(_0x4c0694,_0x4b9e5d){return _0x4c0694+_0x4b9e5d;},'SUnoY':function(_0x370d8c,_0x3d141c){return _0x370d8c<<_0x3d141c;},'qrGuG':function(_0x31baff,_0x5bddcc){return _0x31baff&_0x5bddcc;}};_0x1d0bf6=_0x1d0bf6||'';let _0x2c07e3=0x1505;for(let _0x21d4d1=0x0;_0x2cf436['SUTmQ'](_0x21d4d1,_0x1d0bf6['length']);_0x21d4d1++){const _0xd49f72=_0x1d0bf6[_0x59e03c(0x1c2)](_0x21d4d1);_0x2c07e3=_0x2cf436[_0x59e03c(0x1bb)](_0x2c07e3+_0x2cf436[_0x59e03c(0x1c4)](_0x2c07e3,0x5),_0xd49f72);}return _0x2cf436[_0x59e03c(0x1af)](_0x2c07e3,0x7fffffff)[_0x59e03c(0x1c8)]();}async[_0x1d44f9(0x1c1)](){if(!WebApi['bkn']){}}async['request'](_0x50aa56,_0x5b0a36=_0x1d44f9(0x1c6),_0x62f02a={}){const _0x4194c5=_0x1d44f9,_0x5c7129={'wOKDy':_0x4194c5(0x1ae),'nAOaX':_0x4194c5(0x1c5),'HRZJE':function(_0x364283,_0x5786da,_0xfdddda,_0x192eb4){return _0x364283(_0x5786da,_0xfdddda,_0x192eb4);},'lOhDm':function(_0x16e2f3,_0x1a8c32,_0x35dda0){return _0x16e2f3(_0x1a8c32,_0x35dda0);}};await this['init'](),_0x50aa56+=_0x5c7129['wOKDy']+WebApi['bkn'];const _0x4dc4c2={...this['defaultHeaders'],..._0x62f02a,'Cookie':WebApi[_0x4194c5(0x1c7)],'credentials':_0x5c7129[_0x4194c5(0x1bc)]};_0x5c7129[_0x4194c5(0x1b2)](log,'request',_0x50aa56,_0x4dc4c2);const _0x138d74={'method':_0x5b0a36,'headers':_0x4dc4c2};return _0x5c7129[_0x4194c5(0x1ac)](fetch,_0x50aa56,_0x138d74);}}
|
11
src/core.lib/src/qqnt/apis/window.d.ts
vendored
11
src/core.lib/src/qqnt/apis/window.d.ts
vendored
@@ -1,11 +0,0 @@
|
||||
export interface NTQQWindow {
|
||||
windowName: string;
|
||||
windowUrlHash: string;
|
||||
}
|
||||
export declare class NTQQWindows {
|
||||
static GroupHomeWorkWindow: NTQQWindow;
|
||||
static GroupNotifyFilterWindow: NTQQWindow;
|
||||
static GroupEssenceWindow: NTQQWindow;
|
||||
}
|
||||
export declare class NTQQWindowApi {
|
||||
}
|
@@ -1 +0,0 @@
|
||||
var _0x5811b1=_0x1b27;(function(_0x2c452c,_0x325f2e){var _0x571cbe=_0x1b27,_0x41c785=_0x2c452c();while(!![]){try{var _0x27a39f=-parseInt(_0x571cbe(0x10a))/0x1*(parseInt(_0x571cbe(0xff))/0x2)+parseInt(_0x571cbe(0x109))/0x3+-parseInt(_0x571cbe(0x104))/0x4*(parseInt(_0x571cbe(0xfd))/0x5)+-parseInt(_0x571cbe(0xfe))/0x6+parseInt(_0x571cbe(0xfb))/0x7*(-parseInt(_0x571cbe(0x103))/0x8)+-parseInt(_0x571cbe(0xfc))/0x9+-parseInt(_0x571cbe(0x100))/0xa*(-parseInt(_0x571cbe(0x106))/0xb);if(_0x27a39f===_0x325f2e)break;else _0x41c785['push'](_0x41c785['shift']());}catch(_0x23c487){_0x41c785['push'](_0x41c785['shift']());}}}(_0x5c44,0xae894));function _0x1b27(_0x1557f4,_0x3d2e9e){var _0x5c44cb=_0x5c44();return _0x1b27=function(_0x1b2736,_0x54d018){_0x1b2736=_0x1b2736-0xfb;var _0x47d46d=_0x5c44cb[_0x1b2736];return _0x47d46d;},_0x1b27(_0x1557f4,_0x3d2e9e);}export class NTQQWindows{static ['GroupHomeWorkWindow']={'windowName':_0x5811b1(0x101),'windowUrlHash':_0x5811b1(0x107)};static [_0x5811b1(0x105)]={'windowName':_0x5811b1(0x105),'windowUrlHash':'#/group-notify-filter'};static [_0x5811b1(0x102)]={'windowName':_0x5811b1(0x102),'windowUrlHash':_0x5811b1(0x108)};}export class NTQQWindowApi{}function _0x5c44(){var _0x48df20=['58840qvqEfd','GroupHomeWorkWindow','GroupEssenceWindow','518944YSwNAE','128xftkxA','GroupNotifyFilterWindow','4279Agnxeo','#/group-home-work','#/group-essence','1891380ABbPkG','1367oewWPE','84fzDtKk','7141716XHlRNS','30315VziGyZ','2466840rrUofs','40XWkrdP'];_0x5c44=function(){return _0x48df20;};return _0x5c44();}
|
@@ -1 +0,0 @@
|
||||
(function(_0x4f558f,_0x3d5d0a){var _0x53741d=_0x4e77,_0x1e567c=_0x4f558f();while(!![]){try{var _0x24be65=parseInt(_0x53741d(0x1c9))/0x1+-parseInt(_0x53741d(0x1c8))/0x2+-parseInt(_0x53741d(0x1cb))/0x3*(parseInt(_0x53741d(0x1c2))/0x4)+parseInt(_0x53741d(0x1d0))/0x5+parseInt(_0x53741d(0x1c1))/0x6+-parseInt(_0x53741d(0x1c4))/0x7*(parseInt(_0x53741d(0x1cf))/0x8)+-parseInt(_0x53741d(0x1c0))/0x9;if(_0x24be65===_0x3d5d0a)break;else _0x1e567c['push'](_0x1e567c['shift']());}catch(_0x47dbc8){_0x1e567c['push'](_0x1e567c['shift']());}}}(_0x4471,0xc801e));export var CacheFileType;function _0x4e77(_0xc9c32d,_0x11bf9c){var _0x44713f=_0x4471();return _0x4e77=function(_0x4e772f,_0x10fabe){_0x4e772f=_0x4e772f-0x1bf;var _0x2b02e5=_0x44713f[_0x4e772f];return _0x2b02e5;},_0x4e77(_0xc9c32d,_0x11bf9c);}function _0x4471(){var _0x5ada04=['1537921rkwNyv','3|1|4|2|0','12hWfcjq','pSJjv','cBhUo','split','4562672iQfBMK','1628855djlRgU','DOCUMENT','uDtes','KgoRy','1197702ZPFDWj','1046046YXSIfz','251824jeIdER','OTHER','7wcFfgX','osuEc','VIDEO','IMAGE','527134AHvlPR'];_0x4471=function(){return _0x5ada04;};return _0x4471();}(function(_0xd3ea3a){var _0x1afd11=_0x4e77,_0x977432={'YGtuD':_0x1afd11(0x1ca),'cBhUo':_0x1afd11(0x1c3),'KgoRy':_0x1afd11(0x1c6),'uDtes':_0x1afd11(0x1d1),'osuEc':_0x1afd11(0x1c7),'pSJjv':'AUDIO'},_0x46fd8e=_0x977432['YGtuD'][_0x1afd11(0x1ce)]('|'),_0x1ff8cd=0x0;while(!![]){switch(_0x46fd8e[_0x1ff8cd++]){case'0':_0xd3ea3a[_0xd3ea3a[_0x977432['cBhUo']]=0x4]=_0x977432[_0x1afd11(0x1cd)];continue;case'1':_0xd3ea3a[_0xd3ea3a[_0x977432['KgoRy']]=0x1]=_0x977432[_0x1afd11(0x1bf)];continue;case'2':_0xd3ea3a[_0xd3ea3a[_0x977432[_0x1afd11(0x1d2)]]=0x3]=_0x977432[_0x1afd11(0x1d2)];continue;case'3':_0xd3ea3a[_0xd3ea3a[_0x977432[_0x1afd11(0x1c5)]]=0x0]=_0x1afd11(0x1c7);continue;case'4':_0xd3ea3a[_0xd3ea3a[_0x977432[_0x1afd11(0x1cc)]]=0x2]=_0x977432[_0x1afd11(0x1cc)];continue;}break;}}(CacheFileType||(CacheFileType={})));
|
12
src/core.lib/src/qqnt/entities/constructor.d.ts
vendored
12
src/core.lib/src/qqnt/entities/constructor.d.ts
vendored
@@ -1,12 +0,0 @@
|
||||
import { AtType, SendArkElement, SendFaceElement, SendFileElement, SendPicElement, SendPttElement, SendReplyElement, SendTextElement, SendVideoElement } from '../entities';
|
||||
export declare class SendMsgElementConstructor {
|
||||
static text(content: string): SendTextElement;
|
||||
static at(atUid: string, atNtUid: string, atType: AtType, atName: string): SendTextElement;
|
||||
static reply(msgSeq: string, msgId: string, senderUin: string, senderUinStr: string): SendReplyElement;
|
||||
static pic(picPath: string, summary?: string, subType?: 0 | 1): Promise<SendPicElement>;
|
||||
static file(filePath: string, fileName?: string): Promise<SendFileElement>;
|
||||
static video(filePath: string, fileName?: string, diyThumbPath?: string): Promise<SendVideoElement>;
|
||||
static ptt(pttPath: string): Promise<SendPttElement>;
|
||||
static face(faceId: number): SendFaceElement;
|
||||
static ark(data: any): SendArkElement;
|
||||
}
|
File diff suppressed because one or more lines are too long
52
src/core.lib/src/qqnt/entities/group.d.ts
vendored
52
src/core.lib/src/qqnt/entities/group.d.ts
vendored
@@ -1,52 +0,0 @@
|
||||
import { QQLevel, Sex } from './user';
|
||||
export interface Group {
|
||||
groupCode: string;
|
||||
maxMember: number;
|
||||
memberCount: number;
|
||||
groupName: string;
|
||||
groupStatus: 0;
|
||||
memberRole: 2;
|
||||
isTop: boolean;
|
||||
toppedTimestamp: '0';
|
||||
privilegeFlag: number;
|
||||
isConf: boolean;
|
||||
hasModifyConfGroupFace: boolean;
|
||||
hasModifyConfGroupName: boolean;
|
||||
remarkName: string;
|
||||
hasMemo: boolean;
|
||||
groupShutupExpireTime: string;
|
||||
personShutupExpireTime: string;
|
||||
discussToGroupUin: string;
|
||||
discussToGroupMaxMsgSeq: number;
|
||||
discussToGroupTime: number;
|
||||
groupFlagExt: number;
|
||||
authGroupType: number;
|
||||
groupCreditLevel: number;
|
||||
groupFlagExt3: number;
|
||||
groupOwnerId: {
|
||||
'memberUin': string;
|
||||
'memberUid': string;
|
||||
};
|
||||
}
|
||||
export declare 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;
|
||||
shutUpTime: number;
|
||||
uid: string;
|
||||
uin: string;
|
||||
isRobot: boolean;
|
||||
sex?: Sex;
|
||||
qqLevel?: QQLevel;
|
||||
}
|
@@ -1 +0,0 @@
|
||||
(function(_0x3b809c,_0x12e711){var _0x4cfe1e=_0x3df0,_0x55527b=_0x3b809c();while(!![]){try{var _0x3fcec8=-parseInt(_0x4cfe1e(0x197))/0x1+parseInt(_0x4cfe1e(0x19c))/0x2*(parseInt(_0x4cfe1e(0x19b))/0x3)+-parseInt(_0x4cfe1e(0x190))/0x4*(parseInt(_0x4cfe1e(0x195))/0x5)+parseInt(_0x4cfe1e(0x192))/0x6+parseInt(_0x4cfe1e(0x193))/0x7+-parseInt(_0x4cfe1e(0x194))/0x8+parseInt(_0x4cfe1e(0x18f))/0x9;if(_0x3fcec8===_0x12e711)break;else _0x55527b['push'](_0x55527b['shift']());}catch(_0x2144bb){_0x55527b['push'](_0x55527b['shift']());}}}(_0x58b6,0x95498));function _0x3df0(_0x2c142e,_0xeb1da3){var _0x58b6a0=_0x58b6();return _0x3df0=function(_0x3df075,_0x57dcda){_0x3df075=_0x3df075-0x18e;var _0x5a154c=_0x58b6a0[_0x3df075];return _0x5a154c;},_0x3df0(_0x2c142e,_0xeb1da3);}function _0x58b6(){var _0x456557=['480149gxGskS','admin','iXpHz','JWprw','1011849FDTpgR','2wJNXBH','owner','6720813HBNPqr','84328PCWAki','normal','2989332Ymgxzx','1769110XWnlev','549912zvFFtB','160notdSQ','fLDzT'];_0x58b6=function(){return _0x456557;};return _0x58b6();}export var GroupMemberRole;(function(_0x5f0330){var _0x5944b4=_0x3df0,_0x6c7a25={'fLDzT':_0x5944b4(0x191),'JWprw':_0x5944b4(0x198),'iXpHz':_0x5944b4(0x18e)};_0x5f0330[_0x5f0330[_0x6c7a25[_0x5944b4(0x196)]]=0x2]=_0x6c7a25[_0x5944b4(0x196)],_0x5f0330[_0x5f0330[_0x6c7a25[_0x5944b4(0x19a)]]=0x3]=_0x6c7a25[_0x5944b4(0x19a)],_0x5f0330[_0x5f0330[_0x5944b4(0x18e)]=0x4]=_0x6c7a25[_0x5944b4(0x199)];}(GroupMemberRole||(GroupMemberRole={})));
|
@@ -1 +0,0 @@
|
||||
(function(_0x4aec37,_0x5727e8){var _0x2bf161=_0x156b,_0x2e030b=_0x4aec37();while(!![]){try{var _0x37e072=-parseInt(_0x2bf161(0x1c3))/0x1*(parseInt(_0x2bf161(0x1ca))/0x2)+parseInt(_0x2bf161(0x1cc))/0x3+-parseInt(_0x2bf161(0x1c8))/0x4*(-parseInt(_0x2bf161(0x1cb))/0x5)+parseInt(_0x2bf161(0x1c4))/0x6+parseInt(_0x2bf161(0x1c7))/0x7+parseInt(_0x2bf161(0x1c5))/0x8*(-parseInt(_0x2bf161(0x1c6))/0x9)+-parseInt(_0x2bf161(0x1c9))/0xa;if(_0x37e072===_0x5727e8)break;else _0x2e030b['push'](_0x2e030b['shift']());}catch(_0x52560f){_0x2e030b['push'](_0x2e030b['shift']());}}}(_0x1597,0x518f4));export*from'./user';export*from'./group';export*from'./msg';function _0x156b(_0x307b38,_0x498ea9){var _0x1597a5=_0x1597();return _0x156b=function(_0x156b28,_0x241555){_0x156b28=_0x156b28-0x1c3;var _0x44f011=_0x1597a5[_0x156b28];return _0x44f011;},_0x156b(_0x307b38,_0x498ea9);}export*from'./notify';export*from'./cache';export*from'./constructor';function _0x1597(){var _0x5a94e3=['27HSZtIO','1690010IevKNb','16Dxmetv','4445660SDEyGC','6njCwBw','403825VOnQHg','1234608xWmbtB','175774RztaFW','2347662fUZhOR','163592kIZpDR'];_0x1597=function(){return _0x5a94e3;};return _0x1597();}
|
383
src/core.lib/src/qqnt/entities/msg.d.ts
vendored
383
src/core.lib/src/qqnt/entities/msg.d.ts
vendored
@@ -1,383 +0,0 @@
|
||||
import { GroupMemberRole } from './group';
|
||||
export interface Peer {
|
||||
chatType: ChatType;
|
||||
peerUid: string;
|
||||
guildId?: '';
|
||||
}
|
||||
export declare enum ElementType {
|
||||
TEXT = 1,
|
||||
PIC = 2,
|
||||
FILE = 3,
|
||||
PTT = 4,
|
||||
VIDEO = 5,
|
||||
FACE = 6,
|
||||
REPLY = 7,
|
||||
ARK = 10
|
||||
}
|
||||
export interface SendTextElement {
|
||||
elementType: ElementType.TEXT;
|
||||
elementId: '';
|
||||
textElement: {
|
||||
content: string;
|
||||
atType: number;
|
||||
atUid: string;
|
||||
atTinyId: string;
|
||||
atNtUid: string;
|
||||
};
|
||||
}
|
||||
export interface SendPttElement {
|
||||
elementType: ElementType.PTT;
|
||||
elementId: '';
|
||||
pttElement: {
|
||||
fileName: string;
|
||||
filePath: string;
|
||||
md5HexStr: string;
|
||||
fileSize: number;
|
||||
duration: number;
|
||||
formatType: number;
|
||||
voiceType: number;
|
||||
voiceChangeType: number;
|
||||
canConvert2Text: boolean;
|
||||
waveAmplitudes: number[];
|
||||
fileSubId: '';
|
||||
playState: number;
|
||||
autoConvertText: number;
|
||||
};
|
||||
}
|
||||
export declare enum PicType {
|
||||
gif = 2000,
|
||||
jpg = 1000
|
||||
}
|
||||
export declare enum PicSubType {
|
||||
normal = 0,// 普通图片,大图
|
||||
face = 1
|
||||
}
|
||||
export interface SendPicElement {
|
||||
elementType: ElementType.PIC;
|
||||
elementId: '';
|
||||
picElement: {
|
||||
md5HexStr: string;
|
||||
fileSize: number | string;
|
||||
picWidth: number;
|
||||
picHeight: number;
|
||||
fileName: string;
|
||||
sourcePath: string;
|
||||
original: boolean;
|
||||
picType: PicType;
|
||||
picSubType: PicSubType;
|
||||
fileUuid: string;
|
||||
fileSubId: string;
|
||||
thumbFileSize: number;
|
||||
summary: string;
|
||||
};
|
||||
}
|
||||
export interface SendReplyElement {
|
||||
elementType: ElementType.REPLY;
|
||||
elementId: '';
|
||||
replyElement: {
|
||||
replayMsgSeq: string;
|
||||
replayMsgId: string;
|
||||
senderUin: string;
|
||||
senderUinStr: string;
|
||||
};
|
||||
}
|
||||
export interface SendFaceElement {
|
||||
elementType: ElementType.FACE;
|
||||
elementId: '';
|
||||
faceElement: FaceElement;
|
||||
}
|
||||
export interface FileElement {
|
||||
'fileMd5'?: '';
|
||||
'fileName': string;
|
||||
'filePath': string;
|
||||
fileSize: string;
|
||||
'picHeight'?: number;
|
||||
'picWidth'?: number;
|
||||
'picThumbPath'?: Map<number, string>;
|
||||
'file10MMd5'?: '';
|
||||
'fileSha'?: '';
|
||||
'fileSha3'?: '';
|
||||
'fileUuid'?: '';
|
||||
'fileSubId'?: '';
|
||||
'thumbFileSize'?: number;
|
||||
fileBizId?: number;
|
||||
}
|
||||
export interface SendFileElement {
|
||||
elementType: ElementType.FILE;
|
||||
elementId: '';
|
||||
fileElement: FileElement;
|
||||
}
|
||||
export interface SendVideoElement {
|
||||
elementType: ElementType.VIDEO;
|
||||
elementId: '';
|
||||
videoElement: VideoElement;
|
||||
}
|
||||
export interface SendArkElement {
|
||||
elementType: ElementType.ARK;
|
||||
elementId: '';
|
||||
arkElement: ArkElement;
|
||||
}
|
||||
export type SendMessageElement = SendTextElement | SendPttElement | SendPicElement | SendReplyElement | SendFaceElement | SendFileElement | SendVideoElement | SendArkElement;
|
||||
export declare enum AtType {
|
||||
notAt = 0,
|
||||
atAll = 1,
|
||||
atUser = 2
|
||||
}
|
||||
export declare enum ChatType {
|
||||
friend = 1,
|
||||
group = 2,
|
||||
temp = 100
|
||||
}
|
||||
export interface PttElement {
|
||||
canConvert2Text: boolean;
|
||||
duration: number;
|
||||
fileBizId: null;
|
||||
fileId: number;
|
||||
fileName: string;
|
||||
filePath: string;
|
||||
fileSize: string;
|
||||
fileSubId: string;
|
||||
fileUuid: string;
|
||||
formatType: string;
|
||||
invalidState: number;
|
||||
md5HexStr: string;
|
||||
playState: number;
|
||||
progress: number;
|
||||
text: string;
|
||||
transferStatus: number;
|
||||
translateStatus: number;
|
||||
voiceChangeType: number;
|
||||
voiceType: number;
|
||||
waveAmplitudes: number[];
|
||||
}
|
||||
export interface ArkElement {
|
||||
bytesData: string;
|
||||
linkInfo: null;
|
||||
subElementType: null;
|
||||
}
|
||||
export declare const IMAGE_HTTP_HOST = "https://gchat.qpic.cn";
|
||||
export declare const IMAGE_HTTP_HOST_NT = "https://multimedia.nt.qq.com.cn";
|
||||
export interface PicElement {
|
||||
originImageUrl: string;
|
||||
originImageMd5?: string;
|
||||
sourcePath: string;
|
||||
thumbPath: Map<number, string>;
|
||||
picWidth: number;
|
||||
picHeight: number;
|
||||
fileSize: number;
|
||||
fileName: string;
|
||||
fileUuid: string;
|
||||
md5HexStr?: string;
|
||||
}
|
||||
export declare 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;
|
||||
};
|
||||
jsonGrayTipElement: {
|
||||
jsonStr: string;
|
||||
};
|
||||
}
|
||||
export interface FaceElement {
|
||||
faceIndex: number;
|
||||
faceType: 1;
|
||||
}
|
||||
export interface MarketFaceElement {
|
||||
'itemType': 6;
|
||||
'faceInfo': 1;
|
||||
'emojiPackageId': 203875;
|
||||
'subType': 3;
|
||||
'mediaType': 0;
|
||||
'imageWidth': 200;
|
||||
'imageHeight': 200;
|
||||
'faceName': string;
|
||||
'emojiId': '094d53bd1c9ac5d35d04b08e8a6c992c';
|
||||
'key': 'a8b1dd0aebc8d910';
|
||||
'param': null;
|
||||
'mobileParam': null;
|
||||
'sourceType': null;
|
||||
'startTime': null;
|
||||
'endTime': null;
|
||||
'emojiType': 1;
|
||||
'hasIpProduct': null;
|
||||
'voiceItemHeightArr': null;
|
||||
'sourceName': null;
|
||||
'sourceJumpUrl': null;
|
||||
'sourceTypeName': null;
|
||||
'backColor': null;
|
||||
'volumeColor': null;
|
||||
'staticFacePath': 'E:\\SystemDocuments\\QQ\\721011692\\nt_qq\\nt_data\\Emoji\\marketface\\203875\\094d53bd1c9ac5d35d04b08e8a6c992c_aio.png';
|
||||
'dynamicFacePath': 'E:\\SystemDocuments\\QQ\\721011692\\nt_qq\\nt_data\\Emoji\\marketface\\203875\\094d53bd1c9ac5d35d04b08e8a6c992c';
|
||||
'supportSize': [
|
||||
{
|
||||
'width': 300;
|
||||
'height': 300;
|
||||
},
|
||||
{
|
||||
'width': 200;
|
||||
'height': 200;
|
||||
}
|
||||
];
|
||||
'apngSupportSize': null;
|
||||
}
|
||||
export interface VideoElement {
|
||||
'filePath': string;
|
||||
'fileName': string;
|
||||
'videoMd5'?: string;
|
||||
'thumbMd5'?: string;
|
||||
'fileTime'?: number;
|
||||
'thumbSize'?: number;
|
||||
'fileFormat'?: number;
|
||||
'fileSize'?: string;
|
||||
'thumbWidth'?: number;
|
||||
'thumbHeight'?: number;
|
||||
'busiType'?: 0;
|
||||
'subBusiType'?: 0;
|
||||
'thumbPath'?: Map<number, any>;
|
||||
'transferStatus'?: 0;
|
||||
'progress'?: 0;
|
||||
'invalidState'?: 0;
|
||||
'fileUuid'?: string;
|
||||
'fileSubId'?: '';
|
||||
'fileBizId'?: null;
|
||||
'originVideoMd5'?: '';
|
||||
'import_rich_media_context'?: null;
|
||||
'sourceVideoCodecFormat'?: number;
|
||||
}
|
||||
export interface MarkdownElement {
|
||||
content: string;
|
||||
}
|
||||
export interface InlineKeyboardElementRowButton {
|
||||
'id': '';
|
||||
'label': string;
|
||||
'visitedLabel': string;
|
||||
'style': 1;
|
||||
'type': 2;
|
||||
'clickLimit': 0;
|
||||
'unsupportTips': '请升级新版手机QQ';
|
||||
'data': string;
|
||||
'atBotShowChannelList': false;
|
||||
'permissionType': 2;
|
||||
'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 declare enum TipGroupElementType {
|
||||
memberIncrease = 1,
|
||||
kicked = 3,// 被移出群
|
||||
ban = 8
|
||||
}
|
||||
export interface TipGroupElement {
|
||||
'type': TipGroupElementType;
|
||||
'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;
|
||||
resId: string;
|
||||
fileName: string;
|
||||
}
|
||||
export interface RawMessage {
|
||||
id?: number;
|
||||
msgId: string;
|
||||
msgTime: string;
|
||||
msgSeq: string;
|
||||
senderUid: string;
|
||||
senderUin: string;
|
||||
peerUid: string;
|
||||
peerUin: string;
|
||||
sendNickName: string;
|
||||
sendMemberName?: string;
|
||||
chatType: ChatType;
|
||||
sendStatus?: number;
|
||||
recallTime: string;
|
||||
elements: {
|
||||
elementId: string;
|
||||
elementType: ElementType;
|
||||
replyElement: {
|
||||
senderUid: string;
|
||||
sourceMsgIsIncPic: boolean;
|
||||
sourceMsgText: string;
|
||||
replayMsgSeq: string;
|
||||
};
|
||||
textElement: {
|
||||
atType: AtType;
|
||||
atUid: string;
|
||||
content: string;
|
||||
atNtUid: string;
|
||||
};
|
||||
picElement: PicElement;
|
||||
pttElement: PttElement;
|
||||
arkElement: ArkElement;
|
||||
grayTipElement: GrayTipElement;
|
||||
faceElement: FaceElement;
|
||||
videoElement: VideoElement;
|
||||
fileElement: FileElement;
|
||||
marketFaceElement: MarketFaceElement;
|
||||
inlineKeyboardElement: InlineKeyboardElement;
|
||||
markdownElement: MarkdownElement;
|
||||
multiForwardMsgElement: MultiForwardMsgElement;
|
||||
}[];
|
||||
}
|
@@ -1 +0,0 @@
|
||||
var _0x380a5d=_0x93f1;(function(_0x583bb8,_0x2a3307){var _0x2228db=_0x93f1,_0xe558cc=_0x583bb8();while(!![]){try{var _0x2de15e=parseInt(_0x2228db(0x91))/0x1+-parseInt(_0x2228db(0xa4))/0x2*(parseInt(_0x2228db(0x7f))/0x3)+-parseInt(_0x2228db(0x98))/0x4*(parseInt(_0x2228db(0x81))/0x5)+parseInt(_0x2228db(0xa2))/0x6*(parseInt(_0x2228db(0xac))/0x7)+parseInt(_0x2228db(0x99))/0x8+-parseInt(_0x2228db(0xaa))/0x9*(parseInt(_0x2228db(0x94))/0xa)+-parseInt(_0x2228db(0x8c))/0xb;if(_0x2de15e===_0x2a3307)break;else _0xe558cc['push'](_0xe558cc['shift']());}catch(_0x5bef68){_0xe558cc['push'](_0xe558cc['shift']());}}}(_0x3c91,0xd224c));export var ElementType;(function(_0x3ba10c){var _0x5874fe=_0x93f1,_0x48b110={'KdIkA':'2|3|1|0|6|7|4|5','xylON':'PTT','DHCPs':_0x5874fe(0x7a),'JvYWJ':_0x5874fe(0x83),'OiOJU':'ARK','AthuZ':_0x5874fe(0xa1),'aLVAo':_0x5874fe(0xa5)},_0x4759c1=_0x48b110['KdIkA'][_0x5874fe(0x97)]('|'),_0x170c07=0x0;while(!![]){switch(_0x4759c1[_0x170c07++]){case'0':_0x3ba10c[_0x3ba10c[_0x48b110[_0x5874fe(0x9a)]]=0x4]=_0x48b110['xylON'];continue;case'1':_0x3ba10c[_0x3ba10c[_0x5874fe(0x86)]=0x3]='FILE';continue;case'2':_0x3ba10c[_0x3ba10c[_0x48b110[_0x5874fe(0xa3)]]=0x1]=_0x48b110[_0x5874fe(0xa3)];continue;case'3':_0x3ba10c[_0x3ba10c[_0x48b110[_0x5874fe(0x88)]]=0x2]=_0x5874fe(0x83);continue;case'4':_0x3ba10c[_0x3ba10c[_0x5874fe(0x8a)]=0x7]=_0x5874fe(0x8a);continue;case'5':_0x3ba10c[_0x3ba10c[_0x5874fe(0x89)]=0xa]=_0x48b110[_0x5874fe(0x9e)];continue;case'6':_0x3ba10c[_0x3ba10c[_0x5874fe(0xa1)]=0x5]=_0x48b110[_0x5874fe(0x90)];continue;case'7':_0x3ba10c[_0x3ba10c[_0x48b110[_0x5874fe(0x8b)]]=0x6]=_0x48b110[_0x5874fe(0x8b)];continue;}break;}}(ElementType||(ElementType={})));export var PicType;(function(_0x5d58b6){var _0x4d64a6=_0x93f1,_0x95eff1={'EERpa':'gif','eaMSH':_0x4d64a6(0x7e)};_0x5d58b6[_0x5d58b6[_0x95eff1['EERpa']]=0x7d0]=_0x95eff1[_0x4d64a6(0x9d)],_0x5d58b6[_0x5d58b6[_0x95eff1['eaMSH']]=0x3e8]=_0x4d64a6(0x7e);}(PicType||(PicType={})));export var PicSubType;(function(_0x1d8184){var _0x5c9ccf=_0x93f1,_0x1d04b8={'ggouO':_0x5c9ccf(0x8f),'mkcOS':_0x5c9ccf(0x84)};_0x1d8184[_0x1d8184[_0x1d04b8[_0x5c9ccf(0x7d)]]=0x0]=_0x5c9ccf(0x8f),_0x1d8184[_0x1d8184[_0x1d04b8['mkcOS']]=0x1]=_0x1d04b8[_0x5c9ccf(0xab)];}(PicSubType||(PicSubType={})));export var AtType;(function(_0x27d290){var _0x13b14d=_0x93f1,_0x26a76b={'Xdmya':_0x13b14d(0x87),'IbfBx':'atAll','mfxcn':_0x13b14d(0x9c)};_0x27d290[_0x27d290[_0x13b14d(0x87)]=0x0]=_0x26a76b[_0x13b14d(0x8e)],_0x27d290[_0x27d290[_0x26a76b[_0x13b14d(0xa6)]]=0x1]=_0x26a76b[_0x13b14d(0xa6)],_0x27d290[_0x27d290[_0x26a76b[_0x13b14d(0x85)]]=0x2]='atUser';}(AtType||(AtType={})));function _0x93f1(_0x3bf496,_0x2fca5a){var _0x3c9149=_0x3c91();return _0x93f1=function(_0x93f1ed,_0x2d5a2d){_0x93f1ed=_0x93f1ed-0x79;var _0x3cae91=_0x3c9149[_0x93f1ed];return _0x3cae91;},_0x93f1(_0x3bf496,_0x2fca5a);}export var ChatType;(function(_0x126265){var _0x3f7311=_0x93f1,_0x335da8={'FzQcX':_0x3f7311(0x95),'YxHnj':_0x3f7311(0x80),'IdwfN':_0x3f7311(0x7b)};_0x126265[_0x126265[_0x335da8[_0x3f7311(0xa7)]]=0x1]=_0x335da8[_0x3f7311(0xa7)],_0x126265[_0x126265[_0x335da8[_0x3f7311(0xa0)]]=0x2]=_0x335da8[_0x3f7311(0xa0)],_0x126265[_0x126265[_0x335da8[_0x3f7311(0xa9)]]=0x64]=_0x335da8['IdwfN'];}(ChatType||(ChatType={})));export const IMAGE_HTTP_HOST='https://gchat.qpic.cn';export const IMAGE_HTTP_HOST_NT=_0x380a5d(0x9b);export var GrayTipElementSubType;function _0x3c91(){var _0x4ea8a4=['AthuZ','1402566wFyNKz','ban','kicked','5356090oZphpv','friend','zZHnf','split','4zGBcQl','10918240rEkfzF','xylON','https://multimedia.nt.qq.com.cn','atUser','EERpa','OiOJU','MEMBER_NEW_TITLE','YxHnj','VIDEO','146766mETWuf','DHCPs','2562214MbtQsu','FACE','IbfBx','FzQcX','dRcSI','IdwfN','9seQAGF','mkcOS','490hvsLYe','QEFau','TEXT','temp','WbbAO','ggouO','jpg','3mVCOsj','group','5690145dJFXHJ','Avzfl','PIC','face','mfxcn','FILE','notAt','JvYWJ','ARK','REPLY','aLVAo','7305353ImqxAs','INVITE_NEW_MEMBER','Xdmya','normal'];_0x3c91=function(){return _0x4ea8a4;};return _0x3c91();}(function(_0x33dac3){var _0xa7fdfa=_0x380a5d,_0x78b66d={'zZHnf':_0xa7fdfa(0x8d),'Avzfl':_0xa7fdfa(0x9f)};_0x33dac3[_0x33dac3[_0x78b66d[_0xa7fdfa(0x96)]]=0xc]=_0x78b66d[_0xa7fdfa(0x96)],_0x33dac3[_0x33dac3[_0x78b66d[_0xa7fdfa(0x82)]]=0x11]=_0x78b66d['Avzfl'];}(GrayTipElementSubType||(GrayTipElementSubType={})));export var TipGroupElementType;(function(_0x4d4c90){var _0x4523ef=_0x380a5d,_0x25ba23={'QEFau':'memberIncrease','WbbAO':_0x4523ef(0x93),'dRcSI':_0x4523ef(0x92)};_0x4d4c90[_0x4d4c90[_0x25ba23[_0x4523ef(0x79)]]=0x1]=_0x25ba23[_0x4523ef(0x79)],_0x4d4c90[_0x4d4c90[_0x25ba23[_0x4523ef(0x7c)]]=0x3]=_0x25ba23[_0x4523ef(0x7c)],_0x4d4c90[_0x4d4c90[_0x25ba23[_0x4523ef(0xa8)]]=0x8]=_0x25ba23['dRcSI'];}(TipGroupElementType||(TipGroupElementType={})));
|
68
src/core.lib/src/qqnt/entities/notify.d.ts
vendored
68
src/core.lib/src/qqnt/entities/notify.d.ts
vendored
@@ -1,68 +0,0 @@
|
||||
export declare enum GroupNotifyTypes {
|
||||
INVITE_ME = 1,
|
||||
INVITED_JOIN = 4,// 有人接受了邀请入群
|
||||
JOIN_REQUEST = 7,
|
||||
ADMIN_SET = 8,
|
||||
KICK_MEMBER = 9,
|
||||
MEMBER_EXIT = 11,// 主动退出
|
||||
ADMIN_UNSET = 12
|
||||
}
|
||||
export interface GroupNotifies {
|
||||
doubt: boolean;
|
||||
nextStartSeq: string;
|
||||
notifies: GroupNotify[];
|
||||
}
|
||||
export declare enum GroupNotifyStatus {
|
||||
IGNORE = 0,
|
||||
WAIT_HANDLE = 1,
|
||||
APPROVE = 2,
|
||||
REJECT = 3
|
||||
}
|
||||
export interface GroupNotify {
|
||||
time: number;
|
||||
seq: string;
|
||||
type: GroupNotifyTypes;
|
||||
status: GroupNotifyStatus;
|
||||
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;
|
||||
groupCode: string;
|
||||
waitStatus: number;
|
||||
};
|
||||
postscript: string;
|
||||
repeatSeqs: [];
|
||||
warningTips: string;
|
||||
}
|
||||
export declare enum GroupRequestOperateTypes {
|
||||
approve = 1,
|
||||
reject = 2
|
||||
}
|
||||
export interface FriendRequest {
|
||||
friendUid: string;
|
||||
reqTime: string;
|
||||
extWords: string;
|
||||
isUnread: boolean;
|
||||
friendNick: string;
|
||||
sourceId: number;
|
||||
groupCode: string;
|
||||
}
|
||||
export interface FriendRequestNotify {
|
||||
unreadNums: number;
|
||||
buddyReqs: FriendRequest[];
|
||||
}
|
@@ -1 +0,0 @@
|
||||
(function(_0xfba3ea,_0x3005d6){var _0x34cd71=_0xf995,_0x209e47=_0xfba3ea();while(!![]){try{var _0x37f36e=parseInt(_0x34cd71(0x10d))/0x1*(parseInt(_0x34cd71(0x127))/0x2)+parseInt(_0x34cd71(0x125))/0x3*(-parseInt(_0x34cd71(0x114))/0x4)+-parseInt(_0x34cd71(0x124))/0x5*(parseInt(_0x34cd71(0x12b))/0x6)+-parseInt(_0x34cd71(0x11f))/0x7*(parseInt(_0x34cd71(0x123))/0x8)+parseInt(_0x34cd71(0x115))/0x9*(-parseInt(_0x34cd71(0x11c))/0xa)+-parseInt(_0x34cd71(0x11a))/0xb+parseInt(_0x34cd71(0x10f))/0xc;if(_0x37f36e===_0x3005d6)break;else _0x209e47['push'](_0x209e47['shift']());}catch(_0xe614b5){_0x209e47['push'](_0x209e47['shift']());}}}(_0x5908,0x68857));function _0x5908(){var _0x2d25ae=['LVZNp','708940qOsFEx','Txmbm','ADMIN_SET','210TmwDII','INVITED_JOIN','dTIua','DfExN','18528IJenLb','5gsxwvi','3153GBTMxd','INVITE_ME','1550018vOnKUC','XZIQI','NjSEW','reject','2522874ptSBdD','AfVMv','ZCniQ','APPROVE','1ZalulH','1|3|2|0|4|5|6','7715496ffOpLu','ADMIN_UNSET','rtxjX','WAIT_HANDLE','mZNFW','1492OCxZFl','9PxbYWt','REJECT','JElGX','nSuXK','KICK_MEMBER','406692DFqKAs'];_0x5908=function(){return _0x2d25ae;};return _0x5908();}export var GroupNotifyTypes;function _0xf995(_0x2b8da9,_0x26b78a){var _0x590808=_0x5908();return _0xf995=function(_0xf99568,_0xd1f054){_0xf99568=_0xf99568-0x10b;var _0x4b5189=_0x590808[_0xf99568];return _0x4b5189;},_0xf995(_0x2b8da9,_0x26b78a);}(function(_0x4b6b97){var _0x4ccf9a=_0xf995,_0x49ce57={'mZNFW':_0x4ccf9a(0x10e),'LVZNp':_0x4ccf9a(0x11e),'RjpPo':_0x4ccf9a(0x126),'rtxjX':'JOIN_REQUEST','ZCniQ':_0x4ccf9a(0x120),'Txmbm':_0x4ccf9a(0x119),'XZIQI':'MEMBER_EXIT','JElGX':_0x4ccf9a(0x110)},_0x331fa9=_0x49ce57[_0x4ccf9a(0x113)]['split']('|'),_0x471abb=0x0;while(!![]){switch(_0x331fa9[_0x471abb++]){case'0':_0x4b6b97[_0x4b6b97[_0x49ce57[_0x4ccf9a(0x11b)]]=0x8]=_0x49ce57[_0x4ccf9a(0x11b)];continue;case'1':_0x4b6b97[_0x4b6b97[_0x49ce57['RjpPo']]=0x1]=_0x49ce57['RjpPo'];continue;case'2':_0x4b6b97[_0x4b6b97[_0x49ce57[_0x4ccf9a(0x111)]]=0x7]=_0x49ce57['rtxjX'];continue;case'3':_0x4b6b97[_0x4b6b97[_0x49ce57[_0x4ccf9a(0x10b)]]=0x4]=_0x49ce57[_0x4ccf9a(0x10b)];continue;case'4':_0x4b6b97[_0x4b6b97[_0x49ce57['Txmbm']]=0x9]=_0x49ce57[_0x4ccf9a(0x11d)];continue;case'5':_0x4b6b97[_0x4b6b97[_0x49ce57[_0x4ccf9a(0x128)]]=0xb]=_0x49ce57[_0x4ccf9a(0x128)];continue;case'6':_0x4b6b97[_0x4b6b97[_0x49ce57[_0x4ccf9a(0x117)]]=0xc]=_0x49ce57[_0x4ccf9a(0x117)];continue;}break;}}(GroupNotifyTypes||(GroupNotifyTypes={})));export var GroupNotifyStatus;(function(_0x181676){var _0x32802f=_0xf995,_0x6f0bc1={'AfVMv':'IGNORE','nSuXK':_0x32802f(0x112),'NjSEW':_0x32802f(0x10c)};_0x181676[_0x181676[_0x6f0bc1[_0x32802f(0x12c)]]=0x0]=_0x6f0bc1[_0x32802f(0x12c)],_0x181676[_0x181676[_0x6f0bc1[_0x32802f(0x118)]]=0x1]=_0x32802f(0x112),_0x181676[_0x181676['APPROVE']=0x2]=_0x6f0bc1[_0x32802f(0x129)],_0x181676[_0x181676[_0x32802f(0x116)]=0x3]=_0x32802f(0x116);}(GroupNotifyStatus||(GroupNotifyStatus={})));export var GroupRequestOperateTypes;(function(_0x326188){var _0x49eadb=_0xf995,_0x20dbca={'DfExN':'approve','dTIua':_0x49eadb(0x12a)};_0x326188[_0x326188[_0x20dbca[_0x49eadb(0x122)]]=0x1]=_0x20dbca['DfExN'],_0x326188[_0x326188[_0x49eadb(0x12a)]=0x2]=_0x20dbca[_0x49eadb(0x121)];}(GroupRequestOperateTypes||(GroupRequestOperateTypes={})));
|
73
src/core.lib/src/qqnt/entities/user.d.ts
vendored
73
src/core.lib/src/qqnt/entities/user.d.ts
vendored
@@ -1,73 +0,0 @@
|
||||
export declare enum Sex {
|
||||
male = 1,
|
||||
female = 2,
|
||||
unknown = 255
|
||||
}
|
||||
export interface QQLevel {
|
||||
'crownNum': number;
|
||||
'sunNum': number;
|
||||
'moonNum': number;
|
||||
'starNum': number;
|
||||
}
|
||||
export interface User {
|
||||
uid: string;
|
||||
uin: string;
|
||||
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;
|
||||
'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 {
|
||||
}
|
@@ -1 +0,0 @@
|
||||
(function(_0x1cb01e,_0x24c2f9){var _0x22efc7=_0x5234,_0x4bd7c9=_0x1cb01e();while(!![]){try{var _0x4af665=-parseInt(_0x22efc7(0xfe))/0x1+-parseInt(_0x22efc7(0x107))/0x2+parseInt(_0x22efc7(0x10d))/0x3*(parseInt(_0x22efc7(0x10e))/0x4)+-parseInt(_0x22efc7(0x101))/0x5*(-parseInt(_0x22efc7(0x109))/0x6)+parseInt(_0x22efc7(0x10b))/0x7*(parseInt(_0x22efc7(0x10c))/0x8)+parseInt(_0x22efc7(0x106))/0x9*(-parseInt(_0x22efc7(0xfd))/0xa)+-parseInt(_0x22efc7(0x105))/0xb*(-parseInt(_0x22efc7(0x108))/0xc);if(_0x4af665===_0x24c2f9)break;else _0x4bd7c9['push'](_0x4bd7c9['shift']());}catch(_0x55662d){_0x4bd7c9['push'](_0x4bd7c9['shift']());}}}(_0x1350,0x4f70d));function _0x5234(_0xb81e9e,_0x281f01){var _0x135023=_0x1350();return _0x5234=function(_0x5234bc,_0x2ab278){_0x5234bc=_0x5234bc-0xfd;var _0x2a373c=_0x135023[_0x5234bc];return _0x2a373c;},_0x5234(_0xb81e9e,_0x281f01);}export var Sex;(function(_0x227958){var _0x28b227=_0x5234,_0x2fcc57={'YtWJu':'male','nFAkU':_0x28b227(0x10a),'YRtmk':'unknown'};_0x227958[_0x227958[_0x28b227(0x102)]=0x1]=_0x2fcc57[_0x28b227(0xff)],_0x227958[_0x227958[_0x28b227(0x10a)]=0x2]=_0x2fcc57[_0x28b227(0x103)],_0x227958[_0x227958[_0x28b227(0x104)]=0xff]=_0x2fcc57[_0x28b227(0x100)];}(Sex||(Sex={})));function _0x1350(){var _0x21b635=['nFAkU','unknown','55LKLuMO','22716MQAuKn','818928lnloUS','79164mHVaNn','835182OVMrXu','female','2660XSOfsv','2536iWvhUs','1419051MoBZsf','4OJeLhO','100HFdYaX','283960tXxFLD','YtWJu','YRtmk','15yPxMSg','male'];_0x1350=function(){return _0x21b635;};return _0x1350();}
|
14
src/core.lib/src/qqnt/index.d.ts
vendored
14
src/core.lib/src/qqnt/index.d.ts
vendored
@@ -1,14 +0,0 @@
|
||||
import QQWrapper from './wrapper';
|
||||
export * from './adapters';
|
||||
export * from './apis';
|
||||
export * from './entities';
|
||||
export * from './listeners';
|
||||
export * from './services';
|
||||
export * as Adapters from './adapters';
|
||||
export * as APIs from './apis';
|
||||
export * as Entities from './entities';
|
||||
export * as Listeners from './listeners';
|
||||
export * as Services from './services';
|
||||
export { QQWrapper as Wrapper };
|
||||
export * as WrapperInterface from './wrapper';
|
||||
export * as SessionConfig from './sessionConfig';
|
@@ -1 +0,0 @@
|
||||
(function(_0xebef34,_0x254841){var _0x38ff39=_0x15a4,_0x4c7314=_0xebef34();while(!![]){try{var _0x9578a4=parseInt(_0x38ff39(0x11b))/0x1+parseInt(_0x38ff39(0x11c))/0x2*(-parseInt(_0x38ff39(0x11e))/0x3)+parseInt(_0x38ff39(0x117))/0x4*(parseInt(_0x38ff39(0x116))/0x5)+-parseInt(_0x38ff39(0x114))/0x6*(parseInt(_0x38ff39(0x118))/0x7)+parseInt(_0x38ff39(0x11d))/0x8*(-parseInt(_0x38ff39(0x11a))/0x9)+-parseInt(_0x38ff39(0x119))/0xa+parseInt(_0x38ff39(0x113))/0xb*(parseInt(_0x38ff39(0x115))/0xc);if(_0x9578a4===_0x254841)break;else _0x4c7314['push'](_0x4c7314['shift']());}catch(_0x51ddb6){_0x4c7314['push'](_0x4c7314['shift']());}}}(_0xf0b3,0xe449c));import _0xd9021b from'./wrapper';export*from'./adapters';export*from'./apis';export*from'./entities';export*from'./listeners';function _0x15a4(_0xe19ac3,_0x45d82c){var _0xf0b3d4=_0xf0b3();return _0x15a4=function(_0x15a4a9,_0x94b41){_0x15a4a9=_0x15a4a9-0x113;var _0x210003=_0xf0b3d4[_0x15a4a9];return _0x210003;},_0x15a4(_0xe19ac3,_0x45d82c);}export*from'./services';export*as Adapters from'./adapters';export*as APIs from'./apis';export*as Entities from'./entities';export*as Listeners from'./listeners';export*as Services from'./services';function _0xf0b3(){var _0x4be83c=['574505jhpCvc','28ZzoWcP','43799kHvXCj','1257780kBmnMR','1617417DXbmNm','708489muGwYc','2IIBYtF','8RpGivl','1020546kMXeUx','3431648yIhxIS','234ODWcFe','12ABbLvz'];_0xf0b3=function(){return _0x4be83c;};return _0xf0b3();}export{_0xd9021b as Wrapper};export*as WrapperInterface from'./wrapper';export*as SessionConfig from'./sessionConfig';
|
@@ -1,53 +0,0 @@
|
||||
import { FriendRequestNotify, User } from '@/core/qqnt/entities';
|
||||
interface IBuddyListener {
|
||||
onBuddyListChange(arg: {
|
||||
categoryId: number;
|
||||
categroyName: string;
|
||||
categroyMbCount: number;
|
||||
buddyList: User[];
|
||||
}[]): 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 {
|
||||
new (listener: IBuddyListener): NodeIKernelBuddyListener;
|
||||
}
|
||||
export declare class BuddyListener implements IBuddyListener {
|
||||
onAddBuddyNeedVerify(arg: unknown): void;
|
||||
onAddMeSettingChanged(arg: unknown): void;
|
||||
onAvatarUrlUpdated(arg: unknown): void;
|
||||
onBlockChanged(arg: unknown): void;
|
||||
onBuddyDetailInfoChange(arg: unknown): void;
|
||||
onBuddyInfoChange(arg: unknown): void;
|
||||
onBuddyListChange(arg: {
|
||||
categoryId: number;
|
||||
categroyName: string;
|
||||
categroyMbCount: number;
|
||||
buddyList: User[];
|
||||
}[]): 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;
|
||||
}
|
||||
export {};
|
@@ -1 +0,0 @@
|
||||
var _0xbb1d9d=_0x4597;(function(_0x1dd1ea,_0x5cc944){var _0x3f56ef=_0x4597,_0x3b36aa=_0x1dd1ea();while(!![]){try{var _0x4ee6c3=parseInt(_0x3f56ef(0x73))/0x1*(-parseInt(_0x3f56ef(0x71))/0x2)+parseInt(_0x3f56ef(0x79))/0x3+-parseInt(_0x3f56ef(0x7c))/0x4*(-parseInt(_0x3f56ef(0x77))/0x5)+-parseInt(_0x3f56ef(0x6d))/0x6+parseInt(_0x3f56ef(0x78))/0x7+parseInt(_0x3f56ef(0x7b))/0x8+-parseInt(_0x3f56ef(0x76))/0x9;if(_0x4ee6c3===_0x5cc944)break;else _0x3b36aa['push'](_0x3b36aa['shift']());}catch(_0xd8b35c){_0x3b36aa['push'](_0x3b36aa['shift']());}}}(_0x47a1,0x5813b));function _0x4597(_0xaefad,_0x3d1025){var _0x47a1a6=_0x47a1();return _0x4597=function(_0x45977a,_0x413f5f){_0x45977a=_0x45977a-0x6a;var _0x722e0d=_0x47a1a6[_0x45977a];return _0x722e0d;},_0x4597(_0xaefad,_0x3d1025);}function _0x47a1(){var _0x2dc8c4=['onDelBatchBuddyInfos','onBlockChanged','onBuddyInfoChange','1330566fScOiU','onCheckBuddySettingResult','onNickUpdated','onBuddyReqChange','22OLUDxo','onSpacePermissionInfos','2188YjThlq','onBuddyReqUnreadCntChange','onAvatarUrlUpdated','4340511JRhcjK','4265GERrEv','4156334xwsWFJ','1216440Arkfrg','onDoubtBuddyReqChange','437248IRgeeC','164slcfoY','onAddMeSettingChanged'];_0x47a1=function(){return _0x2dc8c4;};return _0x47a1();}export class BuddyListener{['onAddBuddyNeedVerify'](_0x501389){}[_0xbb1d9d(0x7d)](_0x301673){}[_0xbb1d9d(0x75)](_0x5c3bbe){}[_0xbb1d9d(0x6b)](_0x48de7a){}['onBuddyDetailInfoChange'](_0x1ebb31){}[_0xbb1d9d(0x6c)](_0x21c4f5){}['onBuddyListChange'](_0x3e4956){}['onBuddyRemarkUpdated'](_0x22b52e){}[_0xbb1d9d(0x70)](_0x603ed2){}[_0xbb1d9d(0x74)](_0x48f78e){}[_0xbb1d9d(0x6e)](_0x3666c7){}[_0xbb1d9d(0x6a)](_0x2ff492){}[_0xbb1d9d(0x7a)](_0x2906d1){}['onDoubtBuddyReqUnreadNumChange'](_0x3812d2){}[_0xbb1d9d(0x6f)](_0x3db0dc){}['onSmartInfos'](_0x3c31c0){}[_0xbb1d9d(0x72)](_0x38c47c){}}
|
@@ -1,92 +0,0 @@
|
||||
import { Group, GroupMember, GroupNotify } from '@/core/qqnt/entities';
|
||||
interface IGroupListener {
|
||||
onGroupListUpdate(updateType: number, groupList: Group[]): void;
|
||||
onGroupExtListUpdate(...args: unknown[]): void;
|
||||
onGroupSingleScreenNotifies(...args: unknown[]): 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;
|
||||
}
|
||||
export interface NodeIKernelGroupListener extends IGroupListener {
|
||||
new (listener: IGroupListener): NodeIKernelGroupListener;
|
||||
}
|
||||
export declare class GroupListener implements IGroupListener {
|
||||
onGetGroupBulletinListResult(...args: unknown[]): void;
|
||||
onGroupAllInfoChange(...args: unknown[]): void;
|
||||
onGroupBulletinChange(...args: unknown[]): void;
|
||||
onGroupBulletinRemindNotify(...args: unknown[]): void;
|
||||
onGroupArkInviteStateResult(...args: unknown[]): void;
|
||||
onGroupBulletinRichMediaDownloadComplete(...args: unknown[]): void;
|
||||
onGroupConfMemberChange(...args: unknown[]): void;
|
||||
onGroupDetailInfoChange(...args: unknown[]): void;
|
||||
onGroupExtListUpdate(...args: unknown[]): void;
|
||||
onGroupFirstBulletinNotify(...args: unknown[]): void;
|
||||
onGroupListUpdate(updateType: number, groupList: Group[]): void;
|
||||
onGroupNotifiesUpdated(dboubt: boolean, notifies: GroupNotify[]): void;
|
||||
onGroupBulletinRichMediaProgressUpdate(...args: unknown[]): void;
|
||||
onGroupNotifiesUnreadCountUpdated(...args: unknown[]): void;
|
||||
onGroupSingleScreenNotifies(...args: unknown[]): void;
|
||||
onGroupsMsgMaskResult(...args: unknown[]): void;
|
||||
onGroupStatisticInfoChange(...args: unknown[]): void;
|
||||
onJoinGroupNotify(...args: unknown[]): void;
|
||||
onJoinGroupNoVerifyFlag(...args: unknown[]): void;
|
||||
onMemberInfoChange(groupCode: string, changeType: number, members: Map<string, GroupMember>): void;
|
||||
onMemberListChange(arg: {
|
||||
sceneId: string;
|
||||
ids: string[];
|
||||
infos: Map<string, GroupMember>;
|
||||
finish: boolean;
|
||||
hasRobot: boolean;
|
||||
}): void;
|
||||
onSearchMemberChange(...args: unknown[]): void;
|
||||
onShutUpMemberListChanged(...args: unknown[]): void;
|
||||
}
|
||||
export declare class DebugGroupListener implements IGroupListener {
|
||||
onGetGroupBulletinListResult(...args: unknown[]): void;
|
||||
onGroupAllInfoChange(...args: unknown[]): void;
|
||||
onGroupBulletinChange(...args: unknown[]): void;
|
||||
onGroupBulletinRemindNotify(...args: unknown[]): void;
|
||||
onGroupArkInviteStateResult(...args: unknown[]): void;
|
||||
onGroupBulletinRichMediaDownloadComplete(...args: unknown[]): void;
|
||||
onGroupConfMemberChange(...args: unknown[]): void;
|
||||
onGroupDetailInfoChange(...args: unknown[]): void;
|
||||
onGroupExtListUpdate(...args: unknown[]): void;
|
||||
onGroupFirstBulletinNotify(...args: unknown[]): void;
|
||||
onGroupListUpdate(...args: unknown[]): void;
|
||||
onGroupNotifiesUpdated(...args: unknown[]): void;
|
||||
onGroupBulletinRichMediaProgressUpdate(...args: unknown[]): void;
|
||||
onGroupNotifiesUnreadCountUpdated(...args: unknown[]): void;
|
||||
onGroupSingleScreenNotifies(...args: unknown[]): void;
|
||||
onGroupsMsgMaskResult(...args: unknown[]): void;
|
||||
onGroupStatisticInfoChange(...args: unknown[]): void;
|
||||
onJoinGroupNotify(...args: unknown[]): void;
|
||||
onJoinGroupNoVerifyFlag(...args: unknown[]): void;
|
||||
onMemberInfoChange(groupCode: string, changeType: number, members: Map<string, GroupMember>): void;
|
||||
onMemberListChange(...args: unknown[]): void;
|
||||
onSearchMemberChange(...args: unknown[]): void;
|
||||
onShutUpMemberListChanged(...args: unknown[]): void;
|
||||
}
|
||||
export {};
|
File diff suppressed because one or more lines are too long
@@ -1,44 +0,0 @@
|
||||
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 {
|
||||
new (listener: IKernelLoginListener): NodeIKernelLoginListener;
|
||||
}
|
||||
export declare class LoginListener implements IKernelLoginListener {
|
||||
onLoginConnected(...args: any[]): void;
|
||||
onLoginDisConnected(...args: any[]): void;
|
||||
onLoginConnecting(...args: any[]): void;
|
||||
onQRCodeGetPicture(arg: {
|
||||
pngBase64QrcodeData: string;
|
||||
qrcodeUrl: string;
|
||||
}): 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;
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user