Compare commits
867 Commits
Author | SHA1 | Date | |
---|---|---|---|
9c525141f6 | |||
3d218a0e8d | |||
db385f597b | |||
98a9d6413a | |||
a77f97fd4b | |||
e6ea1b700f | |||
596f4d06ea | |||
8e3d3b3b5d | |||
a34ad87e01 | |||
6c4c7f34cd | |||
0c859957b4 | |||
fbb9ed82ee | |||
b469c9420e | |||
aa4d930cc4 | |||
76be748160 | |||
a9dd37b8a5 | |||
5900d621f2 | |||
7442a3651b | |||
413f438689 | |||
1fc4999b09 | |||
975446a096 | |||
98cdd2f4b8 | |||
c0beec0429 | |||
614d78b3fa | |||
24b0f345e4 | |||
0ae10aa1b2 | |||
9fe7478840 | |||
efca0bc7b3 | |||
50c5e99b98 | |||
7415efcc90 | |||
5bb4584e6a | |||
795a6f3f76 | |||
fa74e08514 | |||
e6cf6e0c68 | |||
6789c16773 | |||
cdea0f8563 | |||
9df55671ac | |||
d96c6f13c1 | |||
bce1bf8704 | |||
8eb626b8da | |||
e6505d335b | |||
c8cb341afb | |||
e99cb88b13 | |||
78c3e299d0 | |||
23338437e9 | |||
f95899aebd | |||
5df10c66b6 | |||
811d1594cd | |||
c162208638 | |||
679d6597d8 | |||
f402799f28 | |||
60542d7426 | |||
db1fb58717 | |||
7d5675ec97 | |||
d8c50752f7 | |||
c674b837bb | |||
d867996072 | |||
7ef36c6933 | |||
982aae4dbf | |||
b5d3c6aaa8 | |||
5537bc32df | |||
5c0c723c5d | |||
0ed3b307d9 | |||
53a603d4ee | |||
fbf906bea7 | |||
a87e8bc3e8 | |||
a16a67dbc9 | |||
4c2231feb5 | |||
3932dd60da | |||
3c6380cb82 | |||
2612f99f35 | |||
0b4b9a6241 | |||
2d100885ee | |||
cb335720b7 | |||
dc8ad30b84 | |||
09e00652c3 | |||
b5b15c82f8 | |||
72e71124b8 | |||
d2be2acc95 | |||
d95614e960 | |||
dad9482d7a | |||
fff5d09ad9 | |||
e6ffd1fcc0 | |||
a97747b7c4 | |||
1921dcd023 | |||
18af1d00bd | |||
30cdc1da23 | |||
bb84958ce4 | |||
44de3fd00a | |||
39b1920532 | |||
aa591ec29e | |||
310c3f065d | |||
2311ef82c3 | |||
b4b931fc95 | |||
d1b887fcaa | |||
5a2990770c | |||
1d0f0a2539 | |||
dbc4d83b08 | |||
da905d21bd | |||
7d91079500 | |||
81a006a308 | |||
be59e241c6 | |||
e493139d85 | |||
1f59ec2ef9 | |||
20d05f609d | |||
0f9683de89 | |||
c805db3371 | |||
58d0d12c1f | |||
359683dbae | |||
94cab8b743 | |||
837447b6e4 | |||
ad52eade07 | |||
9cae3edb6b | |||
0860b61ccd | |||
d125c52b50 | |||
d485e095ae | |||
499caca7e3 | |||
83a2d36209 | |||
8b77ced05e | |||
49a9617f08 | |||
06aa919d9b | |||
77b77c285b | |||
bdc32b26fe | |||
736125f4ee | |||
89cb75f105 | |||
34a6261f27 | |||
ae18bfaee1 | |||
8510b0ed3f | |||
967f1a0e5b | |||
6c1fc62ef1 | |||
433c6b3b85 | |||
ee1ae5a071 | |||
49a15d512e | |||
fd1d73cc32 | |||
29c2aa9404 | |||
0ec1195930 | |||
d7a625bedb | |||
3854376210 | |||
5253d0e515 | |||
3bc7fa82b1 | |||
7d98d5819d | |||
8316c0ff63 | |||
315b8c91e5 | |||
405eb10a8a | |||
c3072e93c7 | |||
ae34ff622d | |||
d2704818d9 | |||
2ab4184314 | |||
9aade6599c | |||
2f87b06c83 | |||
8bb3f15bd9 | |||
7f198c83b5 | |||
f70c75e9c4 | |||
be5a4b270d | |||
50c0216435 | |||
4910de74fd | |||
f12b6854b7 | |||
b0b61fbaf7 | |||
7c0b0df6ed | |||
cb3ee4b72f | |||
3a3ef4d6ae | |||
9b3dea840e | |||
ca89fa7efd | |||
4705eac79c | |||
ebe0c5bcbb | |||
93c287bbd9 | |||
0defb00ede | |||
81c1d0286d | |||
fb25005bd5 | |||
2eb5aae23f | |||
a7d0560932 | |||
391f112bb3 | |||
bc8d13ba8a | |||
e8ec2ee28a | |||
b6c8fcbccd | |||
71476560e4 | |||
aa2d182840 | |||
4bf8512a7d | |||
a3a31a2c94 | |||
9ed4c1abb1 | |||
a9c6ea0452 | |||
9e2bbe2e5c | |||
598bff8c49 | |||
eb7c8300fa | |||
287ab63091 | |||
0c942d9806 | |||
237789e0d4 | |||
e656fa6a48 | |||
6dcb085b53 | |||
55a427e344 | |||
43eef20b71 | |||
b8fdb4146e | |||
cdbede7135 | |||
85a3a9ad52 | |||
943e0c2665 | |||
fd4d680e87 | |||
0b763135c9 | |||
832cc2ec44 | |||
3160b4be69 | |||
775596b5bf | |||
84782a92d8 | |||
6e817111cb | |||
cd8d631348 | |||
af37e61d05 | |||
803b65e08e | |||
aa9abde63a | |||
9c35abc6e2 | |||
1af95a15aa | |||
d2b693b1e0 | |||
b05bbf2f19 | |||
37ed3b0824 | |||
b56ec5ce38 | |||
1fb3f6cd58 | |||
1dfe1a5819 | |||
5d194b8ebe | |||
78810d2ca8 | |||
87d4202ed3 | |||
1d0b18291e | |||
16df5706ff | |||
1b24157f08 | |||
8ba50b7bd6 | |||
502ccb46bb | |||
fcbc410f1a | |||
85a7c28a3a | |||
7bd0da9316 | |||
212c338fcd | |||
137270b886 | |||
079b940d8e | |||
e396db67ce | |||
f37b469ab9 | |||
0a3363ebce | |||
53291822c0 | |||
5f5dcc7f99 | |||
9d27abfe04 | |||
4b4f030fe3 | |||
d5b0f947e0 | |||
9e6372185f | |||
0e125f7c81 | |||
a3ea422ec3 | |||
8d78e643e0 | |||
aa9cae7008 | |||
48085a946d | |||
8e27f6b9b0 | |||
f980e77a4a | |||
01798f7b11 | |||
03057c8ef9 | |||
ee851116d8 | |||
d367903946 | |||
66ade9efc6 | |||
ff41e72378 | |||
169f1645a4 | |||
c3914b2b15 | |||
b356524a9e | |||
85a13251a5 | |||
0417805e46 | |||
d3f1e35a12 | |||
ff585ac7c2 | |||
1b692dd13f | |||
dd00e6ecec | |||
222250bc41 | |||
c36e706731 | |||
adc9b76688 | |||
2e3ea96972 | |||
9b07d41f86 | |||
65ad377099 | |||
a61357f4e2 | |||
60403b2a4f | |||
624afa57ba | |||
36a39e1ed7 | |||
4a872c3435 | |||
90b9d1af1e | |||
551ca06ea7 | |||
61c0cf2c2d | |||
e801a99f67 | |||
beebfe7deb | |||
32e1963d5a | |||
facf5bedb1 | |||
035d43fb18 | |||
0d5f9fee52 | |||
3cb03fa4dc | |||
47ef3f2a49 | |||
8568c7bb99 | |||
02cf058552 | |||
c9157f0e2c | |||
83325e63ea | |||
37b8d969b1 | |||
298bdc7b8c | |||
81a191f8ba | |||
c3fc5d429b | |||
b08c934c78 | |||
1b1ddbdd8d | |||
0d16d53cb7 | |||
6c39ed8ab5 | |||
2f8999b5ad | |||
7107d03b72 | |||
3bd40e7271 | |||
8ace3e68f4 | |||
f69feb1def | |||
9fb423d5e0 | |||
e9df67a661 | |||
b6871ea13a | |||
cb84a7d0d9 | |||
25f7540f86 | |||
c29a3fd6d4 | |||
ab48396db9 | |||
51982b63c3 | |||
2b537d27ec | |||
16930e96aa | |||
d63ba4943a | |||
5d22f20ce3 | |||
2451849fd6 | |||
61680d9e87 | |||
850dd75822 | |||
6ba983fae3 | |||
ca34f9c2a1 | |||
0fb5b84392 | |||
39a9c39924 | |||
13692228c6 | |||
f22f4d229d | |||
97dbf42a4d | |||
041a219151 | |||
c137f2f916 | |||
6ef3b09ec9 | |||
263b78e995 | |||
0d87848a7e | |||
44ad0832ba | |||
93ced26e07 | |||
363daf6251 | |||
a6b1d1c9e0 | |||
576d8c23b3 | |||
c232c6e5f6 | |||
5d6ae52157 | |||
01e6256ed4 | |||
dbc114a529 | |||
4d77af8f0c | |||
c36a925bb5 | |||
605dd035d4 | |||
7526ae13d7 | |||
0eb41f70d2 | |||
a4c7ee738c | |||
8d4602c40d | |||
bb20d9623d | |||
73cc28d1cf | |||
ae54cd923c | |||
9d3c9a7d70 | |||
92a4274be7 | |||
4800b3f46c | |||
cce593b2f4 | |||
c491642713 | |||
5b4dd638a4 | |||
8440952167 | |||
4e4227e204 | |||
effb01d43c | |||
28d730a2ca | |||
0bd135a5c9 | |||
5522391942 | |||
7576355e95 | |||
c9e518f2ed | |||
593cf2407b | |||
bdb4c76d70 | |||
7cce805d39 | |||
b9d3ecc15d | |||
ec5eae08f7 | |||
c1edf31577 | |||
5262c04e46 | |||
8b01943d14 | |||
35823be13e | |||
4162ea33ff | |||
1787ef4db7 | |||
38b13611c9 | |||
52fa143e75 | |||
89047a0c8a | |||
9fbded7d6a | |||
c657781599 | |||
ecbe1ff79e | |||
d6811ab9b3 | |||
d45170db3e | |||
6e63768c71 | |||
1424bc2cf6 | |||
051fe3d15d | |||
fcae485071 | |||
2e9a7fdf94 | |||
570d7e18a4 | |||
8ac53970a3 | |||
dd3e108e10 | |||
c29bd81ffb | |||
a1173e4d84 | |||
d877e30a05 | |||
33ad54090d | |||
83ee6cfdbd | |||
f48971a0c4 | |||
a4b71aa73c | |||
4b7df662e8 | |||
4cd7b6718b | |||
ac2a94dda0 | |||
de7e65b32a | |||
1b283261c3 | |||
39cbfc1baa | |||
e563f18d31 | |||
1d03b3f28f | |||
ae0025a203 | |||
b5bd7acb7f | |||
657e7e52ac | |||
d6341c88cd | |||
bdb1191f9e | |||
3b29b67c0b | |||
cc43e53c4b | |||
a25c900d49 | |||
206651da94 | |||
be28116a98 | |||
4cdf29557c | |||
62928e47eb | |||
9b50b719d9 | |||
def60bf298 | |||
6496b6e463 | |||
6ce4c972a0 | |||
70e3c9968a | |||
074882f092 | |||
c2b3018908 | |||
96c85d9dca | |||
e15aafd781 | |||
9e17b84a5d | |||
c66d470166 | |||
4deb7d11a1 | |||
6f069f83d4 | |||
fa53df1e8a | |||
ba17f9d159 | |||
b558b51601 | |||
3ea0acd48b | |||
c171873fa6 | |||
1ccf94883a | |||
b26f8e0d24 | |||
5bc2725d1b | |||
8e06244311 | |||
0f35613e50 | |||
4cfad0b5ca | |||
c2593e71c0 | |||
bb331232ca | |||
96e8293bf4 | |||
e13464cb7c | |||
c5f8fbe86d | |||
8667706377 | |||
86e47ab226 | |||
10c383d66a | |||
4c65a308d6 | |||
246e43317f | |||
974b97b744 | |||
c914ddc0ee | |||
6509b293db | |||
a72eeb4c3f | |||
309397b72c | |||
077658c68d | |||
322ad19889 | |||
ab9d3d3d3e | |||
06a109d2b5 | |||
351743068a | |||
4e6532ff0d | |||
eaf57f2c33 | |||
7abdac7c9c | |||
002df66878 | |||
251bfaf410 | |||
24722447da | |||
90e7a90bcf | |||
6d3d3fc52c | |||
e843d790b1 | |||
c90ac1d21a | |||
041ceb81d8 | |||
c6f2a29320 | |||
0532d7592e | |||
f9fe1922d4 | |||
88b5b55062 | |||
afe501a06d | |||
43dfc9a940 | |||
7fbfafe2db | |||
f22f8f772a | |||
205b69e5cb | |||
75a4d1fdcb | |||
b3aa5c9e02 | |||
38b496d800 | |||
d2bb672f65 | |||
6c267c6072 | |||
598260895c | |||
e02dfdf5d6 | |||
154b342057 | |||
b8375013a3 | |||
5913528d32 | |||
773137591f | |||
8fa0470187 | |||
bf768b6cb5 | |||
9b8c38cac0 | |||
1f96372196 | |||
148d671b5d | |||
61d91ea0a9 | |||
f9317802f4 | |||
8809459f1b | |||
8f906f2d12 | |||
269dd5ced2 | |||
c063c69dea | |||
d43fe327c2 | |||
d80c4a7c90 | |||
4d274df6b2 | |||
431ebb59c2 | |||
3713bf397c | |||
86c7b70e63 | |||
9cfdd375ca | |||
c4e00e3402 | |||
93e1a0ff77 | |||
f69844717f | |||
5100ca6c77 | |||
857e58d635 | |||
bb9b8a1ced | |||
b240d75552 | |||
0a1d96c434 | |||
fe2ca8b05b | |||
bfb5cf2cf0 | |||
a6408a3397 | |||
5db29c7e2c | |||
a38e2b887c | |||
4a8ddaba2d | |||
e1879bbebd | |||
6b51b5fe9d | |||
acfc70ea50 | |||
fb495d34d5 | |||
af038cb789 | |||
53bc6df30f | |||
ece71ca1e7 | |||
a0079da01b | |||
bd5f6c5205 | |||
9687ddb842 | |||
ae19113141 | |||
8b55156da9 | |||
34ba5ffde3 | |||
e948b9e94e | |||
e3ec25790f | |||
6ea3b2c1e2 | |||
1a930dc604 | |||
e0982f3a24 | |||
18d9ac3249 | |||
7585a5473d | |||
4dd3b4aedc | |||
5a9e8449cc | |||
62a2755ecf | |||
6e66c95487 | |||
5c1170f6fb | |||
dc83d6b469 | |||
46715e17aa | |||
2e4013e948 | |||
b284e52203 | |||
391a183402 | |||
6722eeffa9 | |||
2cfd0de8e3 | |||
778bcf7623 | |||
0fcde73178 | |||
8a0f25b5b0 | |||
0e47e3c163 | |||
0e02af59ca | |||
c4db4dc6a6 | |||
016fe3ef72 | |||
0d3361dc99 | |||
79d8063b5d | |||
7d0b9662f4 | |||
afbcad3a1c | |||
15a329029d | |||
190e7ebdea | |||
65dcf36fe7 | |||
e2779bdfd7 | |||
87061fb5cb | |||
50b851a2c4 | |||
6a4c88a6ba | |||
185b1d8a21 | |||
7046c0d10e | |||
83cd164a45 | |||
33dd2f104d | |||
58278fa735 | |||
f9e5742821 | |||
d37442bc9d | |||
79451ac24f | |||
38f658edf9 | |||
db0542279b | |||
23353a3673 | |||
e0dc840197 | |||
f2fda7f92e | |||
cd0812af42 | |||
6f207a54aa | |||
aaebccf7ab | |||
655fb0999a | |||
c400eae7c8 | |||
3fba4c78dc | |||
9346144f0e | |||
edc86990a7 | |||
83692ffd55 | |||
8e1ec22679 | |||
29867dd187 | |||
0f9b8fcca8 | |||
195c98ddd2 | |||
6e521497db | |||
64ce2a2971 | |||
2781c8bdfb | |||
c45061a95a | |||
16c1ba440c | |||
e271059720 | |||
9743868cce | |||
72742d805c | |||
08564b3ac6 | |||
a76bc3de92 | |||
be0b5e6de1 | |||
bc856b4aa9 | |||
468a534d85 | |||
9bfe173f92 | |||
26a15229cf | |||
a9426ca48f | |||
844f04d555 | |||
731f07e062 | |||
1b3d82ebe2 | |||
b275a646ac | |||
90e059af32 | |||
321f19953d | |||
110b0cfc21 | |||
d9a32328b2 | |||
71faffaa44 | |||
edc0a16cad | |||
c5d2c040fe | |||
813f1c2ded | |||
b965d4d005 | |||
d0c5385534 | |||
eaae8ceaad | |||
ad543dd738 | |||
7df870e65d | |||
263b28b2f9 | |||
4c60f09d94 | |||
86e6397fa7 | |||
6c41a36d8e | |||
955d9f6d62 | |||
14fb96fec2 | |||
e7765a4513 | |||
7e302922c5 | |||
209d636919 | |||
55ea08cf11 | |||
e58e853445 | |||
e97bd0a50b | |||
31f266bf21 | |||
b611ec1714 | |||
4e549af1c9 | |||
54cc57a2b2 | |||
e43cb0ab07 | |||
c1ba64e7c3 | |||
19308ffc53 | |||
6a03003d41 | |||
0e7e731080 | |||
1993b46750 | |||
7ee18c4334 | |||
00166e0ff3 | |||
b43a5827c9 | |||
1619504059 | |||
80a61a6eed | |||
7c551aecb2 | |||
3065122059 | |||
d3f3ee6dfa | |||
d3fce1f145 | |||
83468af6c7 | |||
9365aec559 | |||
9315af3dfd | |||
41e389d690 | |||
c80919ff1e | |||
f446308e2a | |||
724e13180c | |||
2ad2bb4182 | |||
76359ba83e | |||
d8efa08d2f | |||
b87e150e34 | |||
9f3a451b6d | |||
41ee427040 | |||
93569fcd99 | |||
807e552f8a | |||
84e2223dbb | |||
f9e61fd184 | |||
c8851bd696 | |||
9b2b0a7c7d | |||
c15c604752 | |||
65866488c6 | |||
392376248d | |||
d20699ee0f | |||
a5f9247b32 | |||
dd30b64004 | |||
aed63c34c9 | |||
9cf05fd8fd | |||
6a49a70481 | |||
205a28eb56 | |||
f23567194c | |||
bb17d2949a | |||
9e0b065566 | |||
6dffb0f581 | |||
2e37bd546b | |||
37b1346361 | |||
7cecfd1053 | |||
de75849cc3 | |||
edafffcbb5 | |||
ea35147938 | |||
86add474f4 | |||
6c02bfc783 | |||
dcae427c60 | |||
47f00c48c5 | |||
7db0617a5b | |||
19d79d356d | |||
57a302a71e | |||
bff728b7f1 | |||
1e0ebe0d30 | |||
42248dbc71 | |||
d0318c47d3 | |||
388946e56b | |||
b24489ae80 | |||
acb7a752de | |||
16b7347ca3 | |||
8763dfbe85 | |||
3a1e1b6b92 | |||
0b0e63f1d5 | |||
602636520e | |||
1f3231c0b4 | |||
f9ee8c0aed | |||
41b2b13442 | |||
58b756eb67 | |||
e18b9b5317 | |||
d602467b80 | |||
58ebf6efea | |||
7633ab444c | |||
e1a3e9a16c | |||
4ff78d0a6f | |||
7f7a66d639 | |||
1d268e6f97 | |||
97d936f9be | |||
f3e45c895d | |||
fafdbea96c | |||
70cabc2383 | |||
d44b5ea143 | |||
0ac374f5df | |||
a8f6a25369 | |||
1bbd1dd234 | |||
0880623930 | |||
8a8e9c62ab | |||
c5d850ac13 | |||
8eb693aee9 | |||
570a46a840 | |||
6e23542296 | |||
f53aa6aa23 | |||
7baaaebef8 | |||
630b6dc0ce | |||
aaf4a752f7 | |||
adcbc79c1a | |||
b95c2b2e7e | |||
58ab62c03b | |||
7bd1b36ec9 | |||
c7a2ebb4ea | |||
75795a5b13 | |||
505dfe3254 | |||
9594c6163f | |||
f6ee13c263 | |||
f15bd985af | |||
e78a3cdab6 | |||
cf2ba6fe3b | |||
6edad57470 | |||
90216c1a60 | |||
1d352d1fce | |||
0552cdfd05 | |||
980c8e6ee4 | |||
bf51f5a83b | |||
e22fadcf44 | |||
0bdd2d9b1d | |||
bd29df4f67 | |||
4b6226dfd7 | |||
fca93a7c66 | |||
76ce54f68b | |||
e5fd1ce9ae | |||
3932366955 | |||
423ba84908 | |||
5d7c201018 | |||
ac234544a3 | |||
58e603e1ad | |||
90c9ef31a1 | |||
ecbe06a9e8 | |||
0e17762427 | |||
04fc9c3dd7 | |||
eff60d8294 | |||
ee55a9b9e9 | |||
365b2fcba2 | |||
475506273d | |||
e9d2a1fe86 | |||
c2e43fff7a | |||
45c3bf3a5e | |||
d6c7c292d4 | |||
2eb60b3e1b | |||
d4a8aa1f87 | |||
615c7e6681 | |||
23b13595b0 | |||
e85292fdec | |||
573716e24e | |||
ede65d91a1 | |||
987b00d2ad | |||
172f45208f | |||
91b14d568c | |||
581aa7d6cc | |||
cc70a8ab2c | |||
4bfed64586 | |||
788bca7113 | |||
bb27eea0c2 | |||
d239a8a63d | |||
d87bd6c3b2 | |||
d66fd31a4a | |||
9d950a89ba | |||
98634c1f4c | |||
beb6f63199 | |||
45f9afb73c | |||
ccef8ca125 | |||
5a929d3e99 | |||
c6f65d544f | |||
66e18a9c8d | |||
81089523a1 | |||
0e996c07df | |||
fab5be70b3 | |||
71bd9ab000 | |||
de0c073c26 | |||
ca0bfe0181 | |||
4f20089e57 | |||
500c68c6d5 | |||
a0e8bd36c9 | |||
96d7763c7d | |||
ca2c5b0911 | |||
bcc5cb77ad | |||
1aacceecf0 | |||
0bd415961c | |||
367b8a5e5d | |||
0b4217b592 | |||
5737b75d26 | |||
73b593ff98 | |||
2711d8844b | |||
933979ceaa | |||
59506fcc76 | |||
a3d60fb435 | |||
e24c5c912e | |||
ca997f727a | |||
c4b1cb15be | |||
49bf677da5 | |||
3657e6b93b | |||
db900a17d6 | |||
d8a25c6ba5 | |||
71cd7e6250 | |||
a1f99b74cf | |||
79f6d50e82 | |||
14d9f041ce | |||
2b8cb2afb6 | |||
8a1c981666 | |||
fdefedf288 | |||
e351465d97 | |||
ab5dc2200a | |||
0bf56f79f1 | |||
edf390ff43 | |||
15c751b1c8 | |||
9585910623 | |||
d739c4cde6 | |||
3adc265876 | |||
51cb1a87b8 | |||
9e9f6e4ad6 | |||
d1795f0ca8 | |||
433ecf39ee | |||
e7c29c1597 | |||
8303514fa0 | |||
a3f63e383d | |||
12231d08a8 |
1
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
1
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
@ -0,0 +1 @@
|
||||
blank_issues_enabled: false
|
59
.github/ISSUE_TEMPLATE/resource_publish_en.yml
vendored
Normal file
59
.github/ISSUE_TEMPLATE/resource_publish_en.yml
vendored
Normal file
@ -0,0 +1,59 @@
|
||||
name: Publish Resource
|
||||
title: "Resource: {name}"
|
||||
description: Publish the resource to the LiteyukiBot official store
|
||||
labels: [ "Resource" ]
|
||||
body:
|
||||
- type: input
|
||||
id: name
|
||||
attributes:
|
||||
label: Name
|
||||
description: The readable name of the resource pack
|
||||
placeholder: e.g. Cute UI Resource Pack
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: input
|
||||
id: description
|
||||
attributes:
|
||||
label: Description
|
||||
description: A brief description of the resource
|
||||
placeholder: e.g. Makes the rendering card style more beautiful
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: input
|
||||
id: author
|
||||
attributes:
|
||||
label: Author
|
||||
description: The GitHub username of the author
|
||||
placeholder: e.g. snowykami
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: input
|
||||
id: link
|
||||
attributes:
|
||||
label: Link
|
||||
description: Direct download link of the resource pack
|
||||
placeholder: e.g. https://aaa.com/r.zip
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: input
|
||||
id: homepage
|
||||
attributes:
|
||||
label: Homepage
|
||||
description: Homepage of the resource pack
|
||||
placeholder: e.g. https://github.com/user/repo
|
||||
validations:
|
||||
required: false
|
||||
|
||||
- type: input
|
||||
id: tags
|
||||
attributes:
|
||||
label: Tags
|
||||
description: Tags
|
||||
placeholder: 'e.g. [{"label": "Tag Name", "color": "#ea5252"}]'
|
||||
value: '[ { "label": "Text", "color": "#a2d8f4" } ]'
|
||||
validations:
|
||||
required: false
|
59
.github/ISSUE_TEMPLATE/resource_publish_zh.yml
vendored
Normal file
59
.github/ISSUE_TEMPLATE/resource_publish_zh.yml
vendored
Normal file
@ -0,0 +1,59 @@
|
||||
name: 发布资源
|
||||
title: "Resource: {name}"
|
||||
description: 发布资源到轻雪机器人官方商店
|
||||
labels: [ "Resource" ]
|
||||
body:
|
||||
- type: input
|
||||
id: name
|
||||
attributes:
|
||||
label: 名称
|
||||
description: 资源包的可读名称
|
||||
placeholder: e.g. 可爱UI资源包
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: input
|
||||
id: description
|
||||
attributes:
|
||||
label: 描述
|
||||
description: 资源的简短描述
|
||||
placeholder: e.g. 使渲染卡片的样式更美观
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: input
|
||||
id: author
|
||||
attributes:
|
||||
label: 作者
|
||||
description: 作者的github用户名
|
||||
placeholder: e.g. snowykami
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: input
|
||||
id: link
|
||||
attributes:
|
||||
label: 下载链接
|
||||
description: 资源包直接下载链接
|
||||
placeholder: e.g. https://aaa.com/r.zip
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: input
|
||||
id: homepage
|
||||
attributes:
|
||||
label: 主页
|
||||
description: 资源包主页
|
||||
placeholder: e.g. https://github.com/user/repo
|
||||
validations:
|
||||
required: false
|
||||
|
||||
- type: input
|
||||
id: tags
|
||||
attributes:
|
||||
label: 标签
|
||||
description: 标签
|
||||
placeholder: 'e.g. [{"label": "标签名", "color": "#ea5252"}]'
|
||||
value: '[ { "label": "标签名", "color": "#a2d8f4" } ]'
|
||||
validations:
|
||||
required: false
|
53
.github/ISSUE_TEMPLATE/问题反馈.md
vendored
Normal file
53
.github/ISSUE_TEMPLATE/问题反馈.md
vendored
Normal file
@ -0,0 +1,53 @@
|
||||
---
|
||||
name: 问题反馈
|
||||
about: 反馈你在使用轻雪中遇到的问题
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
# 问题反馈
|
||||
|
||||
## **请确保**
|
||||
|
||||
- 已认真阅读[文档]("https://bot.liteyuki.icu"),该问题不是文档提及的或你自己操作不当造成的
|
||||
- 你的问题是在最新版本的代码上测试的
|
||||
- 请勿重复提交相同或类似的issue
|
||||
|
||||
|
||||
## **描述问题**
|
||||
|
||||
请在此简单描述问题
|
||||
|
||||
|
||||
|
||||
## **如何复现**
|
||||
|
||||
请阐述一下如何重现这个问题
|
||||
### 预期
|
||||
|
||||
描述你期望发生的事情
|
||||
|
||||
### 实际
|
||||
|
||||
描述实际发生的事情
|
||||
|
||||
|
||||
|
||||
## **日志或截图**
|
||||
```
|
||||
日志内容
|
||||
```
|
||||
|
||||
|
||||
## **设备信息**
|
||||
- **系统**: [例如 Ubuntu 22.04]
|
||||
- **CPU**: [例如 Intel i7-7700K]
|
||||
- **内存**: [例如 16GB]
|
||||
- **Python**: [例如CPython 3.10.7]
|
||||
|
||||
|
||||
**补充内容**
|
||||
|
||||
可选,推荐提供`pip freeze`的输出,以及其他相关信息,以及你的建议
|
11
.github/dependabot.yml
vendored
Normal file
11
.github/dependabot.yml
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
# To get started with Dependabot version updates, you'll need to specify which
|
||||
# package ecosystems to update and where the package manifests are located.
|
||||
# Please see the documentation for all configuration options:
|
||||
# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
|
||||
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "" # See documentation for possible values
|
||||
directory: "/" # Location of package manifests
|
||||
schedule:
|
||||
interval: "weekly"
|
70
.github/workflows/deploy-docs.yml
vendored
Normal file
70
.github/workflows/deploy-docs.yml
vendored
Normal file
@ -0,0 +1,70 @@
|
||||
# 构建 VitePress 站点并将其部署到 GitHub Pages 的示例工作流程
|
||||
#
|
||||
name: Deploy VitePress site to Pages
|
||||
|
||||
on:
|
||||
# 在针对 `main` 分支的推送上运行。如果你
|
||||
# 使用 `master` 分支作为默认分支,请将其更改为 `master`
|
||||
push:
|
||||
branches: [main]
|
||||
|
||||
# 允许你从 Actions 选项卡手动运行此工作流程
|
||||
workflow_dispatch:
|
||||
|
||||
# 设置 GITHUB_TOKEN 的权限,以允许部署到 GitHub Pages
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
# 只允许同时进行一次部署,跳过正在运行和最新队列之间的运行队列
|
||||
# 但是,不要取消正在进行的运行,因为我们希望允许这些生产部署完成
|
||||
concurrency:
|
||||
group: pages
|
||||
cancel-in-progress: false
|
||||
|
||||
jobs:
|
||||
# 构建工作
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0 # 如果未启用 lastUpdated,则不需要
|
||||
# - uses: pnpm/action-setup@v3 # 如果使用 pnpm,请取消注释
|
||||
# - uses: oven-sh/setup-bun@v1 # 如果使用 Bun,请取消注释
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: '3.11'
|
||||
|
||||
- name: Setup API markdown
|
||||
run: |-
|
||||
python -m pip install litedoc
|
||||
litedoc liteyuki -o docs/zh/dev/api -l zh-Hans -cd class -fd func -md func -vd var -bu https://github.com/LiteyukiStudio/LiteyukiBot/tree/main/liteyuki/
|
||||
litedoc liteyuki -o docs/en/dev/api -l en -cd class -fd func -md func -vd var -bu https://github.com/LiteyukiStudio/LiteyukiBot/tree/main/liteyuki/
|
||||
|
||||
- name: 安装 pnpm
|
||||
uses: pnpm/action-setup@v2
|
||||
with:
|
||||
run_install: true
|
||||
version: 8
|
||||
|
||||
- name: 设置 Node.js
|
||||
run: |-
|
||||
cd docs
|
||||
pnpm install
|
||||
|
||||
- name: 构建文档
|
||||
env:
|
||||
NODE_OPTIONS: --max_old_space_size=8192
|
||||
run: |-
|
||||
cd docs
|
||||
pnpm run docs:build
|
||||
> .vitepress/dist/.nojekyll
|
||||
|
||||
- name: 部署文档
|
||||
uses: JamesIves/github-pages-deploy-action@v4
|
||||
with:
|
||||
# 这是文档部署到的分支名称
|
||||
branch: docs
|
||||
folder: docs/.vitepress/dist
|
51
.github/workflows/issue_handler.yml
vendored
Normal file
51
.github/workflows/issue_handler.yml
vendored
Normal file
@ -0,0 +1,51 @@
|
||||
name: Issue Handler
|
||||
|
||||
on:
|
||||
issues:
|
||||
types: [ opened, edited, closed ]
|
||||
|
||||
concurrency:
|
||||
group: issue_handler
|
||||
cancel-in-progress: false
|
||||
|
||||
jobs:
|
||||
check-issue:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Check issue title
|
||||
id: check_title
|
||||
run: |
|
||||
ISSUE_TITLE="${{ github.event.issue.title }}"
|
||||
if [[ "$ISSUE_TITLE" == Plugin:* || "$ISSUE_TITLE" == Resource:* ]]; then
|
||||
echo "Title starts with Plugin: or Resource:."
|
||||
echo "::set-output name=title_match::true"
|
||||
else
|
||||
echo "Title does not start with Plugin: or Resource:."
|
||||
echo "::set-output name=title_match::false"
|
||||
fi
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v2
|
||||
if: steps.check_title.outputs.title_match == 'true'
|
||||
with:
|
||||
python-version: '3.12'
|
||||
|
||||
- name: Install dependencies
|
||||
if: steps.check_title.outputs.title_match == 'true'
|
||||
run: |
|
||||
echo "Installing dependencies."
|
||||
pip install -r liteyuki_flow/requirements.txt
|
||||
|
||||
- name: Run Plugin/Resource issue handler
|
||||
if: steps.check_title.outputs.title_match == 'true'
|
||||
run: |
|
||||
echo "Running Plugin/Resource issue handler."
|
||||
python -m liteyuki_flow --handle
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.TOKEN }}
|
||||
ISSUE_NUMBER: ${{ github.event.issue.number }}
|
||||
REPOSITORY: ${{ github.repository }}
|
||||
ACT_TYPE: ${{ github.event.action }}
|
21
.github/workflows/pypi-publish.yml
vendored
Normal file
21
.github/workflows/pypi-publish.yml
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
name: Publish
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'v*'
|
||||
|
||||
jobs:
|
||||
pypi-publish:
|
||||
name: upload release to PyPI
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
id-token: write
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- uses: pdm-project/setup-pdm@v3
|
||||
|
||||
- name: Publish package distributions to PyPI
|
||||
run: pdm publish
|
71
.gitignore
vendored
71
.gitignore
vendored
@ -1,12 +1,67 @@
|
||||
# idea
|
||||
plugin/
|
||||
.venv/
|
||||
.idea/
|
||||
.vscode/
|
||||
.cache/
|
||||
|
||||
# config
|
||||
config.yml
|
||||
venv/
|
||||
|
||||
# external plugins
|
||||
node_modules/
|
||||
data/
|
||||
db/
|
||||
/resources/
|
||||
__pycache__/
|
||||
*.pyc
|
||||
*.pyo
|
||||
*.pyd
|
||||
*.pyw
|
||||
/plugins/
|
||||
|
||||
# pyc/pyo
|
||||
**/*.pyc
|
||||
**/*.pyo
|
||||
#config
|
||||
/config/
|
||||
!config/default.yml
|
||||
_config.yml
|
||||
config.yml
|
||||
config.example.yml
|
||||
|
||||
# vuepress
|
||||
.github
|
||||
|
||||
# mupy
|
||||
mypy.ini
|
||||
|
||||
# nuitka
|
||||
compile.bat
|
||||
src/resources/templates/latest-debug.html
|
||||
main.build/
|
||||
main.dist/
|
||||
main.exe
|
||||
main.cmd
|
||||
docs/.vuepress/.cache/
|
||||
docs/.vuepress/.temp/
|
||||
docs/.vuepress/dist/
|
||||
prompt.txt
|
||||
|
||||
# js
|
||||
**/echarts.js
|
||||
.env
|
||||
|
||||
|
||||
# pdm
|
||||
.pdm-python
|
||||
.pdm-build
|
||||
dist
|
||||
|
||||
doc
|
||||
|
||||
mkdoc2.py
|
||||
result.json
|
||||
|
||||
# litedoc
|
||||
docs/zh/dev/api
|
||||
docs/en/dev/api
|
||||
mkdoc.bat
|
||||
|
||||
# vitepress
|
||||
docs/.vitepress/dist/
|
||||
docs/.vitepress/cache
|
||||
docs/.vitepress/.temp
|
19
Dockerfile
Normal file
19
Dockerfile
Normal file
@ -0,0 +1,19 @@
|
||||
FROM swr.cn-north-4.myhuaweicloud.com/ddn-k8s/docker.io/library/python:3.10-slim-bullseye
|
||||
|
||||
ENV TZ Asia/Shanghai
|
||||
|
||||
COPY docker/sources.list /etc/apt/sources.list
|
||||
|
||||
RUN apt-get update && apt-get install -y git
|
||||
|
||||
WORKDIR /liteyukibot
|
||||
|
||||
COPY . /liteyukibot
|
||||
|
||||
RUN pip install --no-cache-dir -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple
|
||||
|
||||
RUN apt-get install -y libnss3 libnspr4 libdbus-1-3 libatk1.0-0 libatk-bridge2.0-0 libcups2 libdrm2 libatspi2.0-0 libxcomposite1 libxdamage1 libxfixes3 libxrandr2 libgbm1 libxkbcommon0 libasound2
|
||||
|
||||
EXPOSE 20216
|
||||
|
||||
CMD ["python", "main.py"]
|
31
LICENSE
Normal file
31
LICENSE
Normal file
@ -0,0 +1,31 @@
|
||||
LSO license
|
||||
LiteyukiStudio Opensource license
|
||||
|
||||
---
|
||||
|
||||
版权所有 © 2024 Snowykami
|
||||
|
||||
---
|
||||
|
||||
免费向任何获得副本的人或组织授予以相同许可为基础的权利
|
||||
|
||||
包括但不限于使用、复制、修改、合并、发布、分发、再许可和/或出售软件的副本
|
||||
|
||||
本软件及相关文档文件(以下简称"本软件")在相同方式许可为基础, 以开源的形式发布于互联网抑或其他媒体介质平台
|
||||
|
||||
任何人都有权利获取副本并以上述方式获取许可传播和/或使用
|
||||
|
||||
但获取副本时仍需注意:
|
||||
|
||||
- 上述版权声明和本许可声明应包含在本软件的副本中
|
||||
- 使用本软件及其副本时仍需保持与原有形式相同
|
||||
|
||||
- 在使用时仍需将本软件的副本以相同许可公开表现:
|
||||
- 不得未经原作者允许将本软件的副本以非原许可的形式对外盈利
|
||||
|
||||
---
|
||||
|
||||
该软件按"原样"之副本提供,不提供任何形式的任意保证,明示或暗示:
|
||||
包括但不限于适销性保证, 适用于特定目的非侵权
|
||||
|
||||
在任何情况下, 作者或版权所有者对任何非因作者或版权所有者使用该软件造成的索赔、损害或其他责任, 无论是在合同诉讼、侵权行为还是其他诉讼中都不具有责任, 作者及其版权所有者有权利驳回使用者因个人原因造成的任何损失之赔付
|
99
README.md
99
README.md
@ -1,80 +1,49 @@
|
||||
<div align="center">
|
||||
<img src="https://ks.liteyuki.icu:809/static/img/liteyuki_icon.png" style="width: 30%; margin-top:10%;" alt="a">
|
||||
|
||||
[//]: # (<img src="https://cdn.liteyuki.icu/static/svg/lylogo-full.svg" style="align-content: center; width: 50%; margin-top:10%;" alt="a">)
|
||||
[![][banner]][lightyuki-link]
|
||||
<h2><a href="https://bot.liteyuki.icu"> <span style="color: #a2d8f4">轻雪</span> <span style="color: #d0e9ff">6</span></a></h2>
|
||||
<h4> <span style="color: #a2d8f4">✨ 轻量,高效,易于扩展✨</span></h4>
|
||||
|
||||
[![][OneBot]][onebot-link]
|
||||
[![][NoneBot2]][nonebot-link]
|
||||
[![][Liteyuki6.0]][lightyuki-link]
|
||||
[![][Python3.10+]][python-link]
|
||||
[![][Usage]][usage-link]
|
||||
|
||||
- 原生支持与任意`Python`Bot框架互联,有良好的生态支持
|
||||
- 开箱即用,无需复杂配置
|
||||
- 集成包管理器,支持一键安装插件
|
||||
- 自定义主题支持,满足审美需求
|
||||
- 国际化支持,支持多种语言
|
||||
- 高性能,500插件2s内启动
|
||||
|
||||
<h3>👇更多内容请访问👇</h3>
|
||||
<h2><a href="https://bot.liteyuki.icu">轻雪机器人主页</a></h2>
|
||||
</div>
|
||||
<div align=center>
|
||||
<h2>
|
||||
<font color="#d0e9ff">
|
||||
轻雪
|
||||
</font>
|
||||
<font color="#a2d8f4">
|
||||
6.0
|
||||
</font>
|
||||
</h2>
|
||||
</div>
|
||||
<div align=center><h4>轻量,高效,易于扩展</h4></div>
|
||||
|
||||
- 基于[Nonebot2]("https://github.com/nonebot/nonebot2"),有良好的生态支持
|
||||
- 集成了上一代轻雪的优点和~~缺点~~
|
||||
- 支持一切Onebot标准通信,后续会支持更多的平台
|
||||
### 感谢
|
||||
- 所有贡献者们
|
||||
|
||||
## 手动安装和部署
|
||||
|
||||
1. 安装`Git`和`Python3.10+`后,使用命令`git clone https://github.com/snowykami/LiteyukiBot` 克隆项目至本地。
|
||||
一定要安装Git,Bot自带功能需要git支持
|
||||
2. 切换到轻雪目录,使用`pip install -r requirements.txt`安装依赖
|
||||
[OneBot]: https://img.shields.io/badge/OneBot-11/12-blue?style=for-the-badge
|
||||
|
||||
3. `python main.py`启动!
|
||||
[NoneBot2]: https://img.shields.io/badge/Nonebot-2-red?style=for-the-badge
|
||||
|
||||
## 一键部署脚本(复制到本地保存执行)
|
||||
[Liteyuki6.0]: https://img.shields.io/badge/Liteyuki-6.0-blue?style=for-the-badge
|
||||
|
||||
#### 提前部署好`Python3.10+`环境和`Git`环境
|
||||
[Python3.10+]: https://img.shields.io/badge/Python-3.10+-blue?style=for-the-badge
|
||||
|
||||
#### Windows
|
||||
[Usage]: https://img.shields.io/badge/文档-页面-blue?style=for-the-badge
|
||||
|
||||
```bash
|
||||
chcp 65001
|
||||
git clone https://github.com/snowykami/LiteyukiBot
|
||||
cd LiteyukiBot
|
||||
pip install -r requirements.txt
|
||||
echo python3 main.py > start.bat
|
||||
echo Install finished! Please run start.bat to start the bot!
|
||||
```
|
||||
[onebot-link]:https://onebot.dev/
|
||||
|
||||
#### Linux
|
||||
[nonebot-link]:https://nonebot.dev/
|
||||
|
||||
```bash
|
||||
git clone https://github.com/snowykami/LiteyukiBot
|
||||
cd LiteyukiBot
|
||||
pip install -r requirements.txt
|
||||
echo python3 main.py > start.sh
|
||||
chmod +x start.sh
|
||||
echo Install finished! Please run start.sh to start the bot!
|
||||
```
|
||||
[lightyuki-link]:/
|
||||
|
||||
## 注意事项
|
||||
[python-link]:https://www.python.org/
|
||||
|
||||
- 尽可能不要去动配置文件,通过与bot交互进行配置即可,若仍然想自定义配置请在`config.yml`中修改
|
||||
[usage-link]:https://bot.liteyuki.icu/
|
||||
|
||||
- 首次启动会提醒用户注册超级用户
|
||||
|
||||
- Bot会自动检测新版本,若出现新版本,可用`git pull`命令更新
|
||||
|
||||
### Onebot实现端配置
|
||||
|
||||
| 字段 | 参考值 | 说明 |
|
||||
|----|-------------------------------|-------------------------|
|
||||
| 协议 | 反向WebSocket | 轻雪使用反向ws协议进行通信,即轻雪作为服务端 |
|
||||
| 地址 | ws://`host`:`port`/onebot/v11 | 地址取决于配置文件,默认为`20216`端口 |
|
||||
|
||||
### 推荐方案
|
||||
1. 使用`Lagrange.Core`,`Lagrange.Core`支持多种协议
|
||||
2. 云崽的`icqq-plugin`和`ws-plugin`进行通信
|
||||
3. `Go-cqhttp`(目前已经半死不活了)
|
||||
4. 人工实现的`Onebot`协议,自己整一个WebSocket客户端,看着QQ的消息,然后给轻雪传输数据
|
||||
|
||||
请先自行查阅文档,若有困难请联系相关开发者而不是Liteyuki的开发者
|
||||
## 鸣谢
|
||||
|
||||
- html转图片使用的[kexue-z](https://github.com/kexue-z)的[nonebot-plugin-htmlrender](https://github.com/kexue-z/nonebot-plugin-htmlrender)插件的部分代码
|
||||
- 重启方案用的[18870](https://github.com/18870)的[Nonebot-plugin-reboot](https://github.com/18870/nonebot-plugin-reboot)插件的部分代码
|
||||
- Lagrange.Core的测试环境支持
|
||||
[banner]: https://socialify.git.ci/LiteyukiStudio/LiteyukiBot/image?description=1&forks=1&issues=1&Plus&pulls=1&stargazers=1&theme=Auto&logo=https%3a%2f%2fcdn.liteyuki.icu%2fstatic%2fsvg%2flylogo-full.svg
|
||||
|
8
config/default.yml
Normal file
8
config/default.yml
Normal file
@ -0,0 +1,8 @@
|
||||
nonebot:
|
||||
host: 127.0.0.1
|
||||
port: 20216
|
||||
command_start: ["", "/"]
|
||||
nickname: [ "liteyuki" ]
|
||||
default_language: zh
|
||||
driver: ~fastapi+~httpx+~websockets
|
||||
alconna_use_command_start: true
|
@ -1,4 +0,0 @@
|
||||
{
|
||||
"name": "Liteyuki Default",
|
||||
"version": "1.0"
|
||||
}
|
@ -1,4 +0,0 @@
|
||||
{
|
||||
"name": "Liteyuki Language Pack",
|
||||
"version": "1.0"
|
||||
}
|
10
docker/sources.list
Normal file
10
docker/sources.list
Normal file
@ -0,0 +1,10 @@
|
||||
deb https://mirrors.tuna.tsinghua.edu.cn/debian/ bullseye main contrib non-free
|
||||
# deb-src https://mirrors.tuna.tsinghua.edu.cn/debian/ bullseye main contrib non-free
|
||||
deb https://mirrors.tuna.tsinghua.edu.cn/debian/ bullseye-updates main contrib non-free
|
||||
# deb-src https://mirrors.tuna.tsinghua.edu.cn/debian/ bullseye-updates main contrib non-free
|
||||
|
||||
deb https://mirrors.tuna.tsinghua.edu.cn/debian/ bullseye-backports main contrib non-free
|
||||
# deb-src https://mirrors.tuna.tsinghua.edu.cn/debian/ bullseye-backports main contrib non-free
|
||||
|
||||
deb https://mirrors.tuna.tsinghua.edu.cn/debian-security bullseye-security main contrib non-free
|
||||
# deb-src https://mirrors.tuna.tsinghua.edu.cn/debian-security bullseye-security main contrib non-free
|
131
docs/.vitepress/config/common.ts
Normal file
131
docs/.vitepress/config/common.ts
Normal file
@ -0,0 +1,131 @@
|
||||
// 共有配置项,导入index用
|
||||
|
||||
import {defineConfig} from 'vitepress'
|
||||
import {generateSidebar} from 'vitepress-sidebar';
|
||||
import {zh} from "./zh";
|
||||
import {en} from "./en";
|
||||
|
||||
let defaultLocale = 'zh';
|
||||
const commonSidebarOptions = {
|
||||
collapsed: true,
|
||||
convertSameNameSubFileToGroupIndexPage: true,
|
||||
useTitleFromFrontmatter: true,
|
||||
useFolderTitleFromIndexFile: true,
|
||||
useFolderLinkFromIndexFile: true,
|
||||
includeFolderIndexFile: true,
|
||||
sortMenusByFrontmatterOrder: true,
|
||||
rootGroupText: 'LITEYUKIBOT',
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate sidebar config
|
||||
* multiple languages and sections
|
||||
* @returns {any[]}
|
||||
*/
|
||||
function generateSidebarConfig(): any[] {
|
||||
let sections = ["dev", "store", "usage", "deploy"]
|
||||
let languages = ['zh', 'en']
|
||||
let ret = []
|
||||
for (let language of languages) {
|
||||
for (let section of sections) {
|
||||
if (language === defaultLocale) {
|
||||
ret.push({
|
||||
basePath: `/${section}/`,
|
||||
scanStartPath: `${language}/${section}`,
|
||||
resolvePath: `/${section}/`,
|
||||
...commonSidebarOptions
|
||||
})
|
||||
} else {
|
||||
ret.push({
|
||||
basePath: `/${language}/${section}/`,
|
||||
scanStartPath: `${language}/${section}`,
|
||||
resolvePath: `/${language}/${section}/`,
|
||||
...commonSidebarOptions
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
console.log(generateSidebarConfig())
|
||||
|
||||
export const common = defineConfig({
|
||||
head: [
|
||||
// 配置favicon.ico
|
||||
['link', {rel: 'icon', type: 'image/x-icon', href: 'favicon.ico'}],
|
||||
['link', {rel: 'stylesheet', href: 'https://fonts.font.im/css?family=Cousine:400,400i,700,700i|Poppins:100,100i,200,200i,300,300i,400,400i,500,500i,600,600i,700,700i,800,800i,900,900i'}],
|
||||
],
|
||||
rewrites: {
|
||||
[`${defaultLocale}/:rest*`]: ":rest*",
|
||||
},
|
||||
themeConfig: {
|
||||
logo: {
|
||||
light: '/liteyuki.svg',
|
||||
dark: '/liteyuki-dark.svg',
|
||||
alt: 'LiteyukiBot Logo'
|
||||
},
|
||||
sidebar: generateSidebar(
|
||||
[
|
||||
...generateSidebarConfig()
|
||||
]
|
||||
),
|
||||
socialLinks: [
|
||||
{icon: 'github', link: 'https://github.com/LiteyukiStudio/LiteyukiBot'},
|
||||
{
|
||||
icon: {
|
||||
svg: '<svg t="1725391346807" class="icon" viewBox="0 0 1025 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5067" width="256" height="256"><path d="M1004.692673 466.396616l-447.094409-447.073929c-25.743103-25.763582-67.501405-25.763582-93.264987 0l-103.873521 103.873521 78.171378 78.171378c12.533635-6.00058 26.562294-9.359266 41.389666-9.359266 53.02219 0 96.00928 42.98709 96.00928 96.00928 0 14.827372-3.358686 28.856031-9.359266 41.389666l127.97824 127.97824c12.533635-6.00058 26.562294-9.359266 41.389666-9.359266 53.02219 0 96.00928 42.98709 96.00928 96.00928s-42.98709 96.00928-96.00928 96.00928-96.00928-42.98709-96.00928-96.00928c0-14.827372 3.358686-28.856031 9.359266-41.389666l-127.97824-127.97824c-3.051489 1.454065-6.184898 2.744293-9.379746 3.870681l0 266.97461c37.273227 13.188988 63.99936 48.721433 63.99936 90.520695 0 53.02219-42.98709 96.00928-96.00928 96.00928s-96.00928-42.98709-96.00928-96.00928c0-41.799262 26.726133-77.331707 63.99936-90.520695l0-266.97461c-37.273227-13.188988-63.99936-48.721433-63.99936-90.520695 0-14.827372 3.358686-28.856031 9.359266-41.389666l-78.171378-78.171378-295.892081 295.871601c-25.743103 25.784062-25.743103 67.542365 0 93.285467l447.114889 447.073929c25.743103 25.743103 67.480925 25.743103 93.264987 0l445.00547-445.00547c25.763582-25.763582 25.763582-67.542365 0-93.285467z" fill="#a2d8f4" p-id="5068"></path></svg>'
|
||||
},
|
||||
link: "https://git.liteyuki.icu/LiteyukiStudio/LiteyukiBot"
|
||||
}
|
||||
],
|
||||
search: {
|
||||
provider: 'local',
|
||||
options: {
|
||||
locales: {
|
||||
root: {
|
||||
translations: {
|
||||
button: {
|
||||
buttonText: '搜索文档',
|
||||
buttonAriaLabel: '打开搜索框',
|
||||
},
|
||||
modal: {
|
||||
noResultsText: '没有找到搜索结果',
|
||||
resetButtonTitle: '清除查询条件',
|
||||
footer: {
|
||||
selectText: '选择',
|
||||
navigateText: '切换',
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
en: {
|
||||
translations: {
|
||||
button: {
|
||||
buttonText: 'Search',
|
||||
buttonAriaLabel: 'Search',
|
||||
},
|
||||
modal: {
|
||||
noResultsText: 'No results found',
|
||||
resetButtonTitle: 'Reset search query',
|
||||
footer: {
|
||||
selectText: 'Select',
|
||||
navigateText: 'Navigate',
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
sitemap: {
|
||||
hostname: 'https://bot.liteyuki.icu'
|
||||
},
|
||||
lastUpdated: true,
|
||||
locales: {
|
||||
root: {label: "简体中文", ...zh},
|
||||
en: {label: "English", ...en},
|
||||
},
|
||||
|
||||
})
|
35
docs/.vitepress/config/en.ts
Normal file
35
docs/.vitepress/config/en.ts
Normal file
@ -0,0 +1,35 @@
|
||||
import {defineConfig} from 'vitepress'
|
||||
import {ThemeConfig} from "./utils";
|
||||
|
||||
export const en = defineConfig({
|
||||
lang: "en-US",
|
||||
title: "LiteyukiBot",
|
||||
description: "A high-performance, easy-to-use chatbot framework and application",
|
||||
themeConfig: {
|
||||
nav: [
|
||||
{text: 'Deploy', link: '/en/deploy/install'},
|
||||
{text: 'Usage', link: '/en/usage/basic'},
|
||||
{text: 'Extension', link: '/en/store/resource'},
|
||||
{text: 'Develop', link: '/en/dev/guide'},
|
||||
],
|
||||
docFooter: {
|
||||
prev: 'Prev Page',
|
||||
next: 'Next Page'
|
||||
},
|
||||
editLink: ThemeConfig.getEditLink(
|
||||
'Edit this page on GitHub',
|
||||
),
|
||||
footer: {
|
||||
message: 'Documentation built with <a href="https://vitepress.dev/">VitePress</a> | API references generated by <a href="https://github.com/LiteyukiStudio/litedoc">litedoc</a>',
|
||||
copyright: ThemeConfig.copyright
|
||||
},
|
||||
outline: ThemeConfig.getOutLine("Page Content"),
|
||||
|
||||
langMenuLabel: 'Language',
|
||||
returnToTopLabel: 'Back to top',
|
||||
sidebarMenuLabel: 'Menu',
|
||||
darkModeSwitchLabel: 'Theme',
|
||||
lightModeSwitchTitle: 'Light Mode',
|
||||
darkModeSwitchTitle: 'Dark Mode',
|
||||
},
|
||||
})
|
6
docs/.vitepress/config/index.ts
Normal file
6
docs/.vitepress/config/index.ts
Normal file
@ -0,0 +1,6 @@
|
||||
import {defineConfig} from "vitepress";
|
||||
|
||||
import {common} from './common'
|
||||
export default defineConfig({
|
||||
...common,
|
||||
})
|
40
docs/.vitepress/config/utils.ts
Normal file
40
docs/.vitepress/config/utils.ts
Normal file
@ -0,0 +1,40 @@
|
||||
// 本模块储存一些工具函数和引用
|
||||
|
||||
/**
|
||||
* GetEditLink Options
|
||||
* @param text Edit link text
|
||||
*/
|
||||
export const ThemeConfig = {
|
||||
getEditLink: (editPageText: string): { pattern: (params: { filePath: string; }) => string; text: string; } => {
|
||||
return {
|
||||
pattern: ({filePath}: { filePath: string; }): string => {
|
||||
// 匹配 /dev/api或 /{lang}/dev/api
|
||||
const regex = /^[^\/]+\/dev\/api/;
|
||||
console.log(filePath);
|
||||
if (regex.test(filePath)) {
|
||||
// remove {lang}/api prefix
|
||||
filePath = filePath.replace(regex, '')
|
||||
.replace('index.md', '__init__.py')
|
||||
.replace('.md', '.py');
|
||||
// 若文件名(不含扩展)和上级文件夹相同,返回文件夹/__init__.py
|
||||
if (filePath.split('/').pop().split('.')[0] === filePath.split('/').slice(-2, -1)[0]) {
|
||||
filePath = filePath.split('/').slice(0, -1).join('/') + '/__init__.py';
|
||||
}
|
||||
return `https://github.com/LiteyukiStudio/LiteyukiBot/tree/main/liteyuki${filePath}`;
|
||||
} else {
|
||||
return `https://github.com/LiteyukiStudio/LiteyukiBot/tree/main/docs/${filePath}`;
|
||||
}
|
||||
},
|
||||
text: editPageText
|
||||
};
|
||||
},
|
||||
|
||||
getOutLine: (label: string): { label: string; level: [number, number]; } => {
|
||||
return {
|
||||
label: label,
|
||||
level: [2, 6]
|
||||
};
|
||||
},
|
||||
|
||||
copyright: 'Copyright (C) 2020-2024 LiteyukiStudio. All Rights Reserved'
|
||||
}
|
35
docs/.vitepress/config/zh.ts
Normal file
35
docs/.vitepress/config/zh.ts
Normal file
@ -0,0 +1,35 @@
|
||||
import {defineConfig} from 'vitepress'
|
||||
import {ThemeConfig} from "./utils";
|
||||
|
||||
export const zh = defineConfig({
|
||||
lang: "zh-Hans",
|
||||
title: "轻雪机器人",
|
||||
description: "一个综合性的机器人应用及管理框架",
|
||||
themeConfig: {
|
||||
nav: [
|
||||
{text: '部署', link: '/deploy/install'},
|
||||
{text: '使用', link: '/usage/basic'},
|
||||
{text: '扩展', link: '/store/resource'},
|
||||
{text: '开发', link: '/dev/guide'},
|
||||
],
|
||||
docFooter: {
|
||||
prev: '上一页',
|
||||
next: '下一页'
|
||||
},
|
||||
editLink: ThemeConfig.getEditLink(
|
||||
'在 GitHub 上编辑此页',
|
||||
),
|
||||
footer: {
|
||||
message: '文档由 <a href="https://vitepress.dev/">VitePress</a> 构建 | API引用由 <a href="https://github.com/LiteyukiStudio/litedoc">litedoc</a> 生成',
|
||||
copyright: ThemeConfig.copyright
|
||||
},
|
||||
outline: ThemeConfig.getOutLine("页面内容"),
|
||||
|
||||
langMenuLabel: '语言',
|
||||
returnToTopLabel: '返回顶部',
|
||||
sidebarMenuLabel: '菜单',
|
||||
darkModeSwitchLabel: '主题',
|
||||
lightModeSwitchTitle: '轻色模式',
|
||||
darkModeSwitchTitle: '深色模式',
|
||||
},
|
||||
})
|
18
docs/.vitepress/theme/index.ts
Normal file
18
docs/.vitepress/theme/index.ts
Normal file
@ -0,0 +1,18 @@
|
||||
import DefaultTheme from 'vitepress/theme'
|
||||
import './liteyuki.scss'
|
||||
|
||||
import StatsBar from '../../components/StatsBar.vue'
|
||||
import PluginStore from '../../components/PluginStore.vue'
|
||||
import ResStore from '../../components/ResStore.vue'
|
||||
|
||||
|
||||
|
||||
export default {
|
||||
extends: DefaultTheme,
|
||||
enhanceApp({ app }) {
|
||||
app.component('StatsBar', StatsBar);
|
||||
app.component('PluginStore', PluginStore);
|
||||
app.component('ResStore', ResStore);
|
||||
},
|
||||
Layout: StatsBar
|
||||
}
|
149
docs/.vitepress/theme/liteyuki.scss
Normal file
149
docs/.vitepress/theme/liteyuki.scss
Normal file
@ -0,0 +1,149 @@
|
||||
:root {
|
||||
--vp-c-brand-1: #149ef8;
|
||||
--vp-c-brand-2: #0434ad;
|
||||
--vp-home-hero-name-color: transparent;
|
||||
--vp-home-hero-name-background: -webkit-linear-gradient(120deg, #34a3fe 30%, #8d44ff);
|
||||
|
||||
--vp-home-hero-image-background-image: linear-gradient(-45deg, #d0e9ff 50%, #a2d8f4 50%);
|
||||
--vp-home-hero-image-filter: blur(44px);
|
||||
|
||||
--vp-c-gray-1: #eee;
|
||||
--vp-c-gray-2: #aaa;
|
||||
--border-radius-1: 10px;
|
||||
--border-radius-2: 20px;
|
||||
--border-radius-3: 40px;
|
||||
--vp-font-family-base: 'Poppins', 'Punctuation SC', 'Inter', ui-sans-serif, system-ui,
|
||||
'PingFang SC', 'Noto Sans CJK SC', 'Noto Sans SC', 'Heiti SC',
|
||||
'Microsoft YaHei', 'DengXian', sans-serif, 'Apple Color Emoji',
|
||||
'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji';
|
||||
--vp-font-family-mono: 'Cousine', monospace;
|
||||
|
||||
--red: #ef4444;
|
||||
--liteyuki-color: #149ef8;
|
||||
--button-radius: 6px;
|
||||
|
||||
|
||||
}
|
||||
|
||||
.dark {
|
||||
--vp-home-hero-image-background-image: linear-gradient(-45deg, #004785 50%, #0374ad 50%);
|
||||
--vp-c-gray-1: #333;
|
||||
--vp-c-gray-2: #666;
|
||||
}
|
||||
|
||||
.tab-buttons {
|
||||
padding: 7px;
|
||||
flex: 1;
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
height: 60%;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
|
||||
.tab-title {
|
||||
text-align: center;
|
||||
justify-content: center;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.item-search-box {
|
||||
border-radius: 100px;
|
||||
width: 80%;
|
||||
padding: 10px;
|
||||
margin: 10px 0;
|
||||
border: 1px solid #ccc;
|
||||
}
|
||||
|
||||
.search-box-div {
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
.item-search-box {
|
||||
width: 80%;
|
||||
}
|
||||
|
||||
.item-card {
|
||||
position: relative;
|
||||
border-radius: 15px;
|
||||
background-color: var(--vp-c-gray-1);
|
||||
height: 160px;
|
||||
padding: 16px;
|
||||
margin: 10px;
|
||||
box-sizing: border-box;
|
||||
transition: background 0.3s ease;
|
||||
}
|
||||
|
||||
.item-name {
|
||||
font-size: 20px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.item-description {
|
||||
color: var(--vp-c-gray-2);
|
||||
font-size: 13px;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
.author-info {
|
||||
display: flex;
|
||||
justify-content: left;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.author-name {
|
||||
font-size: 15px;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.avatar {
|
||||
border-radius: 50%;
|
||||
margin: 0 10px;
|
||||
}
|
||||
|
||||
|
||||
/*store*/
|
||||
|
||||
.store-tabs {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
.publish-button {
|
||||
right: 10px;
|
||||
bottom: 10px;
|
||||
padding: 5px 10px;
|
||||
border-radius: 10px;
|
||||
background-color: var(--vp-c-brand-1);
|
||||
color: white;
|
||||
font-size: 15px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.pub-window {
|
||||
input {
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
margin: 10px 0;
|
||||
border-radius: 10px;
|
||||
border: 1px solid #ccc;
|
||||
}
|
||||
}
|
||||
|
||||
.pub-option {
|
||||
margin: 10px;
|
||||
padding: 5px;
|
||||
border: 2px solid var(--vp-c-gray-1);
|
||||
border-radius: var(--button-radius);
|
||||
background-color: var(--vp-c-gray-1);
|
||||
|
||||
&.close {
|
||||
}
|
||||
|
||||
&.submit {
|
||||
background-color: var(--vp-c-brand-1);
|
||||
}
|
||||
}
|
26
docs/components/ContributorBar.vue
Normal file
26
docs/components/ContributorBar.vue
Normal file
@ -0,0 +1,26 @@
|
||||
<script setup lang="ts">
|
||||
import {getTextRef} from "./scripts/i18n";
|
||||
import {repoPath, repoURL} from "./scripts/const";
|
||||
|
||||
const contributorImgSrc = `https://contrib.rocks/image?repo=${repoPath}`
|
||||
const contributorsUrl = `${repoURL}/graphs/contributors`
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="contributor-bar">
|
||||
<h2>{{ getTextRef('thx_contributors') }}</h2>
|
||||
<a :href="contributorsUrl">
|
||||
<div class="contributor-list">
|
||||
<img :src=contributorImgSrc alt="Contributors">
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.contributor-bar {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
</style>
|
38
docs/components/Dash.vue
Normal file
38
docs/components/Dash.vue
Normal file
@ -0,0 +1,38 @@
|
||||
<script setup lang="ts">
|
||||
import {ref} from "vue";
|
||||
|
||||
let total = ref(0);
|
||||
let online = ref(0);
|
||||
fetch("https://api.liteyuki.icu/count")
|
||||
.then(res => res.json())
|
||||
.then(data => {
|
||||
total.value = data.register;
|
||||
})
|
||||
.catch(err => console.error(err));
|
||||
fetch("https://api.liteyuki.icu/online")
|
||||
.then(res => res.json())
|
||||
.then(data => {
|
||||
online.value = data.online;
|
||||
})
|
||||
.catch(err => console.error(err));
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="info-box">
|
||||
<h1>Dashboard</h1>
|
||||
<div class="info">
|
||||
<div class="info-item">
|
||||
<h2>Total</h2>
|
||||
<p>{{ total }}</p>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<h2>Online</h2>
|
||||
<p>{{ online }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
20
docs/components/Geo.vue
Normal file
20
docs/components/Geo.vue
Normal file
@ -0,0 +1,20 @@
|
||||
<script setup>
|
||||
|
||||
</script>
|
||||
<template>
|
||||
<div>
|
||||
<div style="text-align: center">
|
||||
<h2>地理分布</h2>
|
||||
<p>数据来源于Liteyuki API</p>
|
||||
</div>
|
||||
<div id="main-chart" style="width: 100%; height: 600px;"></div>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
#main-chart {
|
||||
width: 100px;
|
||||
height: 600px;
|
||||
}
|
||||
</style>
|
10
docs/components/Home.vue
Normal file
10
docs/components/Home.vue
Normal file
@ -0,0 +1,10 @@
|
||||
<script setup lang="ts">
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
109
docs/components/PluginItemCard.vue
Normal file
109
docs/components/PluginItemCard.vue
Normal file
@ -0,0 +1,109 @@
|
||||
<template>
|
||||
<div class="item-card">
|
||||
<div class="item-name">{{ props.item.name }}</div>
|
||||
<div class="item-description">{{ props.item.desc }}</div>
|
||||
<div class="tags">
|
||||
<span class="tag" v-for="tag in props.item.tags" :key="tag" :style=getTagStyle(tag.color)>{{ tag.label }}</span>
|
||||
</div>
|
||||
<div class="item-bar">
|
||||
<!-- 三个可点击svg,一个github,一个下载,一个可点击"https://github.com/{{ username }}.png?size=80"个人头像配上id-->
|
||||
<a :href=props.item.homepage class="btn" target="_blank">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 16 16">
|
||||
<path fill="currentColor"
|
||||
d="m7.775 3.275l1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0a.751.751 0 0 1 .018-1.042a.751.751 0 0 1 1.042-.018a1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018a.751.751 0 0 1-.018-1.042m-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018a.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0a.751.751 0 0 1-.018 1.042a.751.751 0 0 1-1.042.018a1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83"/>
|
||||
</svg>
|
||||
</a>
|
||||
|
||||
<!-- <button class="copy-btn btn"><div @click="copyToClipboard">安装</div></button> 点击后把安装命令写入剪贴板-->
|
||||
<button class="btn copy-btn" @click="copyToClipboard">复制安装命令</button>
|
||||
|
||||
<div class="btn">
|
||||
<a class="author-info" :href="`https://github.com/${props.item.author }`">
|
||||
<img class="icon avatar" :src="`https://github.com/${ props.item.author }.png?size=80`" alt="">
|
||||
<div class="author-name">{{ props.item.author }}</div>
|
||||
</a>
|
||||
</div>
|
||||
<!-- 复制键,复制安装命令,npm install props.item.module_name-->
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {defineProps, onMounted} from 'vue'
|
||||
import Clipboard from 'clipboard'
|
||||
// 复制安装命令按钮
|
||||
|
||||
// 构建复制成功和失败的提示
|
||||
const props = defineProps({
|
||||
item: Object
|
||||
})
|
||||
|
||||
const copyToClipboard = () => {
|
||||
const clipboard = new Clipboard('.copy-btn', {
|
||||
text: () => `npm install ${props.item.module_name}`
|
||||
})
|
||||
clipboard.on('success', () => {
|
||||
})
|
||||
clipboard.on('error', () => {
|
||||
})
|
||||
}
|
||||
|
||||
const getTagStyle = (backgroundColor: string) => {
|
||||
// 将颜色值转换为 RGB 格式
|
||||
const rgb = backgroundColor.replace(/^#/, '');
|
||||
const [r, g, b] = rgb.match(/.{2}/g).map(x => parseInt(x, 16));
|
||||
|
||||
// 计算亮度
|
||||
const brightness = (r * 299 + g * 587 + b * 114) / 1000;
|
||||
|
||||
// 根据亮度决定文字颜色
|
||||
return {
|
||||
backgroundColor: backgroundColor,
|
||||
color: brightness > 128 ? '#000' : '#fff'
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
// 复制到剪贴板的函数
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.btn {
|
||||
margin-right: 15px;
|
||||
}
|
||||
|
||||
button {
|
||||
background-color: #00000000;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.copy-btn {
|
||||
cursor: pointer;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.icon {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
|
||||
.item-bar {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
height: 50px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
box-sizing: border-box;
|
||||
justify-content: space-between;
|
||||
color: #00000055;
|
||||
}
|
||||
|
||||
.tag {
|
||||
display: inline-block;
|
||||
padding: 0 5px;
|
||||
margin-right: 5px;
|
||||
border-radius: 5px;
|
||||
font-size: 12px;
|
||||
}
|
||||
</style>
|
91
docs/components/PluginStore.vue
Normal file
91
docs/components/PluginStore.vue
Normal file
@ -0,0 +1,91 @@
|
||||
<script setup lang="ts">
|
||||
import {computed, ref} from 'vue'
|
||||
import ItemCard from './PluginItemCard.vue'
|
||||
import ToggleSwitch from "./ToggleSwitch.vue";
|
||||
import {getTextRef} from "./scripts/i18n";
|
||||
import pluginsJson from "../public/plugins.json"
|
||||
|
||||
let showLiteyukiPluginOnly = ref(false)
|
||||
let filteredItems = computed(() => {
|
||||
let filtered = items.value
|
||||
if (search.value) {
|
||||
filtered = filtered.filter(item =>
|
||||
item.name.toLowerCase().includes(search.value.toLowerCase()) ||
|
||||
item.desc.toLowerCase().includes(search.value.toLowerCase()) ||
|
||||
item.author.toLowerCase().includes(search.value.toLowerCase()) ||
|
||||
item.module_name.toLowerCase().includes(search.value.toLowerCase())
|
||||
)
|
||||
}
|
||||
if (showLiteyukiPluginOnly.value) {
|
||||
filtered = filtered.filter(item => item.is_liteyuki_plugin)
|
||||
}
|
||||
return filtered
|
||||
})
|
||||
// 插件商店Nonebot
|
||||
let items = ref([])
|
||||
let search = ref('')
|
||||
// 从轻雪官方拉取,添加轻雪插件属性
|
||||
items.value = pluginsJson
|
||||
items.value.forEach(item => {
|
||||
item.is_liteyuki_plugin = true
|
||||
})
|
||||
|
||||
//追加
|
||||
fetch('https://registry.nonebot.dev/plugins.json')
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
// 遍历data的每一项,把is_official设为false
|
||||
data.forEach(item => {
|
||||
item.is_official = false
|
||||
})
|
||||
items.value = items.value.concat(data)
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="market">
|
||||
<h1>{{ getTextRef('pluginStore') }}</h1>
|
||||
<p>{{ getTextRef('pluginStoreDesc') }}</p>
|
||||
<!-- 搜索框-->
|
||||
<div class="search-box-div">
|
||||
<input class="item-search-box" type="text" v-model="search" :placeholder="getTextRef('search')"/>
|
||||
<ToggleSwitch v-model:modelValue="showLiteyukiPluginOnly"/>
|
||||
{{ getTextRef('liteyukiOnly') }}
|
||||
</div>
|
||||
|
||||
<!-- 按钮们-->
|
||||
<!-- <div class="tab">-->
|
||||
<!-- <button @click="open"-->
|
||||
<!-- </div>-->
|
||||
<div class="items">
|
||||
<!-- 使用filteredItems来布局商品 -->
|
||||
<ItemCard v-for="item in filteredItems" :key="item.id" :item="item"/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
h1 {
|
||||
color: #00a6ff;
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.items {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.search-box-div {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.search-box-div input {
|
||||
margin-right: 10px;
|
||||
}
|
||||
</style>
|
54
docs/components/ResItemCard.vue
Normal file
54
docs/components/ResItemCard.vue
Normal file
@ -0,0 +1,54 @@
|
||||
<template>
|
||||
<div class="item-card">
|
||||
<div class="item-name">{{ props.item.name }}</div>
|
||||
<div class="item-description">{{ props.item.description }}</div>
|
||||
<div class="item-bar">
|
||||
<!-- 三个可点击svg,一个github,一个下载,一个可点击"https://github.com/{{ username }}.png?size=80"个人头像配上id-->
|
||||
<a :href=props.item.link class="" target="_blank">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 16 16">
|
||||
<path fill="currentColor"
|
||||
d="m7.775 3.275l1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0a.751.751 0 0 1 .018-1.042a.751.751 0 0 1 1.042-.018a1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018a.751.751 0 0 1-.018-1.042m-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018a.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0a.751.751 0 0 1-.018 1.042a.751.751 0 0 1-1.042.018a1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83"/>
|
||||
</svg>
|
||||
</a>
|
||||
<div><a class="author-info" :href="`https://github.com/${props.item.author }`">
|
||||
<img class="icon avatar" :src="`https://github.com/${ props.item.author }.png?size=80`" alt="">
|
||||
<div class="author-name">{{ props.item.author }}</div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {defineProps} from 'vue'
|
||||
|
||||
const props = defineProps({
|
||||
item: Object
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
|
||||
|
||||
.icon {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
color: $themeColor;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
.item-bar {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
height: 50px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
box-sizing: border-box;
|
||||
justify-content: space-between;
|
||||
color: #00000055;
|
||||
}
|
||||
</style>
|
38
docs/components/ResPubWindow.vue
Normal file
38
docs/components/ResPubWindow.vue
Normal file
@ -0,0 +1,38 @@
|
||||
<template>
|
||||
<div v-if="isVisible" class="floating-window">
|
||||
<div class="window-content">
|
||||
<slot></slot>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {defineProps} from 'vue'
|
||||
|
||||
const props = defineProps({
|
||||
isVisible: Boolean,
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.floating-window {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.window-content {
|
||||
background: var(--vp-c-gray-1);
|
||||
padding: 20px;
|
||||
border-radius: 5px;
|
||||
max-width: 60%;
|
||||
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
</style>
|
105
docs/components/ResStore.vue
Normal file
105
docs/components/ResStore.vue
Normal file
@ -0,0 +1,105 @@
|
||||
<script setup lang="ts">
|
||||
import {computed, ref} from 'vue'
|
||||
import ItemCard from './ResItemCard.vue'
|
||||
import ResPubWindow from "./ResPubWindow.vue";
|
||||
import {getTextRef, formatLang} from "./scripts/i18n";
|
||||
import {RepoUrl} from "./scripts/statsApi";
|
||||
|
||||
import resourcesJson from "../public/resources.json"
|
||||
import {useData} from "vitepress";
|
||||
// formLan
|
||||
|
||||
// 从public/assets/resources.json加载插件
|
||||
let filteredItems = computed(() => {
|
||||
if (!search.value) {
|
||||
return items.value.reverse()
|
||||
}
|
||||
return items.value.filter(item =>
|
||||
item.name.toLowerCase().includes(search.value.toLowerCase()) ||
|
||||
item.description.toLowerCase().includes(search.value.toLowerCase()) ||
|
||||
item.author.toLowerCase().includes(search.value.toLowerCase())
|
||||
).reverse()
|
||||
})
|
||||
// 插件商店Nonebot
|
||||
let items = ref([])
|
||||
let search = ref('')
|
||||
items.value = resourcesJson
|
||||
// 列表倒序
|
||||
|
||||
const isPublishWindowOpen = ref(false)
|
||||
|
||||
let newRes = ref({
|
||||
name: '',
|
||||
})
|
||||
|
||||
function openPublishWindow() {
|
||||
isPublishWindowOpen.value = true
|
||||
}
|
||||
|
||||
function closePublishWindow() {
|
||||
isPublishWindowOpen.value = false
|
||||
}
|
||||
|
||||
let submitLang = ""
|
||||
if (formatLang(useData().site.value.lang) === "zh") {
|
||||
submitLang = "zh"
|
||||
} else {
|
||||
submitLang = "en"
|
||||
}
|
||||
|
||||
function submitForm() {
|
||||
const title = encodeURI(`Resource: ${newRes.value.name}`)
|
||||
const issueURL = `${RepoUrl}/issues/new?assignees=&labels=Resource&template=resource_publish_${submitLang}.yml&title=${title}`
|
||||
console.log(issueURL)
|
||||
window.open(issueURL, '_blank')
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="market">
|
||||
<h1>{{ getTextRef('resourceStore') }}</h1>
|
||||
<div class="search-box-div"><input class="item-search-box" type="text" :placeholder="getTextRef('search')"
|
||||
v-model="search"/></div>
|
||||
<div class="store-tabs" style="display: flex">
|
||||
<button class="store-button publish-button" @click="openPublishWindow">{{ getTextRef('publishRes') }}</button>
|
||||
</div>
|
||||
<div class="items">
|
||||
<!-- 使用filteredItems来布局商品 -->
|
||||
<ItemCard v-for="item in filteredItems" :key="item.id" :item="item"/>
|
||||
</div>
|
||||
<ResPubWindow class="pub-window" :is-visible="isPublishWindowOpen">
|
||||
<h2>{{ getTextRef("publishRes") }}</h2>
|
||||
<form @submit.prevent="submitForm">
|
||||
<label for="name">{{ getTextRef("resName") }}</label>
|
||||
<input type="text" id="name" v-model="newRes.name" :placeholder="getTextRef('resNameText')"/>
|
||||
<div class="pub-options" style="display: flex; justify-content: center">
|
||||
<button class="pub-option close" type="button" @click="closePublishWindow">{{
|
||||
getTextRef("closeButtonText")
|
||||
}}
|
||||
</button>
|
||||
<button class="pub-option submit" type="submit" @click="submitForm">{{
|
||||
getTextRef("submitButtonText")
|
||||
}}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</ResPubWindow>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
h1 {
|
||||
color: #00a6ff;
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.items {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
|
||||
</style>
|
301
docs/components/StatsBar.vue
Normal file
301
docs/components/StatsBar.vue
Normal file
@ -0,0 +1,301 @@
|
||||
<script setup lang="ts">
|
||||
import DefaultTheme from "vitepress/theme";
|
||||
import {ref, onMounted, onUnmounted} from "vue";
|
||||
import {statsApi, GithubStats, RepoUrl, StarMapUrl, uploadVisitRecord} from "./scripts/statsApi";
|
||||
import {getTextRef, updateRefData} from "./scripts/i18n";
|
||||
import {onBeforeRouteUpdate} from 'vue-router';
|
||||
|
||||
const {Layout} = DefaultTheme;
|
||||
|
||||
let githubStats: GithubStats | null = null;
|
||||
|
||||
const dataSections = {
|
||||
total: {
|
||||
name: 'total',
|
||||
color: '#00a6ff',
|
||||
value: ref(-1),
|
||||
link: StarMapUrl
|
||||
},
|
||||
online: {
|
||||
name: 'online',
|
||||
color: '#7eff7e',
|
||||
value: ref(-1),
|
||||
link: StarMapUrl
|
||||
},
|
||||
stars: {
|
||||
name: 'stars',
|
||||
color: '#ffcc00',
|
||||
value: ref(-1),
|
||||
link: `${RepoUrl}/stargazers`
|
||||
},
|
||||
forks: {
|
||||
name: 'forks',
|
||||
color: '#ff6600',
|
||||
value: ref(-1),
|
||||
link: `${RepoUrl}/forks`
|
||||
},
|
||||
issues: {
|
||||
name: 'issues',
|
||||
color: '#ff0000',
|
||||
value: ref(-1),
|
||||
link: `${RepoUrl}/issues`
|
||||
},
|
||||
prs: {
|
||||
name: 'prs',
|
||||
color: '#f15df1',
|
||||
value: ref(-1),
|
||||
link: `${RepoUrl}/pulls`
|
||||
},
|
||||
plugins: {
|
||||
name: 'plugins',
|
||||
color: '#a766ff',
|
||||
value: ref(-1),
|
||||
link: './store/plugin'
|
||||
},
|
||||
resources: {
|
||||
name: 'resources',
|
||||
color: '#5a54fa',
|
||||
value: ref(-1),
|
||||
link: './store/resource'
|
||||
},
|
||||
visitors: {
|
||||
name: 'visitors',
|
||||
color: '#00a6ff',
|
||||
value: ref(-1),
|
||||
link: RepoUrl
|
||||
},
|
||||
}
|
||||
|
||||
async function updateData() {
|
||||
// dataSections.online.value.value = await statsApi.getOnline();
|
||||
// dataSections.total.value.value = await statsApi.getTotal();
|
||||
[
|
||||
dataSections.online.value.value,
|
||||
dataSections.total.value.value,
|
||||
dataSections.plugins.value.value,
|
||||
dataSections.resources.value.value,
|
||||
dataSections.visitors.value.value,
|
||||
githubStats,
|
||||
|
||||
] = await Promise.all([
|
||||
statsApi.getOnline(),
|
||||
statsApi.getTotal(),
|
||||
statsApi.getPluginNum(),
|
||||
statsApi.getResourceNum(),
|
||||
statsApi.getVisitCount(),
|
||||
statsApi.getGithubStats(),
|
||||
]);
|
||||
dataSections.stars.value.value = githubStats?.stars || 0;
|
||||
dataSections.forks.value.value = githubStats?.forks || 0;
|
||||
dataSections.issues.value.value = githubStats?.issues || 0;
|
||||
dataSections.prs.value.value = githubStats?.prs || 0;
|
||||
}
|
||||
|
||||
function formatNumber(num: { value: number }): string {
|
||||
return num.value.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
|
||||
}
|
||||
|
||||
|
||||
onMounted(() => {
|
||||
const intervalId = setInterval(updateData, 10000);
|
||||
updateData();
|
||||
uploadVisitRecord();
|
||||
onUnmounted(() => {
|
||||
clearInterval(intervalId);
|
||||
});
|
||||
});
|
||||
|
||||
onBeforeRouteUpdate(() => {
|
||||
updateRefData();
|
||||
});
|
||||
|
||||
console.log(
|
||||
" _ _ _ _ _ ____ _ \n" +
|
||||
" | | (_) | | | (_) _ \\ | | \n" +
|
||||
" | | _| |_ ___ _ _ _ _| | ___| |_) | ___ | |_ \n" +
|
||||
" | | | | __/ _ \\ | | | | | | |/ / | _ < / _ \\| __|\n" +
|
||||
" | |____| | || __/ |_| | |_| | <| | |_) | (_) | |_ \n" +
|
||||
" |______|_|\\__\\___|\\__, |\\__,_|_|\\_\\_|____/ \\___/ \\__|\n" +
|
||||
" __/ | \n" +
|
||||
" |___/ "
|
||||
)
|
||||
|
||||
console.log(
|
||||
getTextRef('easterEgg')
|
||||
)
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Layout>
|
||||
<template #home-features-before>
|
||||
<div class="stats-bar-content">
|
||||
<div class="stats-bar">
|
||||
<div class="stats-info">
|
||||
<div class="stats-title">{{ getTextRef('stats') }}</div>
|
||||
<div class="sections">
|
||||
<div v-for="section in Object.values(dataSections)" :key="section.name" class="section">
|
||||
<a :href="section.link" target="_blank">
|
||||
<div class="section-tab">
|
||||
<span class="dot" :style="{backgroundColor: section.color}"></span>
|
||||
<span class="text">{{ getTextRef(section.name) }}</span>
|
||||
</div>
|
||||
<div class="number">{{ formatNumber(section.value) }}</div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="starmap">
|
||||
<iframe src="https://starmap.liteyuki.icu/" width="100%" height="300px" class="gamma">
|
||||
</iframe>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</Layout>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
.stats-bar-content {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.gamma {
|
||||
filter: brightness(1.8);
|
||||
}
|
||||
|
||||
.dark .gamma {
|
||||
filter: brightness(1.0);
|
||||
}
|
||||
|
||||
.stats-bar {
|
||||
width: 80%;
|
||||
max-width: 1150px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: 20px;
|
||||
margin: 10px;
|
||||
border-radius: var(--border-radius-2);
|
||||
background-color: var(--vp-c-gray-1);
|
||||
flex-direction: column; /* 默认纵向布局 */
|
||||
}
|
||||
|
||||
.stats-info{
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.stats-title {
|
||||
font-size: 20px;
|
||||
font-weight: bold;
|
||||
margin-bottom: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.sections {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
|
||||
gap: 15px;
|
||||
margin: 10px;
|
||||
}
|
||||
|
||||
.section {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
position: relative; /* 使伪元素相对于父元素定位 */
|
||||
border-radius: var(--border-radius-2);
|
||||
}
|
||||
|
||||
.section::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
border: 0 solid transparent; /* 初始边框为透明 */
|
||||
transition: border 0.1s ease-in-out; /* 添加过渡效果 */
|
||||
border-radius: var(--border-radius-2);
|
||||
pointer-events: none; /* 确保伪元素不会阻挡点击事件 */
|
||||
}
|
||||
|
||||
.section:hover::before {
|
||||
border: 1px solid #00a6ff; /* 悬停时添加边框 */
|
||||
border-radius: var(--border-radius-2);
|
||||
}
|
||||
|
||||
.section-tab {
|
||||
margin-left: 15px;
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
align-items: center; /* 确保垂直居中 */
|
||||
}
|
||||
|
||||
.dot {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
border-radius: 50%;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.text {
|
||||
font-size: 14px;
|
||||
white-space: nowrap;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.number {
|
||||
font-size: 27px;
|
||||
font-weight: bold;
|
||||
margin-top: 5px;
|
||||
margin-left: 15px;
|
||||
}
|
||||
|
||||
.starmap {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 200px;
|
||||
overflow: hidden;
|
||||
border-radius: var(--border-radius-2);
|
||||
}
|
||||
|
||||
.starmap iframe {
|
||||
position: absolute;
|
||||
top: -150px; /* 根据需要调整裁剪位置 */
|
||||
left: -40px; /* 根据需要调整裁剪位置 */;
|
||||
width: calc(100% + 80px); /* 根据需要调整裁剪宽度 */
|
||||
height: calc(100% + 300px); /* 根据需要调整裁剪高度 */
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
/* PC模式下的样式 */
|
||||
.stats-bar {
|
||||
flex-direction: row;
|
||||
margin: 30px;
|
||||
}
|
||||
|
||||
.stats-info {
|
||||
width: 40%;
|
||||
margin: 10px 30px 30px 30px;
|
||||
}
|
||||
|
||||
.starmap {
|
||||
width: 60%;
|
||||
height: 400px;
|
||||
}
|
||||
|
||||
.starmap iframe {
|
||||
position: absolute;
|
||||
top: -130px; /* 根据需要调整裁剪位置 */
|
||||
left: -60px; /* 根据需要调整裁剪位置 */;
|
||||
width: calc(100% + 120px); /* 根据需要调整裁剪宽度 */
|
||||
height: calc(100% + 280px); /* 根据需要调整裁剪高度 */
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
0
docs/components/Tabs.vue
Normal file
0
docs/components/Tabs.vue
Normal file
67
docs/components/ToggleSwitch.vue
Normal file
67
docs/components/ToggleSwitch.vue
Normal file
@ -0,0 +1,67 @@
|
||||
<template>
|
||||
<label class="switch">
|
||||
<input type="checkbox" :checked="modelValue" @change="updateValue($event.target.checked)">
|
||||
<span class="slider round"></span>
|
||||
</label>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { defineProps, defineEmits } from 'vue'
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: Boolean
|
||||
})
|
||||
|
||||
const emit = defineEmits(['update:modelValue'])
|
||||
|
||||
const updateValue = (value: boolean) => {
|
||||
emit('update:modelValue', value)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.switch {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
width: 60px;
|
||||
height: 34px;
|
||||
}
|
||||
|
||||
.switch input {
|
||||
opacity: 0;
|
||||
width: 0;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
.slider {
|
||||
position: absolute;
|
||||
cursor: pointer;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: #ccc;
|
||||
transition: .4s;
|
||||
border-radius: 34px;
|
||||
}
|
||||
|
||||
.slider:before {
|
||||
position: absolute;
|
||||
content: "";
|
||||
height: 26px;
|
||||
width: 26px;
|
||||
left: 4px;
|
||||
bottom: 4px;
|
||||
background-color: white;
|
||||
transition: .4s;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
input:checked + .slider {
|
||||
background-color: #2196F3;
|
||||
}
|
||||
|
||||
input:checked + .slider:before {
|
||||
transform: translateX(26px); /* 调整为 26px 以确保对齐 */
|
||||
}
|
||||
</style>
|
11
docs/components/TryLiteyukiWindow.vue
Normal file
11
docs/components/TryLiteyukiWindow.vue
Normal file
@ -0,0 +1,11 @@
|
||||
<script setup lang="ts">
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
4
docs/components/scripts/const.ts
Normal file
4
docs/components/scripts/const.ts
Normal file
@ -0,0 +1,4 @@
|
||||
export const platformBaseURL = "https://github.com/"
|
||||
export const repoPath = "LiteyukiStudio/LiteyukiBot"
|
||||
|
||||
export const repoURL = `${platformBaseURL}${repoPath}`
|
112
docs/components/scripts/i18n.ts
Normal file
112
docs/components/scripts/i18n.ts
Normal file
@ -0,0 +1,112 @@
|
||||
import {ref} from "vue";
|
||||
|
||||
import {useData} from "vitepress";
|
||||
|
||||
const i18nData = {
|
||||
en: {
|
||||
stats: 'Stats',
|
||||
online: 'Online',
|
||||
offline: 'Offline',
|
||||
total: 'Total',
|
||||
fetching: 'Fetching',
|
||||
stars: 'Stars',
|
||||
forks: 'Forks',
|
||||
issues: 'Issues',
|
||||
prs: 'Pull Requests',
|
||||
visitors: 'Visitors',
|
||||
size: 'Size',
|
||||
plugins: 'Plugins',
|
||||
resources: 'Resources',
|
||||
pluginStore: 'Plugin Store',
|
||||
pluginStoreDesc: 'Content from the LightSnow Plugin Store, LightSnow supports NoneBot through the lpnonebot plugin, and references some NoneBot plugins',
|
||||
liteyukiOnly: 'Liteyuki Only',
|
||||
search: 'Search',
|
||||
resourceStore: 'Resources Store',
|
||||
thx_contributors: 'Thanks the following contributors!',
|
||||
easterEgg: 'Congratulations on finding the Easter egg!',
|
||||
|
||||
publishPlugin: 'Publish Plugin',
|
||||
publishRes: 'Publish Resource',
|
||||
closeButtonText: 'Close',
|
||||
submitButtonText: 'Submit',
|
||||
|
||||
resName: 'Name',
|
||||
resDesc: 'Description',
|
||||
resAuthor: 'Author',
|
||||
resLink: 'Download Link',
|
||||
resHomepage: 'Homepage',
|
||||
|
||||
resNameText: 'Example: Kawaii Style Theme',
|
||||
resDescText: 'Example: A kawaii style and color theme',
|
||||
resAuthorText: 'Usually the github username, Example: yanyongyu',
|
||||
resLinkText: 'Direct download link, usually zip package link',
|
||||
resHomepageText: 'Optional, can be the name of the git platform repository"',
|
||||
},
|
||||
zh: {
|
||||
stats: '统计信息',
|
||||
online: '在线',
|
||||
offline: '离线',
|
||||
total: '实例',
|
||||
fetching: '获取中',
|
||||
stars: '星星',
|
||||
forks: '分叉',
|
||||
issues: '议题',
|
||||
prs: '拉取请求',
|
||||
visitors: '访客',
|
||||
size: '大小',
|
||||
plugins: '插件',
|
||||
resources: '主题资源',
|
||||
store: '商店',
|
||||
pluginStore: '插件商店',
|
||||
pluginStoreDesc: '内容来自轻雪插件商店,轻雪通过lpnonebot插件对NoneBot实现支持,引用了部分NoneBot插件',
|
||||
liteyukiOnly: '仅轻雪',
|
||||
search: '搜索',
|
||||
resourceStore: '资源商店',
|
||||
thx_contributors: '感谢以下贡献者!',
|
||||
easterEgg: '恭喜你发现了彩蛋!',
|
||||
|
||||
publishPlugin: '发布插件',
|
||||
publishRes: '发布资源',
|
||||
closeButtonText: '关闭',
|
||||
submitButtonText: '提交',
|
||||
|
||||
resName: '名称',
|
||||
resDesc: '描述',
|
||||
resAuthor: '作者',
|
||||
resLink: '下载链接',
|
||||
resHomepage: '主页',
|
||||
|
||||
resNameText: '示例:可爱风格主题',
|
||||
resDescText: '示例:一个可爱风格和配色的主题',
|
||||
resAuthorText: '通常为github用户名,示例:yanyongyu',
|
||||
resLinkText: '直接下载链接,通常为zip包链接',
|
||||
resHomepageText: '可选,可为git平台仓库名',
|
||||
}
|
||||
}
|
||||
|
||||
let refData = {}
|
||||
|
||||
function getText(lang: string, key: string): string {
|
||||
lang = formatLang(lang);
|
||||
return i18nData[lang][key];
|
||||
}
|
||||
|
||||
export function formatLang(lang: string): string {
|
||||
if (lang.includes('-')) {
|
||||
return lang.split('-')[0];
|
||||
}
|
||||
return lang;
|
||||
}
|
||||
|
||||
export function updateRefData() {
|
||||
const lang = formatLang(useData().site.value.lang);
|
||||
for (let key in refData) {
|
||||
refData[key].value = getText(lang, key);
|
||||
}
|
||||
}
|
||||
|
||||
export function getTextRef(key: string): any {
|
||||
const lang = formatLang(useData().site.value.lang);
|
||||
refData[key] = getText(lang, key);
|
||||
return refData[key] || key;
|
||||
}
|
182
docs/components/scripts/statsApi.ts
Normal file
182
docs/components/scripts/statsApi.ts
Normal file
@ -0,0 +1,182 @@
|
||||
// URL
|
||||
export const OWNER = "LiteyukiStudio"
|
||||
export const REPO = "LiteyukiBot"
|
||||
const githubAPIUrl = "https://api.github.com"
|
||||
const giteaAPIUrl = "https://git.liteyuki.icu/api/v1"
|
||||
const onlineFetchUrl = "https://api.liteyuki.icu/online";
|
||||
const totalFetchUrl = "https://api.liteyuki.icu/count";
|
||||
const visitRecordUrl = "https://api.liteyuki.icu/visit";
|
||||
const visitCountUrl = "https://api.liteyuki.icu/visit_count";
|
||||
|
||||
export const RepoUrl = `https://github.com/${OWNER}/${REPO}`
|
||||
export const StarMapUrl = "https://starmap.liteyuki.icu"
|
||||
|
||||
type GithubStats = {
|
||||
stars: number;
|
||||
forks: number;
|
||||
watchers: number;
|
||||
issues?: number;
|
||||
prs?: number;
|
||||
size?: number;
|
||||
}
|
||||
|
||||
// 异步接口
|
||||
interface StatsApi {
|
||||
getTotal: () => Promise<number>;
|
||||
getOnline: () => Promise<number>;
|
||||
getGithubStats: () => Promise<GithubStats>;
|
||||
getPluginNum: () => Promise<number>;
|
||||
getResourceNum: () => Promise<number>;
|
||||
getVisitCount: () => Promise<number>;
|
||||
}
|
||||
|
||||
|
||||
export type {GithubStats};
|
||||
|
||||
async function getGiteaStats() {
|
||||
try {
|
||||
const url = `${giteaAPIUrl}/repos/${OWNER}/${REPO}`;
|
||||
console.log(url);
|
||||
const res = await fetch(url);
|
||||
const data = await res.json();
|
||||
return {
|
||||
stars: data.stars_count,
|
||||
forks: data.forks_count,
|
||||
watchers: data.watchers_count,
|
||||
issues: 0,
|
||||
prs: 0,
|
||||
size: data.size,
|
||||
};
|
||||
} catch (e) {
|
||||
return {
|
||||
stars: -1,
|
||||
forks: -1,
|
||||
watchers: -1,
|
||||
issues: -1,
|
||||
prs: -1,
|
||||
size: -1,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
async function getGithubStats() {
|
||||
try {
|
||||
const res = await fetch(`${githubAPIUrl}/repos/${OWNER}/${REPO}`);
|
||||
const data = await res.json();
|
||||
return {
|
||||
stars: data.stargazers_count,
|
||||
forks: data.forks_count,
|
||||
watchers: data.watchers_count,
|
||||
issues: data.open_issues_count,
|
||||
prs: data.open_issues_count,
|
||||
size: data.size,
|
||||
};
|
||||
} catch (e) {
|
||||
return {
|
||||
stars: -1,
|
||||
forks: -1,
|
||||
watchers: -1,
|
||||
issues: -1,
|
||||
prs: -1,
|
||||
size: -1,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
async function getRepoStats() {
|
||||
// 两个接口各数据,加和返回
|
||||
const githubStats = await getGithubStats();
|
||||
const giteaStats = await getGiteaStats();
|
||||
return {
|
||||
stars: githubStats.stars + giteaStats.stars,
|
||||
forks: githubStats.forks + giteaStats.forks,
|
||||
watchers: githubStats.watchers + giteaStats.watchers,
|
||||
issues: githubStats.issues + giteaStats.issues,
|
||||
prs: githubStats.prs + giteaStats.prs,
|
||||
size: githubStats.size + giteaStats.size,
|
||||
};
|
||||
}
|
||||
|
||||
// 实现接口
|
||||
export const statsApi: StatsApi = {
|
||||
getTotal: async () => {
|
||||
try {
|
||||
const res = await fetch(totalFetchUrl);
|
||||
const data = await res.json();
|
||||
return data.register;
|
||||
} catch (e) {
|
||||
return -1;
|
||||
}
|
||||
},
|
||||
getOnline: async () => {
|
||||
try {
|
||||
const res = await fetch(onlineFetchUrl);
|
||||
const data = await res.json();
|
||||
return data.online;
|
||||
} catch (e) {
|
||||
return -1;
|
||||
}
|
||||
},
|
||||
getGithubStats: getRepoStats,
|
||||
getPluginNum: async () => {
|
||||
try {
|
||||
const res = await fetch('/plugins.json');
|
||||
const data = await res.json();
|
||||
return data.length;
|
||||
} catch (e) {
|
||||
return -1;
|
||||
}
|
||||
},
|
||||
getResourceNum: async () => {
|
||||
try {
|
||||
const res = await fetch('/resources.json');
|
||||
const data = await res.json();
|
||||
return data.length;
|
||||
} catch (e) {
|
||||
return -1;
|
||||
}
|
||||
},
|
||||
getVisitCount: async () => {
|
||||
try {
|
||||
const res = await fetch(visitCountUrl);
|
||||
const data = await res.json();
|
||||
return data.count;
|
||||
} catch (e) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function getDeviceId(): string {
|
||||
// 用户每次访问时生成一个唯一的设备ID,储存在localStorage中,用于统计用户数量
|
||||
const deviceIdKey = 'deviceId';
|
||||
let deviceId = localStorage.getItem(deviceIdKey);
|
||||
|
||||
if (!deviceId) {
|
||||
deviceId = generateUUID();
|
||||
localStorage.setItem(deviceIdKey, deviceId);
|
||||
}
|
||||
return deviceId;
|
||||
}
|
||||
|
||||
export async function uploadVisitRecord() {
|
||||
const deviceId = getDeviceId();
|
||||
try {
|
||||
await fetch(visitRecordUrl, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded'
|
||||
},
|
||||
body: new URLSearchParams({'device_id': deviceId}).toString(),
|
||||
});
|
||||
} catch (e) {
|
||||
console.error('Failed to upload visit record:', e);
|
||||
}
|
||||
}
|
||||
|
||||
function generateUUID(): string {
|
||||
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
|
||||
var r = (Math.random() * 16) | 0, v = c === 'x' ? r : (r & 0x3 | 0x8);
|
||||
return v.toString(16);
|
||||
});
|
||||
}
|
77
docs/en/deploy/config.md
Normal file
77
docs/en/deploy/config.md
Normal file
@ -0,0 +1,77 @@
|
||||
---
|
||||
title: Configurations
|
||||
order: 2
|
||||
---
|
||||
|
||||
# Configurations
|
||||
|
||||
LiteyukiBot supports `yaml`, `json`, and `toml` as configuration files, depending on your personal preference.
|
||||
|
||||
When you first run LiteyukiBot, it will generate `config.yml` and the `config` directory. You can modify the configuration items and restart LiteyukiBot. In most cases, you only need to modify
|
||||
the `superusers` and `nickname` fields.
|
||||
|
||||
When starting, LiteyukiBot will load all configuration files in the project directory `config.yml/yaml/json/toml` and the `config` directory. You can create multiple configuration files in
|
||||
the `config` directory, and LiteyukiBot will automatically merge these configuration files.
|
||||
|
||||
## **Basic Configuration**
|
||||
|
||||
```yaml
|
||||
# NoneBot configuration
|
||||
nonebot:
|
||||
command_start: [ "/", "" ] # Command prefix, if there is no "" empty command header, please enable alconna_use_command_start to ensure alconna parsing is normal
|
||||
host: 127.0.0.1 # Listening address, default is local, if you want to receive external requests, please fill in
|
||||
port: 20216 # Binding port
|
||||
nickname: [ "liteyuki" ] # Bot nickname list
|
||||
superusers: [ "1919810" ] # Superuser list
|
||||
liteyuki:
|
||||
log_level: "INFO" # Log level
|
||||
log_icon: true # Whether to display the log level icon (some console fonts are not available)
|
||||
auto_report: true # Whether to automatically report problems to Liteyuki server
|
||||
auto_update: true # Whether to automatically update Liteyuki, check for updates at 4 am every day
|
||||
plugins: [ ] # Liteyuki plugin list
|
||||
plugin_dirs: [ ] # Liteyuki plugin directory list
|
||||
```
|
||||
|
||||
## **Other configurations**
|
||||
|
||||
The following is the default value. If you need to customize it, please add it manually
|
||||
|
||||
```yaml
|
||||
# Advanced configuration
|
||||
nonebot:
|
||||
onebot_access_token: "" # OneBot access token
|
||||
default_language: "zh-CN" # Default language
|
||||
alconna_auto_completion: false # alconna auto completion
|
||||
safe_mode: false # Safe mode, if true, the bot will not load any plugins
|
||||
# other nonebot configurations
|
||||
custom_config_1: "custom_value1"
|
||||
custom_config_2: "custom_value2"
|
||||
|
||||
# development configuration
|
||||
liteyuki:
|
||||
allow_update: true # Whether to allow Liteyuki to update
|
||||
debug: false # Debug mode, if true, Liteyuki will output more detailed logs
|
||||
dev_mode: false # development mode, if true, Liteyuki will load all plugins in the development directory
|
||||
...
|
||||
```
|
||||
|
||||
```yaml
|
||||
|
||||
```
|
||||
|
||||
## **Example: Configuration of OneBot implementation side connected to NoneBot**
|
||||
|
||||
In production environments, it is recommended to use reverse WebSocket
|
||||
The fields provided by different implementation sides may be different, but basically the same. Here is a reference value
|
||||
|
||||
| Fields | Value | Description |
|
||||
|-------------|------------------------------------|---------------------------------------------------------------------------------------|
|
||||
| protocol | Reverse WebSocket | Liteyuki-NoneBot as server |
|
||||
| address | ws://127.0.0.1:20216/onebot/v11/ws | The address depends on the configuration file, the default is ` |
|
||||
| AccessToken | `""` | If you have configured `AccessToken` for Liteyuki, please fill in the same value here |
|
||||
|
||||
- To use other communication methods, please visit [OneBot Adapter](https://onebot.adapters.nonebot.dev/) for detailed information
|
||||
|
||||
## **Other**
|
||||
|
||||
- Liteyuki is not limited to the OneBot adapter and NoneBot2. You can use any adapter supported by NoneBot2 or use the Liteyuki message delivery plugin
|
60
docs/en/deploy/fandq.md
Normal file
60
docs/en/deploy/fandq.md
Normal file
@ -0,0 +1,60 @@
|
||||
---
|
||||
title: FAQ
|
||||
order: 3
|
||||
---
|
||||
# FAQ
|
||||
|
||||
## **Frequently Asked Questions**
|
||||
|
||||
- There are too many python interpreters on my computer, how do I know which one to use?
|
||||
- You can specify which python interpreter to use by using the full path to the python executable, for example, `/path/to/python main.py`
|
||||
- Use virtual environments to avoid conflicts between different python interpreters
|
||||
|
||||
- Why does the bot not respond after I start it?
|
||||
- Please check the configuration file `command_start` or `superusers`, make sure you have permission to use the command and send it correctly
|
||||
- Make sure the command header does not conflict with `nickname{}`, for example, a command is `help`, but the `Bot` nickname has a `help`, then it will be parsed as a nickname instead of a command
|
||||
|
||||
- Update Liteyuki failed, error `InvalidGitRepositoryError`
|
||||
- Please install `Git` correctly and deploy Liteyuki using cloning instead of direct download
|
||||
|
||||
- How to log in to chat platforms such as Telegram?
|
||||
- If you have this question, it means you don't know much about this project.
|
||||
This project does not implement the login function, only the message processing and response.
|
||||
The login function is provided by the implementation side (protocol side). The implementation side itself does not handle response logic.
|
||||
It processes and reports messages to Liteyuki according to the OneBot standard.
|
||||
You need to use an implementation side that complies with the OneBot standard to connect to Liteyuki and report messages to Liteyuki.
|
||||
Some recommended implementation sides have been listed below
|
||||
|
||||
- `Playwright` installation failed
|
||||
- Enter `playwright install` to install the browser
|
||||
|
||||
- Some plugins report errors after installation and cannot be started
|
||||
- Please refer to the plugin documentation first, confirm that the necessary configuration items of the plugin are intact,
|
||||
and if the problem persists, please contact the plugin author or start Liteyuki in safe mode `safe_mode: true`.
|
||||
In safe mode, you can use `npm uninstall` to uninstall problematic plugins
|
||||
|
||||
## Other questions
|
||||
|
||||
- Join chat group[775840726](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=SzmDYbfR6jY94o9KFNon7AwelRyI6M_u&authKey=ygeBdEmdFNyCWuNR4w0M1M8%2B5oDg7k%2FDfN0tzBkYcnbB%2FGHNnlVEnCIGbdftsnn7&noverify=0&group_code=775840726)
|
||||
- If you don't have a QQ account, you can [submit an issue on GitHub](https://github.com/LiteyukiStudio/LiteyukiBot/issues/new?assignees=&labels=&projects=&template=%E9%97%AE%E9%A2%98%E5%8F%8D%E9%A6%88.md&title=)
|
||||
|
||||
## **Recommended Solution(QQ)**
|
||||
|
||||
1. [Lagrange.OneBot](https://github.com/KonataDev/Lagrange.Core), based on `Lagrange.Core`, a Linux QQ implementation, supports OneBotv11 protocol
|
||||
2. [LLOneBot](https://github.com/LLOneBot/LLOneBot), a plugin for `Liteloader NTQQ`, supports OneBotv11 protocol
|
||||
3. [OpenShamrock](https://github.com/whitechi73/OpenShamrock), based on Lsposed, supports kritor protocol
|
||||
4. [TRSS-Yunzai](https://github.com/TimeRainStarSky/Yunzai), based on `Node.js`, supports OneBotv11 protocol
|
||||
5. [go-cqhttp](https://github.com/Mrs4s/go-cqhttp),A QQ Client based on `go`, supports OneBotv11 protocol
|
||||
6. [Gensokyo](https://github.com/Hoshinonyaruko/Gensokyo), use QQ protocol
|
||||
|
||||
## **Recommended Solution(Minecraft)**
|
||||
|
||||
1. [MinecraftOneBot](https://github.com/snowykami/MinecraftOnebot), We develop a Minecraft server chat bot
|
||||
|
||||
Other project encountered issues, please prioritize the documentation and issues of the project itself, don't ask LiteyukiBot developers
|
||||
|
||||
## **Acknowledgements**
|
||||
|
||||
- [Nonebot2](https://nonebot.dev) provides the underlying framework
|
||||
- [nonebot-plugin-alconna](https://github.com/ArcletProject/nonebot-plugin-alconna) provides the command parser
|
||||
- [MiSans](https://hyperos.mi.com/font/zh/),[MapleMono](https://gitee.com/mirrors/Maple-Mono) provides the font
|
57
docs/en/deploy/install.md
Normal file
57
docs/en/deploy/install.md
Normal file
@ -0,0 +1,57 @@
|
||||
---
|
||||
title: Install
|
||||
order: 1
|
||||
---
|
||||
# Install
|
||||
|
||||
## **Normal Installation**
|
||||
|
||||
1. Install [`Git`](https://git-scm.com/download/) and [`Python3.10+`](https://www.python.org/downloads/release/python-31010/) Environment.
|
||||
|
||||
```bash
|
||||
# Clone the project
|
||||
git clone https://github.com/LiteyukiStudio/LiteyukiBot --depth=1
|
||||
# change directory
|
||||
cd LiteyukiBot
|
||||
# install dependencies
|
||||
pip install -r requirements.txt
|
||||
# start the bot!
|
||||
python main.py
|
||||
```
|
||||
|
||||
> [!tip]
|
||||
> It is recommended to use a virtual environment to run Liteyuki to avoid dependency conflicts.
|
||||
> You can use `python -m venv .venv` to create a virtual environment, and then use `.venv\Scripts\activate` to activate the virtual environment
|
||||
> (use `source .venv/bin/activate` to activate on Linux).
|
||||
|
||||
|
||||
## **Run with Docker**
|
||||
|
||||
1. Install [`Docker`](https://docs.docker.com/get-docker/)
|
||||
2. Clone Repo `git clone https://github.com/LiteyukiStudio/LiteyukiBot --depth=1`
|
||||
3. Change directory `cd LiteyukiBot`
|
||||
4. Build docker image `docker build -t liteyukibot .`
|
||||
5. Run container `docker run -p 20216:20216 -v $(pwd):/liteyukibot -v $(pwd)/.cache:/root/.cache liteyukibot`
|
||||
|
||||
> [!tip]
|
||||
> If you are using Windows, please use the absolute project directory `/path/to/LiteyukiBot` instead of `$(pwd)` <br>
|
||||
> If you have modified the port number, please replace `20216:20216` with your port number
|
||||
|
||||
## **Use TRSS Script**
|
||||
[TRSS_Liteyuki Management Script](https://timerainstarsky.github.io/TRSS_Liteyuki/), which provides a more convenient way to manage LiteyukiBot, recommended to use `Arch Linux`
|
||||
|
||||
|
||||
## **Device Requirements**
|
||||
- Windows system version minimum `Windows10+`/`Windows Server 2019+`
|
||||
- Linux system requires Python3.10+, recommended `Ubuntu 20.04+`
|
||||
- CPU: at least `1vCPU`
|
||||
- Memory: Bot without other plugins will occupy `300~500MB`, including `chromium` and `node` processes, other plugins depend on specific plugins, recommended `1GB` or more
|
||||
- Disk: at least `1GB` of space
|
||||
|
||||
> [!warning]
|
||||
> If there are multiple environments on the device, please use `path/to/python -m pip install -r requirements.txt` to install dependencies, `path/to/python` is the path to your Python executable
|
||||
|
||||
> [!warning]
|
||||
> Liteyuki's update function depends on Git. If you do not have Git installed and run the source code directly, you will not be able to use the update function
|
||||
|
||||
#### For other questions, please refer to [FAQ](./fandq)
|
21
docs/en/dev/best_practices.md
Normal file
21
docs/en/dev/best_practices.md
Normal file
@ -0,0 +1,21 @@
|
||||
---
|
||||
title: Best Practices
|
||||
order: 10
|
||||
---
|
||||
|
||||
# Best Practices
|
||||
|
||||
## Bot applications
|
||||
- [LiteyukiBot](https://github.com/LiteyukiStudio/LiteyukiBot): Liteyuki Official Bot
|
||||
- [LiteyukiBot-TriM](https://github.com/TriM-Organization/LiteyukiBot-TriM): TriM Official Custom Liteyuki Bot
|
||||
- [Liteyuki Marsho](https://git.liteyuki.icu/LiteyukiStudio/marsho-alpha): A cute cat girl bot based on Liteyuki and the github model
|
||||
|
||||
## plugins
|
||||
- [liteyukibot-plugin-nonebot](https://github.com/LiteyukiStudio/liteyukibot-plugin-nonebot): Liteyuki Bot NoneBot plugin, allowing Liteyuki to support NoneBot
|
||||
- [nonebot-plugin-liteyukibot](https://github.com/LiteyukiStudio/nonebot-plugin-liteyukibot): NoneBot plugin, allowing NoneBot to support Liteyuki
|
||||
|
||||
## Others
|
||||
- [liteyuki starmap](https://starmap.liteyuki.icu): Liteyuki official star map, showing all instances of Liteyuki and their location distribution
|
||||
- [TRSS_Liteyuki](https://timerainstarsky.github.io/TRSS_Liteyuki/): LiteyukiBot management script
|
||||
- [litedoc](https://github.com/LiteyukiStudio/litedoc): Liteyuki API documentation generator
|
||||
- liteyukibot-api (closed source): Liteyuki Bot stat interface
|
98
docs/en/dev/comm.md
Normal file
98
docs/en/dev/comm.md
Normal file
@ -0,0 +1,98 @@
|
||||
---
|
||||
title: Process Communication
|
||||
order: 4
|
||||
---
|
||||
|
||||
# **Channel Communication**
|
||||
|
||||
### Introduction
|
||||
|
||||
LiteyukiBot is running in the main process MainProcess, and other plugin framework processes are child processes that come with it.
|
||||
Therefore, it is impossible to communicate through shared memory and direct object transfer.
|
||||
Liteyuki provides a channel [`Channel`](./api/comm/channel#class-channel-generic-t) for inter-process communication like `go`.
|
||||
You can send messages to other processes through [`Channel`](./api/comm/channel#class-channel-generic-t) and listen to messages from other processes.
|
||||
|
||||
### Example
|
||||
|
||||
The channel is full-duplex, with two receiving modes, but only one mode can be used for a channel, that is, passive mode and active mode, passive mode is implemented by the `chan.on_receive()` decorator callback function, and active mode needs to call `chan.receive()` to implement
|
||||
|
||||
- 创建子进程的同时会初始化一个被动通道和一个主动通道,且通道标识为`{process_name}-active`和`{process_name}-passive`,
|
||||
- 主进程中通过`get_channel`函数获取通道对象
|
||||
- 子进程中导入单例`active_channel`及`passive_channel`即可
|
||||
|
||||
> 在轻雪插件中(主进程中)
|
||||
|
||||
```python
|
||||
import asyncio
|
||||
|
||||
from liteyuki.comm import get_channel, Channel
|
||||
from liteyuki import get_bot
|
||||
|
||||
# get_channel函数获取通道对象,参数为调用set_channel时的通道标识
|
||||
channel_passive = get_channel("nonebot-passive") # 获取被动通道
|
||||
channel_active = get_channel("nonebot-active") # 获取主动通道
|
||||
liteyuki_bot = get_bot()
|
||||
|
||||
|
||||
# 注册一个函数在轻雪启动后运行
|
||||
@liteyuki_bot.on_after_start
|
||||
async def send_data():
|
||||
while True:
|
||||
channel_passive.send("I am liteyuki main process passive")
|
||||
channel_active.send("I am liteyuki main process active")
|
||||
await asyncio.sleep(3) # 每3秒发送一次消息
|
||||
```
|
||||
|
||||
> 在子进程中(例如NoneBot插件中)
|
||||
|
||||
```python
|
||||
from nonebot import get_driver
|
||||
from liteyuki.comm import active_channel, passive_channel # 子进程中获取通道直接导入进程全局单例即可
|
||||
from liteyuki.log import logger
|
||||
|
||||
driver = get_driver()
|
||||
|
||||
|
||||
# 被动模式,通过装饰器注册一个函数在接收到消息时运行,每次接收到字符串数据时都会运行
|
||||
@passive_channel.on_receive(filter_func=lambda data: isinstance(data, str))
|
||||
async def on_passive_receive(data):
|
||||
logger.info(f"Passive receive: {data}")
|
||||
|
||||
|
||||
# 注册一个函数在NoneBot启动后运行
|
||||
@driver.on_startup
|
||||
def on_startup():
|
||||
while True:
|
||||
data = active_channel.receive()
|
||||
logger.info(f"Active receive: {data}")
|
||||
```
|
||||
|
||||
> 启动后控制台输出
|
||||
|
||||
```log
|
||||
0000-00-00 00:00:00 [ℹ️信息] Passive receive: I am liteyuki main process passive
|
||||
0000-00-00 00:00:00 [ℹ️信息] Active receive: I am liteyuki main process active
|
||||
0000-00-00 00:00:03 [ℹ️信息] Passive receive: I am liteyuki main process passive
|
||||
0000-00-00 00:00:03 [ℹ️信息] Active receive: I am liteyuki main process active
|
||||
...
|
||||
```
|
||||
|
||||
## **共享内存通信**
|
||||
|
||||
### 简介
|
||||
|
||||
- 相比于普通进程通信,内存共享使得代码编写更加简洁,轻雪框架提供了一个内存共享通信的接口,你可以通过`storage`模块实现内存共享通信,该模块封装通道实现
|
||||
- 内存共享是线程安全的,你可以在多个线程中读写共享内存,线程锁会自动保护共享内存的读写操作
|
||||
|
||||
### 示例
|
||||
|
||||
> 在任意进程中均可使用
|
||||
|
||||
```python
|
||||
from liteyuki.comm.storage import shared_memory
|
||||
|
||||
shared_memory.set("key", "value") # 设置共享内存
|
||||
value = shared_memory.get("key") # 获取共享内存
|
||||
```
|
||||
|
||||
源代码:[liteyuki/comm/storage.py](https://github.com/LiteyukiStudio/LiteyukiBot/blob/main/liteyuki/comm/storage.py)
|
73
docs/en/dev/guide.md
Normal file
73
docs/en/dev/guide.md
Normal file
@ -0,0 +1,73 @@
|
||||
---
|
||||
title: Development Guide
|
||||
order: 0
|
||||
---
|
||||
|
||||
<script setup>
|
||||
import ContributorBar from '../../components/ContributorBar.vue'
|
||||
</script>
|
||||
|
||||
|
||||
# Development Guide
|
||||
|
||||
## How to Develop
|
||||
Please read the [Plugin Development](./plugin) and [API Reference](./api/) documents if you are developing a plugin.
|
||||
|
||||
If you need to submit code to the Liteyuki repository, please read the following:
|
||||
1. First [fork](https://github.com/LiteyukiStudio/LiteyukiBot/fork) a copy of the Liteyuki repository to your personal/organization account.
|
||||
2. Develop in your repository.
|
||||
3. Create a new branch in your repository and submit your code to this branch.
|
||||
4. Create a Pull Request in your repository to merge your branch into the `main` branch of the Liteyuki repository.
|
||||
|
||||
- Participation in development assumes that you are already familiar with the basic usage of the Python language and the Liteyuki framework.
|
||||
- If you are developing documentation, please ensure that you are familiar with Markdown syntax and basic front-end knowledge.
|
||||
- Contact the repository maintainer in case of conflicts.
|
||||
|
||||
### Suggestions
|
||||
- During development, you can use tools such as `mypy`, `flake8`, and `black` for code checking and formatting.
|
||||
- Enable developer mode by setting `dev_mode: true` in `config.yml` to see more debugging information in the console.
|
||||
|
||||
## Project Structure
|
||||
- `main.py`: Bot entry file
|
||||
- `liteyuki`: Framework directory (liteyukibot)
|
||||
- `src`: Bot application source code directory
|
||||
- `liteyuki_plugins`: Liteyuki plugin directory
|
||||
- `nonebot_plugins`: Nonebot plugin directory
|
||||
- `resources`: Built-in resource packs directory
|
||||
- `utils`: Utility directory
|
||||
- `tests`: Unit test directory
|
||||
- `docs`: Documentation directory
|
||||
- `en`: English documentation
|
||||
- `zh`: Chinese documentation
|
||||
- `docker`: Docker related content directory
|
||||
|
||||
The `liteyuki` framework is included in the `LiteyukiBot` application, so this project is both the `LiteyukiBot` application repository and the `liteyuki` framework repository.
|
||||
|
||||
The `src` directory is the application part, please develop business logic in this directory. `liteyuki_plugins` and `nonebot_plugins` directories are plugin directories, please develop plugins in these directories.
|
||||
|
||||
|
||||
## Standardization
|
||||
- Code should follow [`PEP8`](https://pep8.org/) and [`Google Python Style Guide`](https://google.github.io/styleguide/pyguide.html).
|
||||
- In addition, code submitted to the Liteyuki repository should follow the following guidelines:
|
||||
- Ensure that the code is runnable and harmless.
|
||||
- Ensure that the type hints in the code are correct.
|
||||
- Ensure that the comment style is [`Google Docstring`](https://google.github.io/styleguide/pyguide.html) or
|
||||
[`Liteyuki Docstring`](https://github.com/LiteyukiStudio/litedoc?tab=readme-ov-file#liteyuki-docstring)(recommended) to ensure that Litedoc can parse and generate documentation correctly.
|
||||
- Strings should be enclosed in double quotes `"` as much as possible, and single quotes `'` should be used when nesting strings.
|
||||
- If there is a part for ordinary users, ensure that the documentation is complete (each language has corresponding documentation).
|
||||
- commit message should follow the following guidelines:
|
||||
- You should commit the code after completing a feature or fixing a bug, and not mix the code of multiple features or bugs together.
|
||||
- We use gitmoji to mark the type of commit, such as `:sparkles:` for introducing new features, `:bug:` for fixing bugs, etc., please refer to [gitmoji](https://gitmoji.dev/) for details.
|
||||
- The format of the commit message is `<emoji> [type:] <message>`, such as `:sparkles: feat: add new field to event`,
|
||||
where the type field is the type of commit, not much is required, but please fill in as much as possible; the message field is a brief description, in summary, what you did.
|
||||
- There is no restriction on the language of the commit message, and bilingual use is possible if conditions permit.
|
||||
- The documentation should follow [`Markdown`](https://www.markdownguide.org/) syntax and support vitepress-related content:
|
||||
- Revise the documentation for each language when editing.
|
||||
- Ensure that the links in the document are correct and do not lead to inaccessible links.
|
||||
- Ensure that the **User Documentation** is easy to understand and the **Development Documentation** is detailed.
|
||||
|
||||
## Finally
|
||||
- This project is a non-profit open-source project, and we welcome anyone to participate in development. Your contributions will make Liteyuki better.
|
||||
|
||||
|
||||
<ContributorBar />
|
72
docs/en/dev/lyfunc.md
Normal file
72
docs/en/dev/lyfunc.md
Normal file
@ -0,0 +1,72 @@
|
||||
---
|
||||
title: Liteyuki Function
|
||||
order: 2
|
||||
---
|
||||
|
||||
# **轻雪函数**
|
||||
|
||||
轻雪函数 Liteyuki Function 是轻雪的一个功能,它允许你在轻雪中运行一些自定义的由数据驱动的命令,类似于Minecraft的mcfunction,属于资源包的一部分,但需单独起篇幅.
|
||||
|
||||
### **函数文件**
|
||||
|
||||
函数文件放在资源包的`functions`目录下,文件名以`.mcfunction` `.lyfunction` `.lyf`结尾,例如`test.mcfunction`,文件内容为一系列的命令,每行一个命令,支持单行注释`#`(编辑时的语法高亮可采取`shell`格式),例如:
|
||||
|
||||
```shell
|
||||
# 在发信器输出"hello world"
|
||||
cmd echo hello world
|
||||
|
||||
# 如果你想同时输出多行内容可以尝试换行符(Python格式)
|
||||
cmd echo hello world\nLiteyuki bot
|
||||
```
|
||||
|
||||
也支持句末注释,例如:
|
||||
```shell
|
||||
cmd echo hello world # 输出"hello world"
|
||||
```
|
||||
|
||||
### **命令文档**
|
||||
|
||||
```shell
|
||||
var <var1=value1> [var2=value2] ... # 定义变量
|
||||
cmd <command> # 在设备上执行命令
|
||||
api <api_name> [var=value...] # 调用Bot API
|
||||
function <func_name> # 调用函数,可递归
|
||||
sleep <time> # 异步等待,单位s
|
||||
nohup <command> # 使用新的task执行命令,即不等待
|
||||
end # 结束函数关键字,包括子task
|
||||
await # 等待所有异步任务结束,若函数中启动了其他task,需要在最后调用,否则task对象会被销毁
|
||||
```
|
||||
|
||||
|
||||
#### **示例**
|
||||
|
||||
```shell
|
||||
# 疯狂戳好友
|
||||
# 使用 /function poke user_id=123456 执行
|
||||
# 每隔0.2s戳两次,无限戳,会触发最大递归深度限制
|
||||
# 若要戳20s后停止,则需要删除await,添加sleep 20和end
|
||||
api friend_poke user_id=user_id
|
||||
api friend_poke user_id=user_id
|
||||
sleep 0.2
|
||||
nohup function poke
|
||||
await
|
||||
```
|
||||
|
||||
### **API**
|
||||
|
||||
理论上所有基于onebotv11的api都可调用,不同Adapter api也有差别.
|
||||
|
||||
[Onebot v11 API文档](https://283375.github.io/onebot_v11_vitepress/api/index.html)
|
||||
|
||||
### **结束关键字**
|
||||
|
||||
由于LiteyukiBot基于异步运行, 所以在编写lyfunction时也要注意异步的调用,避免出现"单线程走到底"的情况是效率提升的关键.
|
||||
|
||||
`await` 异步任务结束关键字,用于结束当前已完成function的执行
|
||||
|
||||
> [!warning]
|
||||
> 但若出现非单function的情况,有一个task任务没有完成而await被执行了,那么当前所有函数包的task都会被截停销毁
|
||||
|
||||
|
||||
> [!tip]
|
||||
> 编写轻雪函数推荐你使用VS Code插件[Liteyuki Function](https://github.com/LiteyukiStudio/lyfunctionTextmate)实现语法高亮
|
80
docs/en/dev/plugin.md
Normal file
80
docs/en/dev/plugin.md
Normal file
@ -0,0 +1,80 @@
|
||||
---
|
||||
title: Liteyuki Plugin
|
||||
order: 3
|
||||
---
|
||||
|
||||
# 简介
|
||||
|
||||
轻雪插件是轻雪内置的一部分功能,运行在主进程中,可以很高程度地扩展轻雪的功能
|
||||
|
||||
## 开始
|
||||
|
||||
### 创建插件
|
||||
|
||||
一个`.py`文件或一个包含`__init__.py`的文件夹即可被识别为插件
|
||||
|
||||
首先创建一个文件夹,例如`watchdog_plugin`,并在其中创建一个`__init__.py`文件,即可创建一个插件
|
||||
|
||||
`__init__.py`
|
||||
```python
|
||||
from liteyuki.plugin import PluginMetadata, PluginType
|
||||
from .watch_dog import * # 导入逻辑部分
|
||||
|
||||
# 定义插件元数据
|
||||
__plugin_meta__ = PluginMetadata(
|
||||
name="NoneDog", # 插件名称
|
||||
version="1.0.0", # 插件版本
|
||||
description="A simple plugin for nonebot developer", # 插件描述
|
||||
type=PluginType.SERVICE # 插件类型
|
||||
)
|
||||
|
||||
# 你的插件代码
|
||||
...
|
||||
```
|
||||
|
||||
### 编写逻辑部分
|
||||
|
||||
轻雪主进程不涉及聊天部分,因此插件主要是一些后台任务或者与聊天机器人的通信
|
||||
以下我们会编写一个简单的插件,用于开发NoneBot时进行文件系统变更重载
|
||||
`watch_dog.py`
|
||||
```python
|
||||
import os
|
||||
from liteyuki.dev import observer # 导入文件系统观察器
|
||||
from liteyuki import get_bot, logger # 导入轻雪Bot和日志
|
||||
from watchdog.events import FileSystemEvent # 导入文件系统事件
|
||||
|
||||
liteyuki = get_bot() # 获取唯一的轻雪Bot实例
|
||||
|
||||
exclude_extensions = (".pyc", ".pyo") # 排除的文件扩展名
|
||||
|
||||
|
||||
# 用observer的on_file_system_event装饰器监听文件系统事件
|
||||
@observer.on_file_system_event(
|
||||
directories=("src/nonebot_plugins",),
|
||||
event_filter=lambda event: not event.src_path.endswith(exclude_extensions) and ("__pycache__" not in event.src_path) and os.path.isfile(event.src_path)
|
||||
)
|
||||
def restart_nonebot_process(event: FileSystemEvent):
|
||||
logger.debug(f"File {event.src_path} changed, reloading nonebot...")
|
||||
liteyuki.restart_process("nonebot") # 调用重启进程方法
|
||||
```
|
||||
|
||||
### 加载插件
|
||||
|
||||
#### 方法1
|
||||
|
||||
- 在配置文件中的`liteyuki.plugins`中添加你的插件路径,例如`watchdog_plugin`,重启轻雪即可加载插件。
|
||||
|
||||
#### 方法2
|
||||
|
||||
- 使用开发工具快速运行插件,无需手动创建实例
|
||||
- 创建入口文件,例如`main.py`,并在其中写入以下代码
|
||||
|
||||
```python
|
||||
from liteyuki.dev.plugin import run_plugins
|
||||
|
||||
run_plugins("watchdog_plugin")
|
||||
```
|
||||
|
||||
然后运行`python main.py`即可启动插件
|
||||
|
||||
启用插件后,我们在src/nonebot_plugins下创建一个文件,例如`test.py`,并在其中写入一些代码,保存后轻雪会自动重载NoneBot进程
|
51
docs/en/dev/resource.md
Normal file
51
docs/en/dev/resource.md
Normal file
@ -0,0 +1,51 @@
|
||||
---
|
||||
title: Resource Pack
|
||||
order: 1
|
||||
---
|
||||
|
||||
# 简介
|
||||
|
||||
资源包,亦可根据用途称为主题包、字体包、语言包等,它允许你一定程度上自定义轻雪的外观,并且不用修改源代码
|
||||
|
||||
- [资源/主题商店](../store/resource)提供了一些资源包供你选择,你也可以自己制作资源包
|
||||
- 资源包的制作很简单,如果你接触过`Minecraft`的资源包,那么你能够很快就上手,仅需按照原有路径进行文件替换即可,讲起打包成一个新的资源包。
|
||||
- 部分内容制作需要一点点前端基础,例如`html`,`css`
|
||||
- 轻雪原版资源包请查看`LiteyukiBot/liteyuki/resources`,可以在此基础上进行修改
|
||||
- 欢迎各位投稿资源包到轻雪资源商店
|
||||
|
||||
请注意,主题包中的html渲染使用Js来规定数据的渲染位置,请确保您所编写的html代码能被Bot解析,否则会导致渲染失败或渲染结果不理想/异常/错位等无法预料的事情发生。推荐在编写html时同时更改对应Js代码,以避免出现无法预料的问题。
|
||||
|
||||
---
|
||||
|
||||
## 加载资源包
|
||||
|
||||
- 资源包通常是以`.zip`格式压缩的,只需要将其解压到根目录`resources`目录下即可,注意不要嵌套文件夹,正常的路径应该是这样的
|
||||
|
||||
```shell
|
||||
main.py
|
||||
resources
|
||||
└─resource_pack_1
|
||||
├─metadata.yml
|
||||
├─templates
|
||||
└───...
|
||||
└─resource_pack_2
|
||||
├─metadata.yml
|
||||
└─...
|
||||
```
|
||||
|
||||
- 你自己制作的资源包也应该遵循这个规则,并且应该在`metadata.yml`中填写一些信息
|
||||
- 若没有`metadata.yml`文件,则该文件夹不会被识别为资源包
|
||||
|
||||
```yaml
|
||||
name: "资源包名称"
|
||||
version: "1.0.0"
|
||||
description: "资源包描述"
|
||||
# 你可以自定义一些信息,但请保证以上三个字段
|
||||
...
|
||||
```
|
||||
|
||||
- 资源包加载遵循一个优先级,即后加载的资源包会覆盖前面的资源包,例如,你在A包中定义了一个`index.html`文件,B包也定义了一个`index.html`文件,那么加载B包后,A包中的`index.html`文件会被覆盖
|
||||
- 对于不同资源包的不同文件,是可以相对引用的,例如你在A中定义了`templates/index.html`,在B中定义了`templates/style.css`,可以在A的`index.html`中用`./style.css`相对路径引用B中的css
|
||||
|
||||
> [!tip]
|
||||
> 资源包的结构会随着轻雪的更新而有变动,第三方资源包开发者需要注意版本兼容性,同时用户也应该自行选择可用的资源包
|
63
docs/en/index.md
Normal file
63
docs/en/index.md
Normal file
@ -0,0 +1,63 @@
|
||||
---
|
||||
layout: home
|
||||
|
||||
hero:
|
||||
name: "LiteyukiBot"
|
||||
tagline: "An integrated chat bot application and management framework"
|
||||
actions:
|
||||
- theme: brand
|
||||
text: 🚀 Get Started
|
||||
link: ./deploy/install
|
||||
- theme: alt
|
||||
text: 📖 Manual
|
||||
link: ./usage/basic
|
||||
- theme: alt
|
||||
text: 📦 Extensions
|
||||
link: ./store/
|
||||
- theme: alt
|
||||
text: 🛠️ Development
|
||||
link: ./dev/guide
|
||||
image:
|
||||
light: /liteyuki.svg
|
||||
dark: /liteyuki-dark.svg
|
||||
alt: Liteyuki Logo
|
||||
|
||||
features:
|
||||
- title: Ecological Diversity
|
||||
icon: 🛠️
|
||||
details: Based on the liteyuki framework, compatible with any Python bot framework and application
|
||||
link: ./dev/api
|
||||
|
||||
- title: Pluggable
|
||||
icon: 🧩
|
||||
details: Use package manager for convenient management of plugins and resource packs
|
||||
link: ./store/plugin
|
||||
|
||||
- title: Customizable
|
||||
icon: 🎨
|
||||
details: Fully customize the appearance using resource packs
|
||||
link: ./store/resource
|
||||
|
||||
- title: Localization
|
||||
icon: 🌐
|
||||
details: Support for multiple languages through resource packs
|
||||
link: https://baike.baidu.com/item/i18n/6771940
|
||||
|
||||
- title: Easy to use
|
||||
icon: ⚙️
|
||||
details: No complicated pre-process, ready to use out of the box
|
||||
link: ./deploy/config
|
||||
|
||||
- title: High Performance
|
||||
icon: 🚀
|
||||
details: 500 plugins, starts within 2 seconds
|
||||
|
||||
- title: Rolling Updates
|
||||
icon: ☁️
|
||||
details: Keep your robot up to date with the latest commits
|
||||
|
||||
- title: Open Source
|
||||
icon: 📄
|
||||
details: The project follows the LSO LICENSE and is open to contributions
|
||||
link: https://github.com/LiteyukiStudio/LiteyukiStudioOpensourceLICENSE
|
||||
---
|
6
docs/en/store/plugin.md
Normal file
6
docs/en/store/plugin.md
Normal file
@ -0,0 +1,6 @@
|
||||
---
|
||||
title: Plugin Store
|
||||
order: 2
|
||||
---
|
||||
|
||||
<PluginStore />
|
6
docs/en/store/resource.md
Normal file
6
docs/en/store/resource.md
Normal file
@ -0,0 +1,6 @@
|
||||
---
|
||||
title: Resource Store
|
||||
order: 1
|
||||
---
|
||||
|
||||
<ResStore />
|
15
docs/en/usage/agreement.md
Normal file
15
docs/en/usage/agreement.md
Normal file
@ -0,0 +1,15 @@
|
||||
---
|
||||
title: 用户协议
|
||||
order: 3
|
||||
---
|
||||
# 用户协议
|
||||
|
||||
1. 本项目遵循`MIT`协议,你可以自由使用,修改,分发,但是请保留原作者信息
|
||||
2. 你可以选择开启`auto_report`(默认开启),轻雪会收集以下内容
|
||||
- 运行环境的设备信息:CPU,内存,系统信息及Python信息
|
||||
- 插件信息(不含插件数据)
|
||||
- 部分异常信息,
|
||||
- 会话负载信息(不含隐私部分)
|
||||
以上内容仅用于项目的优化,不包含任何隐私信息,且通过安全的方式传输到轻雪的服务器,若你不希望提供这些信息,可以在配置文件中把`auto_report`设定为`false`
|
||||
3. 本项目不会收集用户的任何隐私信息,但请注意甄别第三方插件的安全性
|
||||
4. 使用此项目代表你已经同意以上协议
|
122
docs/en/usage/basic.md
Normal file
122
docs/en/usage/basic.md
Normal file
@ -0,0 +1,122 @@
|
||||
---
|
||||
title: 基础插件
|
||||
order: 1
|
||||
---
|
||||
|
||||
# 基础插件
|
||||
---
|
||||
|
||||
> [!tip]
|
||||
> **参数**:`<param>`为必填参数,`[option]`为可选参数。
|
||||
>
|
||||
> **命令别名**:配置了命令别名的命令可以使用别名代替原命令,例如`npm install ~`可以使用`插件 安装 ~`代替。
|
||||
|
||||
## **轻雪命令`liteyuki_command`**
|
||||
|
||||
|
||||
| 命令 | 说明 | 权限 | 举例 | 可用参数 |
|
||||
| :----------------------------------------: | :---------------------------------------------------------------------------------------------: | :----------------------------------------: | :---------------------------------------------------------: | :----------------------------------------------------------------------------------: |
|
||||
| `reload-liteyuki` | 重载轻雪 | 超级用户 | ❌ | ❌ |
|
||||
| `update-liteyuki` | 更新轻雪 | 超级用户 | ❌ | ❌ |
|
||||
| `liteecho` | 查看当前bot 版本 | 超级用户 | ❌ | ❌ |
|
||||
| `status` | 查看统计信息和状态 | 超级用户 | ❌ | ❌ |
|
||||
| `config set <key> value` | 添加配置项,若存在则会覆盖,输入值会被执行以转换为正确的类型,"10"和10是不一样的 | 超级用户 | `config set name 'liteyuki-bot'` | `<key>`: 若存在则覆盖, 若不存在则创建于`config.yml` ; `value`: yml格式的所有合法内容 |
|
||||
| `config get [key] ` | 查询配置项,不带key返回配置项列表,推荐私聊使用 | 超级用户 | `config get name` | `<key>`: 若存在则返回, 若不存在则返回空 |
|
||||
| `switch-image-mode ` | 在普通图片和Markdown大图之间切换,该功能需要commit:505468b及以后的Lagrange.OneBot,默认普通图片 | 超级用户 | `switch-image-mode` | ❌ |
|
||||
| `/api api_name [args] ` | 调用机器人API | 超级用户 | `/api get_group_member_list group_id=1234567` | `<args>`: 参数列表, 格式为onebot v11协议api, 可用%20代替空格 |
|
||||
| `/function function_name [args] [kwargs] ` | 调用机器人函数(`.lyfunction`语法) | 超级用户 | `/function send_group_msg group_id=1234567 message='hello'` | `<args>`和`<kwargs>`: 参数列表, api格式为onebot v11协议api |
|
||||
| group enable/disable [group_id] | 在群聊启用/停用机器人,group_id仅超级用户可用 | 超级用户,群聊仅群主、管理员、超级用户可用 | `group enable 1145141919810` | `<group_id>`: 群号 |
|
||||
| liteyuki-docs | 查看轻雪文档 | 所有人 | ❌ | ❌ |
|
||||
|
||||
|
||||
---
|
||||
### **命令别名**
|
||||
|
||||
|
||||
| 命令 | 别名 |
|
||||
| :---------------: | :----------------------------------: |
|
||||
| status | 状态 |
|
||||
| reload-liteyuki | 重启轻雪 |
|
||||
| update-liteyuki | 更新轻雪 |
|
||||
| reload-resources | 重载资源 |
|
||||
| config | 配置, `set` 设置 / `get` 查询 |
|
||||
| switch-image-mode | 切换图片模式 |
|
||||
| liteyuki-docs | 轻雪文档 |
|
||||
| group | 群聊, `enable` 启用 / `disable` 停用 |
|
||||
|
||||
|
||||
---
|
||||
|
||||
## **插件/包管理器 `liteyuki_pacman`**
|
||||
|
||||
- 插件管理
|
||||
|
||||
| 命令 | 说明 | 权限 |
|
||||
| :-----------------------------------------------------: | :----------------------------------------: | :----------------------------------------------: |
|
||||
| `npm update` | 更新插件商店索引 | 超级用户 |
|
||||
| `npm install <plugin_name>` | 安装插件 | 超级用户 |
|
||||
| `npm uninstall <plugin_name>` | 卸载插件 | 超级用户 |
|
||||
| `npm search <keywords...>` | 通过关键词搜索插件 | 超级用户 |
|
||||
| `npm enable-global/disable-global <plugin_name>` | 全局启用/停用插件 | 超级用户 |
|
||||
| `npm enable/disable <plugin_name> [--group <group_id>]` | 当前会话启用/停用插件 | 群聊仅群主、管理员、超级用户可用,私聊所有人可用 |
|
||||
| `npm list [page] [num]` | 列出所有插件 page为页数,num为每页显示数量 | 群聊仅群主、管理员、超级用户可用,私聊所有人可用 |
|
||||
| `help <plugin_name>` | 查看插件帮助 | 所有人 |
|
||||
|
||||
|
||||
- 资源包管理
|
||||
|
||||
| 命令 | 说明 | 权限 |
|
||||
| :----------------------: | :------------------------------------------: | :------: |
|
||||
| `rpm list [page] [num]` | 列出所有资源包 page为页数,num为每页显示数量 | 超级用户 |
|
||||
| `rpm load <pack_name>` | 加载资源包 | 超级用户 |
|
||||
| `rpm unload <pack_name>` | 卸载资源包 | 超级用户 |
|
||||
| `rpm change <pack_name>` | 修改优先级 | 超级用户 |
|
||||
| `rpm reload` | 重载所有资源包 | 超级用户 |
|
||||
|
||||
|
||||
### 命令别名
|
||||
|
||||
| 命令 | 别名 |
|
||||
| :--------------: | :------: |
|
||||
| `npm` | 插件管理 |
|
||||
| `update` | 更新 |
|
||||
| `install` | 安装 |
|
||||
| `uninstall` | 卸载 |
|
||||
| `search` | 搜索 |
|
||||
| `enable` | 启用 |
|
||||
| `disable` | 停用 |
|
||||
| `enable-global` | 全局启用 |
|
||||
| `disable-global` | 全局停用 |
|
||||
| `rpm` | 资源包 |
|
||||
| `load` | 加载 |
|
||||
| `unload` | 卸载 |
|
||||
| `change` | 更改 |
|
||||
| `reload` | 重载 |
|
||||
| `list` | 列表 |
|
||||
| `help` | 帮助 |
|
||||
|
||||
> [!warning]
|
||||
> 受限于NoneBot2钩子函数的依赖注入参数,插件停用只能阻断传入响应,对于主动推送的插件不生效,请阅读插件主页的说明。
|
||||
>
|
||||
|
||||
---
|
||||
|
||||
|
||||
## **用户管理`liteyuki_user`**
|
||||
|
||||
| 命令 | 说明 | 权限 |
|
||||
| :-------------------------: | :----------------------------: | :----: |
|
||||
| `profile` | 查看用户信息菜单 | 所有人 |
|
||||
| `profile set <key> [value]` | 设置用户信息或打开属性设置菜单 | 所有人 |
|
||||
| `profile get <key>` | 获取用户信息 | 所有人 |
|
||||
|
||||
|
||||
###命令别名
|
||||
|
||||
| 命令 | 别名 |
|
||||
| :-------: | :------: |
|
||||
| `profile` | 个人信息 |
|
||||
| `set` | 设置 |
|
||||
| `get` | 查询 |
|
||||
|
||||
|
68
docs/en/usage/extra.md
Normal file
68
docs/en/usage/extra.md
Normal file
@ -0,0 +1,68 @@
|
||||
---
|
||||
title: 功能命令
|
||||
order: 2
|
||||
---
|
||||
|
||||
# 功能插件命令
|
||||
|
||||
### **轻雪天气`liteyuki_weather`**
|
||||
|
||||
查询实时天气,支持绑定城市,支持中英文城市名,支持多个关键词查询。
|
||||
|
||||
配置项
|
||||
|
||||
```yaml
|
||||
weather_key: "" # 和风天气的天气key,会自动判断key版本
|
||||
```
|
||||
|
||||
命令
|
||||
|
||||
```shell
|
||||
weather <keywords...> # Keywords为城市名,支持中英文
|
||||
```
|
||||
查询目标地实时天气,例如:"天气 北京 海淀", "weather Tokyo Shinjuku"
|
||||
|
||||
```shell
|
||||
bind-city <keywords...> # Keywords为城市名,支持中英文
|
||||
```
|
||||
|
||||
绑定查询城市,个人全局生效
|
||||
|
||||
#### 命令别名
|
||||
|
||||
| 命令 | 别名 |
|
||||
| :-------: | :------- |
|
||||
| weather | 天气 |
|
||||
| bind-city | 绑定城市 |
|
||||
|
||||
---
|
||||
|
||||
### **统计信息`liteyuki_statistics`**
|
||||
|
||||
统计信息
|
||||
命令
|
||||
|
||||
```shell
|
||||
statistic message --duration <duration> --period <period> --group [current|group_id] --bot [current|bot_id]
|
||||
```
|
||||
|
||||
功能: 用于统计Bot接收到的消息, 统计周期为`period`, 统计时间范围为`duration`
|
||||
|
||||
| 参数 | 格式 |
|
||||
| :------: | :------------------------------------------------------------: |
|
||||
| duration | 使用通用日期简写: `1d`(天), `1h`(小时), `45m`(分钟), `14s`(秒) |
|
||||
| period | 使用通用日期简写: `1d`(天), `1h`(小时), `45m`(分钟), `14s`(秒) |
|
||||
| group | `current` (当前群聊) 或 `group_id` (QQ群号) |
|
||||
| bot | `current` (当前Bot) 或 `bot_id` |
|
||||
|
||||
#### 命令别名
|
||||
|
||||
| 命令 | 别名 |
|
||||
| :----------: | :---: |
|
||||
| `statistic` | stat |
|
||||
| `message` | m |
|
||||
| `--duration` | -d |
|
||||
| --period` | -p |
|
||||
| `--group` | -g |
|
||||
| `--bot` | -b |
|
||||
| `current` | c |
|
20
docs/package.json
Normal file
20
docs/package.json
Normal file
@ -0,0 +1,20 @@
|
||||
{
|
||||
"devDependencies": {
|
||||
"@intlify/unplugin-vue-i18n": "^4.0.0",
|
||||
"sass-embedded": "^1.78.0",
|
||||
"vitepress": "^1.3.4",
|
||||
"vitepress-sidebar": "^1.25.3"
|
||||
},
|
||||
"scripts": {
|
||||
"docs:dev": "vitepress dev --host",
|
||||
"docs:build": "vitepress build",
|
||||
"docs:preview": "vitepress preview"
|
||||
},
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
"clipboard": "^2.0.11",
|
||||
"vue": "^3.4.38",
|
||||
"vue-route": "^1.5.1",
|
||||
"vue-router": "4"
|
||||
}
|
||||
}
|
1926
docs/pnpm-lock.yaml
generated
Normal file
1926
docs/pnpm-lock.yaml
generated
Normal file
File diff suppressed because it is too large
Load Diff
BIN
docs/public/favicon.ico
Normal file
BIN
docs/public/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 66 KiB |
151
docs/public/liteyuki-dark.svg
Normal file
151
docs/public/liteyuki-dark.svg
Normal file
@ -0,0 +1,151 @@
|
||||
|
||||
<svg width="618" height="419" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<defs>
|
||||
<linearGradient y2="0.49996" x2="1.00023" y1="0.49996" x1="0.00001" data-name="未命名的渐变 11" id="_未命名的渐变_11">
|
||||
<stop stop-color="#73fff1" offset="0"/>
|
||||
<stop stop-color="#5cefff" offset="1"/>
|
||||
</linearGradient>
|
||||
<linearGradient xlink:href="#_未命名的渐变_11" y2="457.99" x2="869.05" y1="457.99" x1="834.98" data-name="未命名的渐变 11" id="_未命名的渐变_11-2"/>
|
||||
<linearGradient xlink:href="#_未命名的渐变_11" y2="377" x2="870" y1="377" x1="838" data-name="未命名的渐变 11" id="_未命名的渐变_11-3"/>
|
||||
<linearGradient xlink:href="#_未命名的渐变_11" y2="425.24" x2="981.27" y1="425.24" x1="894.96" data-name="未命名的渐变 11" id="_未命名的渐变_11-4"/>
|
||||
<linearGradient xlink:href="#_未命名的渐变_11" y2="447.26" x2="1070.42" y1="447.26" x1="986.62" data-name="未命名的渐变 11" id="_未命名的渐变_11-5"/>
|
||||
<linearGradient y2="0.5" x2="1" y1="0.5" x1="0" data-name="未命名的渐变 240" id="_未命名的渐变_240">
|
||||
<stop stop-color="#eadeff" offset="0"/>
|
||||
<stop stop-color="#e0efff" offset="0.28"/>
|
||||
<stop stop-color="#d9ecff" offset="0.43"/>
|
||||
<stop stop-color="#aedbff" offset="0.59"/>
|
||||
<stop stop-color="#00d7ff" offset="1"/>
|
||||
</linearGradient>
|
||||
<linearGradient y2="0.50003" x2="1.00001" y1="0.50003" x1="-0.00002" data-name="未命名的渐变 9" id="_未命名的渐变_9">
|
||||
<stop stop-color="#2ebbff" offset="0"/>
|
||||
<stop stop-color="#006bff" offset="1"/>
|
||||
</linearGradient>
|
||||
<linearGradient xlink:href="#_未命名的渐变_9" y2="632.72" x2="1127.05" y1="632.72" x1="1046.74" data-name="未命名的渐变 9" id="_未命名的渐变_9-2"/>
|
||||
<linearGradient xlink:href="#_未命名的渐变_9" y2="559" x2="1262.04" y1="559" x1="1236.96" data-name="未命名的渐变 9" id="_未命名的渐变_9-3"/>
|
||||
<linearGradient xlink:href="#_未命名的渐变_9" y2="621.97" x2="1262.59" y1="621.97" x1="1235.37" data-name="未命名的渐变 9" id="_未命名的渐变_9-4"/>
|
||||
<linearGradient xlink:href="#_未命名的渐变_9" y2="602.15" x2="1219.09" y1="602.15" x1="1133.96" data-name="未命名的渐变 9" id="_未命名的渐变_9-5"/>
|
||||
<linearGradient y2="0.5" x2="0.99994" y1="0.5" x1="0" data-name="未命名的渐变 12" id="_未命名的渐变_12">
|
||||
<stop stop-color="#64a4ff" offset="0"/>
|
||||
<stop stop-color="#5c98ff" offset="1"/>
|
||||
</linearGradient>
|
||||
<linearGradient xlink:href="#_未命名的渐变_12" y2="629.45" x2="751.06" y1="629.45" x1="730.81" data-name="未命名的渐变 12" id="_未命名的渐变_12-2"/>
|
||||
<linearGradient y2="-0.34151" x2="1.51066" y1="0.36026" x1="0.21368" data-name="未命名的渐变 72" id="_未命名的渐变_72">
|
||||
<stop stop-color="#c4aeff" offset="0"/>
|
||||
<stop stop-color="#ce9fff" offset="0.43"/>
|
||||
<stop stop-color="#af8eff" offset="0.55"/>
|
||||
<stop stop-color="#7d74ff" offset="0.72"/>
|
||||
<stop stop-color="#6333ff" offset="0.9"/>
|
||||
</linearGradient>
|
||||
<linearGradient y2="0.4727" x2="1.00087" y1="0.52857" x1="0.01203" data-name="未命名的渐变 58" id="_未命名的渐变_58">
|
||||
<stop stop-color="#b1ccff" offset="0"/>
|
||||
<stop stop-color="#b8c0ff" offset="0.34"/>
|
||||
<stop stop-color="#cba2ff" offset="0.94"/>
|
||||
<stop stop-color="#ce9fff" offset="1"/>
|
||||
</linearGradient>
|
||||
<linearGradient y2="0.50006" x2="1" y1="0.50006" x1="0.00045" data-name="未命名的渐变 13" id="_未命名的渐变_13">
|
||||
<stop stop-color="#5cb1ff" offset="0"/>
|
||||
<stop stop-color="#64aaff" offset="1"/>
|
||||
</linearGradient>
|
||||
<linearGradient xlink:href="#_未命名的渐变_13" y2="640.89" x2="762.01" y1="640.89" x1="755.95" data-name="未命名的渐变 13" id="_未命名的渐变_13-2"/>
|
||||
<linearGradient y2="0.46723" x2="1.08059" y1="0.5373" x1="0.11482" data-name="未命名的渐变 65" id="_未命名的渐变_65">
|
||||
<stop stop-color="#64a2ff" offset="0.11"/>
|
||||
<stop stop-color="#62a6ff" offset="0.28"/>
|
||||
<stop stop-color="#5cb5ff" offset="1"/>
|
||||
</linearGradient>
|
||||
<linearGradient y2="0.61559" x2="1.13197" y1="0.40965" x1="0.09495" data-name="未命名的渐变 66" id="_未命名的渐变_66">
|
||||
<stop stop-color="#5cb5ff" offset="0"/>
|
||||
<stop stop-color="#74e0ff" offset="1"/>
|
||||
</linearGradient>
|
||||
<linearGradient y2="0.48885" x2="1.08173" y1="0.51016" x1="0.08139" data-name="未命名的渐变 68" id="_未命名的渐变_68">
|
||||
<stop stop-color="#9cd1ff" offset="0"/>
|
||||
<stop stop-color="#bae0ff" offset="1"/>
|
||||
</linearGradient>
|
||||
<linearGradient y2="0.50005" x2="1.00016" y1="0.50005" x1="0.00009" data-name="未命名的渐变 71" id="_未命名的渐变_71">
|
||||
<stop stop-color="#a6d3ff" offset="0"/>
|
||||
<stop stop-color="#b1ccff" offset="0.7"/>
|
||||
</linearGradient>
|
||||
<linearGradient xlink:href="#_未命名的渐变_58" y2="631.98" x2="928.63" y1="638.79" x1="890.79" data-name="未命名的渐变 58" id="_未命名的渐变_58-2"/>
|
||||
<linearGradient y2="0.50004" x2="1.00001" y1="0.50004" x1="-0.00008" data-name="未命名的渐变 14" id="_未命名的渐变_14">
|
||||
<stop stop-color="#5ca4ff" offset="0"/>
|
||||
<stop stop-color="#64a2ff" offset="1"/>
|
||||
</linearGradient>
|
||||
<linearGradient y2="0.4999" x2="0.99997" y1="0.4999" x1="-0.00009" data-name="未命名的渐变 5" id="_未命名的渐变_5">
|
||||
<stop stop-color="#64a2ff" offset="0"/>
|
||||
<stop stop-color="#5cc9ff" offset="1"/>
|
||||
</linearGradient>
|
||||
<linearGradient y2="0.50013" x2="0.99983" y1="0.50013" x1="0.00007" data-name="未命名的渐变 69" id="_未命名的渐变_69">
|
||||
<stop stop-color="#84c9ff" offset="0"/>
|
||||
<stop stop-color="#b1ccff" offset="0.42"/>
|
||||
</linearGradient>
|
||||
<linearGradient y2="0.36565" x2="1.03665" y1="0.6159" x1="0.09061" data-name="未命名的渐变 67" id="_未命名的渐变_67">
|
||||
<stop stop-color="#74e0ff" offset="0"/>
|
||||
<stop stop-color="#8dd6ff" offset="0.56"/>
|
||||
<stop stop-color="#9cd1ff" offset="1"/>
|
||||
</linearGradient>
|
||||
<linearGradient y2="0.5" x2="1" y1="0.5" x1="0" data-name="未命名的渐变 6" id="_未命名的渐变_6">
|
||||
<stop stop-color="#64a2ff" offset="0"/>
|
||||
<stop stop-color="#5cbfff" offset="1"/>
|
||||
</linearGradient>
|
||||
<linearGradient y2="0.5" x2="1" y1="0.5" x1="0" data-name="未命名的渐变 7" id="_未命名的渐变_7">
|
||||
<stop stop-color="#64ccff" offset="0"/>
|
||||
<stop stop-color="#84c9ff" offset="1"/>
|
||||
</linearGradient>
|
||||
<linearGradient xlink:href="#_未命名的渐变_7" y2="548.5" x2="837" y1="548.5" x1="831.98" data-name="未命名的渐变 7" id="_未命名的渐变_7-2"/>
|
||||
<linearGradient xlink:href="#_未命名的渐变_7" y2="569.39" x2="846.98" y1="569.39" data-name="未命名的渐变 7" id="_未命名的渐变_7-3"/>
|
||||
<linearGradient y2="0.5" x2="1" y1="0.5" x1="0" data-name="未命名的渐变 8" id="_未命名的渐变_8">
|
||||
<stop stop-color="#84c9ff" offset="0.01"/>
|
||||
<stop stop-color="#b1ccff" offset="1"/>
|
||||
</linearGradient>
|
||||
<linearGradient y2="0.5" x2="4.66625" y1="0.5" x1="4.61625" data-name="未命名的渐变 55" id="_未命名的渐变_55">
|
||||
<stop stop-color="#b1c9ff" offset="0"/>
|
||||
<stop stop-color="#b1ccff" offset="0.42"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<g>
|
||||
<title>Layer 1</title>
|
||||
<g data-name="图层 3" id="_图层_3">
|
||||
<path id="svg_1" fill="#7a9ec2" stroke-width="0px" d="m74.07715,375.0022c11.04,8.56 30.35,0.82 33,0c14.37,-4.43 15.38,-15.33 26,-15c10.44,0.33 13.16,10.98 24,10c6.48,-0.59 7.04,-4.53 16,-6c8,-1.31 8.66,1.66 17,1c12.76,-1.01 15.73,-8.33 24,-6c8.42,4.47 11.58,6.51 22,5c0,0 11.3,-1.75 32,-10c1.42,-9.07 4.72,6.12 11.99,12c10.1,9.09 19.95,-0.4 31,8c10.86,8.26 13.5,26.66 13,31c-1.65,4.39 6.37,10.16 6,10c-4.41,-0.77 -20.72,-10.82 -25,-11c-6.8,-1.26 -15.53,-2.26 -24,-5c-17.1,-5.24 -15.17,-10.82 -34,-10c-7.53,0.04 -6.69,-0.47 -12,1c-2.54,-0.23 -9.74,0.73 -13,-1c-18.29,-11.87 -9.94,11.78 -36,3c-9.47,1.86 -14.72,0.65 -18.02,-1.12c-2.21,-1.19 -4.61,-5.33 -6.74,-4.61c-0.57,0.19 -0.74,0.58 -2.24,1.74c-1.2,0.93 -2.27,1.58 -3,2c-0.91,0.36 -1.91,0.7 -3,1c-2.93,0.8 -5.59,1.02 -7.75,1.02c-0.74,0 -1.87,-0.01 -3.25,-0.02c-3.97,0 -3.61,0.03 -4,0c-2.87,-0.21 -3.75,-0.93 -7,-1c-0.42,0 -0.77,0 -1,0l-6,0c-3,0.22 -5.39,0.65 -7,1c-1.72,0.37 -3.36,0.82 -10,3c-3.07,1.01 -4.59,1.59 -6,2c-1.81,0.53 -5.13,1.49 -9,2c-5.07,0.67 -9,0.3 -12,0c-6.54,-0.65 -11.56,-2.18 -14,-3c-4.6,-1.55 -26.12,-13.37 -20,-10c0,0 0,0 0,0c-0.03,-0.02 -20.88,-13.37 -28,-32c-1.09,-2.85 -2,-6 -2,-6c-0.52,-1.8 -1.44,-5.03 -2,-9c-0.44,-3.12 -1.34,-9.53 0,-10c1.69,-0.59 6.23,8.51 7.27,10.48c5.15,9.76 18.12,22.54 52.73,35.52l0.01,-0.01z" class="cls-10"/>
|
||||
<path id="svg_2" fill="#7a9ec2" stroke-width="0px" d="m260.07715,284.0022c-13.63,7.43 -2.93,57.29 2,57c1.82,-0.11 1.13,-6.97 8,-16c3,-3.94 3.81,-3.37 5,-6c0.88,-3.8 2.06,-11.66 -2,-19c-0.61,-1.11 -1.29,-2.11 -2,-3c-0.31,-0.36 -0.8,-1.04 -1,-2c-0.06,-0.32 -0.19,-0.91 0,-1c0.26,-0.13 0.82,0.87 2,2c0.84,0.81 1.71,1.61 3,2c1.06,0.32 1.94,0.17 3,0c1.38,-0.23 2.37,-0.69 3,-1c1.31,-0.64 2.25,-1.09 3,-2c1.84,-2.22 1.14,-5.41 1,-6c-0.33,-1.36 -0.69,-1.29 -2,-4c-0.87,-1.8 -0.57,-1.55 -2,-5c-0.49,-1.19 -1,-2.79 -2,-6c-0.12,-0.39 -0.37,-1.22 0,-2c0,0 0.29,-0.6 1,-1c1.98,-1.11 11.6,0.55 15,1c2.66,0.35 13.92,1.61 15.83,2.95c0.42,0.3 0.8,0.64 1.17,1.05c2.43,2.73 0.93,4.31 3,9c3.83,8.69 13.16,12.57 19,15c5.09,2.12 8.54,2.5 13,3c6.22,0.69 7.53,-0.22 8,-1c0.74,-1.23 -0.27,-2.7 -1,-5c-1.53,-4.78 -0.3,-8.09 0,-12c0.51,-6.64 -1.77,-13.74 -4,-17c-9.26,-13.54 -41.29,4.77 -71,-2c-1.28,-0.29 -9.89,-2.3 -12,1c-1.87,2.94 2.99,7.55 1,12c-1.49,3.33 -5.5,3.63 -8,5z" class="cls-10"/>
|
||||
<path id="svg_3" fill="#7a9ec2" stroke-width="0px" d="m310.07715,118.0022c-22.42,19.49 74.7,203.11 32,245c-9.08,8.91 -26.9,13.94 -28,28c-0.65,8.33 4.78,18.3 13,23c13.88,7.94 28.83,2.05 33,-1c2.68,-1.96 4,-4 4,-4c1.44,-2.22 1.56,-3.91 3,-8c0.76,-2.17 1.5,-3.9 2,-5c0.43,-1.13 0.95,-2.86 1,-5c0.04,-1.88 -0.32,-2.29 -0.25,-4.13c0.04,-1.01 0.22,-5.22 2.25,-5.87c0.27,-0.09 1.3,-0.35 3,1c-0.17,-0.41 -0.33,-0.87 -0.21,-0.95c0.24,-0.16 1.16,1.42 2.21,1.95c2.87,1.44 7.56,-0.07 8,-2c-10.79,-5.85 -14.4,-6.84 -15,-6c-0.79,1.11 3.49,5.56 3,6c-0.37,0.33 -3.05,-1.93 -3,-2c0.05,-0.07 3.08,1.97 4,3c0.08,0.09 0.44,0.51 1,1c0.23,0.2 0.43,0.36 0.55,0.46c0.1,0.12 0.26,0.31 0.45,0.54c2.9,3.62 4.2,7.01 4.62,8.05c0.92,2.28 2.67,5.77 6.38,10.95c15.05,16.54 34.83,19.18 54,9c16.33,3.81 7.85,10.54 33,5c25.9,-4.5 50.39,1.98 49,-25c-1.26,-10.66 -5.86,-19.02 0,-10c8.68,14.17 21.69,39.51 43,27c1.34,-1.14 2.88,-2.66 4.33,-4.64c2.87,-3.92 4.1,-7.81 4.67,-10.36c0.04,-0.43 0.28,-2.3 1.9,-3.7c1.23,-1.06 2.58,-1.26 3.1,-1.3c0.01,0.08 0.16,1 -0.41,1.5c-0.6,0.53 -1.8,0.43 -2.59,-0.5c0.43,2.03 1.75,6.85 6,11c1.06,1.04 7.1,6.7 16,6c9.28,-0.73 15.02,-7.84 17,-12c1.58,-3.32 2,-7 2,-7c0.25,-2.19 0.1,-3.79 0,-5c-0.36,-4.65 0,-9.33 0,-14l0,-27c0.04,-1.66 0.03,-8.44 0,-22c0,0 -0.42,-5.41 -3,-9c-0.96,-1.33 -2,-2 -2,-2c-0.29,-0.19 -0.31,-0.16 -2,-1l-2,-1l-2,-1c-0.51,-0.2 -1.22,-0.52 -2,-1c-4.12,-2.53 -6.05,-6.97 -6,-7c0.05,-0.03 2.59,3.37 2,4c-0.16,0.18 -0.53,0.07 -1,0c-1.2,-0.18 -1.99,-1.02 -2,-1c-0.02,0.03 0.5,0.41 3,2c0.12,-0.38 0.34,-0.98 0.76,-1.61c1.31,-1.93 3.18,-2.14 5.24,-3.39c1.95,-1.18 3.03,-2.66 4,-4c0.61,-0.85 2.43,-3.41 3,-7c0.69,-4.36 -0.85,-7.8 -1.68,-9.65c-3.9,-8.74 -12.88,-12.43 -15.32,-13.35c-0.13,0.06 -1.05,0.47 -2,0c-0.61,-0.3 -0.9,-0.8 -1,-1c-0.59,-2.25 -2.17,-3.18 -3,-6c-1.01,-2.71 -2.29,-10.67 -2,-12c0.57,-5.41 2.39,-8.84 4,-11c2.1,-2.82 3.85,-3.49 6,-7c1.06,-1.73 2.73,-4.47 3,-8c0.59,-7.63 -5.74,-13.78 -7,-15c-2.59,-2.51 -5.28,-3.88 -7,-6c-0.29,-0.36 -1.32,-1.69 -3.17,-2.63c-0.4,-0.2 -0.68,-0.31 -0.83,-0.37c-2.54,-1.08 -3.91,-5.69 -4,-6c-1.92,-6.71 -2.5,-10.84 -4,-16c-1.04,-3.56 -1.16,-2.94 -2,-6c-1.68,-6.11 -1.81,-10.75 -2,-14c-0.59,-9.95 -1.18,-14.9 -3,-20c0,0 -2.12,-5.94 -7,-12c-15.98,-19.87 -78.67,-21.15 -79,-18c-0.19,1.79 19.52,6.96 19,9c-0.13,0.51 -1.4,1.23 -3.15,3.43c0,0 0,0 0,0c-2.67,4.24 -1.37,8.35 -7.85,14.57c-10.42,10 -22.57,7.8 -34,13c-5.27,2.29 -9.81,3.41 -13,4c-5.08,0.94 -7.67,1.39 -10,0c-2.43,-1.45 -1.99,-3.22 -6,-8c-0.59,-0.71 -2.4,-2.82 -5,-5c-4.84,-4.06 -6.07,-3.05 -9,-6c-1.97,-1.98 -1.39,-2.41 -6,-10c-2.23,-3.66 -3.34,-5.49 -4,-6c-3.01,-2.31 -5.67,-1.38 -15,-3c-2.88,-0.5 -2.43,-0.55 -5,-1c-11.13,-1.94 -15.79,-0.28 -26,-2c-1.7,-0.29 -4.35,-0.8 -7.57,-1.74c-7.85,-9.54 -30.21,6.06 -40.43,13.74z" class="cls-10"/>
|
||||
<path id="svg_4" fill="#7a9ec2" stroke-width="0px" d="m294.07715,64.0022c-4.66,0.36 -8.11,1.64 -10.79,3.57c-12.46,8.98 -7.88,22.29 -21.35,37.56c-8.93,10.12 -22.26,16.35 -26.86,15.87c-1.25,-0.13 -5.27,-1.1 -8,1c-0.59,0.45 -1.25,1.15 -2,1c-1.15,-0.23 -1.71,-2.29 -2,-4c0,0 1,-2 1,-2c2.92,-5.84 2.97,-5.95 3,-6c2,-4.02 5.56,-11.16 5,-17c-0.84,-8.77 -11.08,-15.9 -20,-18c-12.28,-2.89 -22.17,3.73 -24,5c-12.11,2.27 -15.97,0.27 -17.33,-1.85c-1.94,-3.02 0.84,-6.86 5.33,-19.16c3.86,-10.58 5.89,-16.13 6,-22c0.1,-5.49 -1.43,-13.73 -10,-24c-23.21,-14.8 -47,-13.43 -56,-2c-1.46,1.85 -5.64,7.92 -4,20c-3.3,4.02 -9.31,1.14 -20,4c-10.63,1.96 -20.69,9.46 -13,23c5.63,9.91 94.53,145.36 192.09,115.49c50.17,-15.36 72.52,-65.2 72.91,-66.49c0.03,-0.1 0.51,-1.68 0,-2c-0.15,-0.1 -0.3,-0.03 -1,0l-3,0c-1.55,0 -3.44,-0.77 -4,-1c-0.06,-0.02 -0.12,-0.05 -0.2,-0.08c-1.67,-0.72 -4.91,-2.37 -4.8,-2.92c0.11,-0.56 3.88,-0.54 16,1c-0.74,-0.29 -1.76,-0.66 -3,-1c-1.48,-0.41 -3.48,-0.96 -6,-1c-2.59,-0.05 -2.83,0.27 -5,0c-0.92,-0.12 -2.41,-0.3 -4,-1c-4.1,-1.8 -5.85,-5.64 -6,-6c-0.77,-1.78 -0.86,-3.52 -1,-7c-0.1,-2.49 0.1,-2.7 0,-5c-0.14,-3.15 -0.22,-5.06 -1,-7c-1.47,-3.67 -4.66,-5.59 -7,-7c-2.96,-1.78 -5.54,-2.42 -8,-3c-2.73,-0.65 -6.85,-1.32 -12,-1l0,0.01z" class="cls-10"/>
|
||||
</g>
|
||||
<g data-name="背景" id="_背景">
|
||||
<path id="svg_5" fill="#666" stroke-width="0px" d="m199.07715,72.0022c-16.42,-3.08 -26.21,8.1 -31,4c-7.63,-6.54 18.33,-33.97 9,-55c-6.88,-15.51 -30.12,-21.13 -45,-21c-3.33,0.03 -16.49,0.44 -31,9c-30.76,18.15 -37.62,55.27 -56,109c-20.03,58.55 -43.71,77.3 -45,119c-0.4,13.04 0.88,31.37 1,33c2.91,40.64 9.7,47.97 12,64c0.27,1.85 0.98,7.34 5,12c0.83,0.96 1.59,1.64 2.01,2c0.47,0.48 1.15,1.18 1.99,2c10.84,10.63 27.33,20.18 27.33,20.18c17.89,10.12 26.84,15.18 35.66,13.82c3.19,-0.49 6.88,-1.68 16.62,-3.27c3.46,-0.56 6.46,-0.79 12.39,-1.73c3.95,-0.63 7.36,-0.89 10,-1c3.64,-0.15 6.36,-0.03 7,0c4.66,0.22 8.01,0.84 9,1c3.81,0.61 10.14,0.32 21,-4c3.96,0.13 4.67,2.49 9,4c4.59,1.6 5.73,-0.37 15,0c16.7,2.72 12.4,-0.95 25,-4c5.48,-0.56 5.78,1.97 14,3c9.86,2.45 36.35,-8.51 48,1c6.44,5.27 9.1,4.24 16,6c7.05,3.78 15.85,4.35 23,8c18.2,11.17 34.14,20.96 51,2c5.59,-6.98 3.83,-12.43 8,-14c7.13,-2.81 31.59,30.21 58,23c15.36,-6.57 13.31,-4.51 28,1c5.68,1.04 23.32,-3.84 33,-4c12.6,-0.87 15.51,0.92 21,-2c11.5,-6.32 8.35,-12.82 9,-21c6.52,3 17.82,31.94 43,18c6.28,-4.14 7.41,-10.27 11,-10c3.81,0.29 3.43,7.28 9,11c7.22,4.82 20.84,1.79 26,-6c1.88,-2.84 1.95,-5.28 2,-16c0.06,-12.86 2.5,-34.29 1,-57c-0.27,-4.14 -0.62,-6.12 -2,-8c-1.81,-2.47 -4.68,-3.81 -7.73,-8.57c-0.11,-0.18 -0.21,-0.33 -0.27,-0.43c0,-0.08 -0.02,-0.19 -0.02,-0.33c0.03,-1.6 1.77,-2.75 2.36,-3.17c1.19,-0.85 2.8,-2.4 4.66,-5.5c10.77,-33.99 -34.41,-21.61 -20,-59c3.12,-6.89 6.55,-6.37 8,-12c2.32,-9.04 -4.5,-18.26 -8,-23c-5.99,-8.1 -8.37,-6.45 -17,-17c-2.93,-3.58 -6.84,-8.36 -9.5,-15.5c-2.32,-6.24 -2.48,-11.05 -2.5,-12.5c0,-0.42 0,-0.77 0,-1c0.13,-4.73 -0.28,-14.34 -6,-24c-7.76,-13.12 -28.18,-18.94 -40,-22c-15.86,-4.11 -39.47,-10.22 -51,1c-3.37,3.28 -5.98,8.31 -13,11c-5.03,1.93 -10.55,1.97 -14,2c-4.92,0.04 -8.59,-0.58 -11,-1c-1.86,-0.32 -10.82,-1.96 -17,-6c-0.12,-0.08 -0.17,-0.11 -0.32,-0.21c-0.99,-0.63 -3.29,-1.88 -9.18,-3.72c-18.42,-5.77 -40.67,-7.51 -55.5,-8.07c-2.82,-0.11 -3.62,-0.51 -8,-1c-2.91,-0.33 -4.55,-0.47 -7,-1c-3.02,-0.65 -4.54,-0.98 -6,-2c-2.14,-1.49 -3.17,-3.66 -4,-6c-3.13,-8.82 -0.7,-12.16 -3,-17c-2.84,-5.97 -10.15,-7.96 -14,-9c-2.47,-0.67 -11.2,-2.68 -20,2c-1.88,1 -6.08,3.3 -9,8c-2.37,3.82 -1.56,5.46 -4,14c-2.08,7.28 -3.18,10.97 -6,13c-1.17,0.85 -5.1,3.27 -13,1c-3.07,-0.18 -7.45,0 -12,2c-8.86,3.88 -12,11.82 -14,11c-2.79,-1.15 5.52,-15.64 0,-28c-5.58,-12.5 -22.22,-15.85 -23,-16z" class="cls-6"/>
|
||||
<g data-name="主体" id="_主体">
|
||||
<path fill="url(#_未命名的渐变_11)" stroke-width="0px" d="m143.07715,31.0022c-0.07,-9.92 -10.48,-14.93 -19.79,-10.14c-26.5,20.43 -60.22,177.47 -46.22,199.15l0,0c0.82,1.48 2.09,3.31 4,5c0.71,0.63 3.63,3.11 8.06,4.22c0.06,0.02 0.12,0.03 0.15,0.04c0,0 0.25,0 0.25,0c6.98,0.1 42.56,0.64 49.53,0.74c8.08,0 14.43,-3.25 15.96,-8.03c1.6,-4.98 -2.25,-10.37 -3.96,-11.97c-10.75,-10.03 -32.41,-2.79 -46.53,-8.99c1.03,-45.36 25.88,-124.06 38.53,-170.01l0.02,-0.01z" class="cls-3" id="lite-l"/>
|
||||
<path fill="url(#_未命名的渐变_11-2)" stroke-width="0px" d="m196.07715,130.0022c4.11,0.31 6.91,2.96 8,4c6.83,6.48 6.93,17.68 7,26c0,0.06 0,0.03 0,7c0,14.32 0,21.48 0,22c0.04,13.27 0.66,35.34 -4,42c-1.29,1.84 -3,3 -3,3c-2.97,2.01 -6.17,1.99 -8,2c-1.45,0 -7.74,-0.12 -13,-5c-6.34,-5.87 -6.06,-13.87 -6,-15c0,-0.07 0,-9.93 0,-10c0,-38.04 0,-37.85 0,-38c-0.02,-1.29 -0.01,-3.53 0,-8c0.03,-9.9 0.15,-12.06 1,-15c1.28,-4.42 3.15,-6.95 4,-8c1.1,-1.36 2.19,-2.34 3,-3c0.7,-0.58 1.7,-1.32 3,-2c0.98,-0.52 4.28,-2.28 8,-2z" class="cls-16" id="lite-i-1"/>
|
||||
<path fill="url(#_未命名的渐变_11-3)" stroke-width="0px" d="m193.85715,86.0722c8.85,-0.96 18.71,7.52 18.2,17.09c-0.5,9.28 -10.62,16.63 -19.33,14.46c-6.93,-1.73 -13.7,-9.65 -12.51,-18.4c1.06,-7.79 7.93,-12.52 13.65,-13.14l-0.01,-0.01z" class="cls-31" id="lite-i-1-1"/>
|
||||
<path fill="url(#_未命名的渐变_11-4)" stroke-width="0px" d="m270.07715,109.0022c-5.46,4.9 -13.26,-2.45 -24,3c-1.76,0.89 -7.94,4.02 -9,10c-0.32,7.28 1.09,10.38 7,14c1.09,0.46 1.2,0.26 6,1c5.06,0.78 4.17,0.82 6,1c2.65,0.64 5.81,0.21 8,2c-1.37,3.47 -2.84,7.85 -4,13c-1.54,6.84 -1.9,12.21 -2,14c-0.55,9.95 0.87,18.04 1.98,22.78c2.44,10.5 5.56,15.48 9.02,22.22c2.16,4.21 19.54,19.38 30,8c4.48,-5.11 2.13,-12.61 2,-13c-1.5,-4.54 -4.19,-4.73 -8,-10c-2.14,-2.97 -3.9,-6.54 -6,-15c0,0 -3.62,-14.55 -1,-31c0.07,-0.45 0.7,-4.15 3,-7c0.34,-0.42 0.64,-0.72 1,-1c2.08,-1.58 4.79,-1.16 6,-1c0,0 2.62,0.34 11,0c2.39,-0.1 5.82,-0.51 10,-2c1.2,-0.76 4.96,-3.37 6,-8c1.27,-5.69 -2.25,-11.34 -6,-14c-1.62,-1.15 -7.64,-4.84 -14,-5c-0.75,-0.02 -2.43,-0.01 -3,-1c-0.71,-1.24 0.71,-3.39 2,-5c17.38,-27.36 -15.06,-42.63 -26,-19c-5.75,12.08 -1.75,17.19 -6,21z" class="cls-4" id="lite-t"/>
|
||||
<path fill="url(#_未命名的渐变_11-5)" stroke-width="0px" d="m362.07715,166.0022c-0.58,0.39 -5.22,3.65 -5,8c0.19,3.92 4.21,6.49 5,7c5.42,3.48 9.31,0.51 25,-1c10.15,-0.98 16.08,-0.47 21,-5c1.85,-1.71 2.74,-3.47 3,-4c2.71,-5.47 0.88,-11.23 0,-14c-3.43,-10.82 -12.5,-16.72 -16,-19c-7.86,-5.11 -15.39,-6.53 -19,-7c-4.34,-0.56 -13.27,-1.6 -23,3c-9.69,4.58 -14.55,12.09 -17,16c-1.5,2.4 -5.98,10.02 -7,21c-0.52,5.6 -1.49,16.1 5,26c5.1,7.79 12.21,11.23 16,13c9.03,4.23 17,4.1 23,4c9.99,-0.17 16.58,-2.61 23,-5c2,-0.74 5.17,-2.02 8,-5c1.68,-1.77 4.08,-4.29 4,-8c-0.07,-3.36 -2.16,-6.53 -5,-8c-3.53,-1.83 -7.16,-0.62 -9,0c-1.16,0.39 -1.55,0.69 -4,2c-3.13,1.68 -6.56,3.38 -8,4c-6.18,2.67 -12.7,1.44 -15,1c-3.93,-0.74 -9.92,-1.88 -14,-7c-2.99,-3.76 -3.69,-8.07 -4,-10c-0.23,-1.41 -0.55,-4.21 -0.03,-7.67c0.24,-1.6 0.98,-6.11 4.03,-10.33c0.37,-0.52 7.1,-9.49 18,-9c1.43,0.06 9.65,0.59 14,7c1.16,1.71 2.94,4.33 2,6c-2.22,3.93 -15.76,-4.29 -25,2z" class="cls-8" id="lite-e"/>
|
||||
<path fill="url(#_未命名的渐变_240)" stroke-width="0px" d="m502.07715,162.0022l13.33,22l9.67,-15l-8,-20l4,-6l6,2l9,13l7,-11l9,1l3,7l-11,13l17,2l4,8l-5,4l-22,-4l-13.5,16.8l27.5,5.2l11,-14l7,1l3,7l-9,11l19,3l4,7l-4,5l-18,-3l7,14l-4,4l-8,-1l-10,-20l-40,-8l-2,-2l-20,-37l-23,-4l-3,-7l4,-5l16,3l-9,-15l4,-6l7,1l9,16l9,-10l7,2l3,6l-10,14z" class="cls-20" id="logo-snow"/>
|
||||
<path fill="url(#_未命名的渐变_9)" stroke-width="0px" d="m344.07715,316.0022c-5.28,-2.41 -12.58,-6.43 -20,-13c-8.2,-7.27 -12.49,-14.08 -18,-23c-8.92,-14.43 -16.4,-26.53 -12,-35c2.45,-4.71 7.07,-6.66 7.07,-6.66c2.53,-1.08 5.74,-1.4 9.93,-0.34c15.19,5.99 14.64,39.18 45,55c8.67,-18.92 10.28,-33.18 10,-43c-0.12,-4.37 -0.74,-12.15 4,-16c0.82,-0.55 5.22,-3.4 10.85,-2.15c3.86,0.85 6.25,3.17 7.15,4.15c0.5,0.6 1.28,1.58 2.09,2.87c8.99,14.33 0.03,32.06 -11.49,69.45c-4.1,13.3 -6.65,21.58 -9.6,33.68c-5.81,23.86 -8.17,40.38 -20,51c-11.03,9.91 -22.32,0.03 -23,-9c-2.89,-15.02 23.13,-56.98 18,-68z" class="cls-27" id="yuki-y"/>
|
||||
<path fill="url(#_未命名的渐变_9-2)" stroke-width="0px" d="m403.07715,323.0022c-0.54,-0.11 -5.36,-1.02 -9,2c-2.31,1.92 -3.04,4.55 -4,8c-0.74,2.66 -0.91,4.8 -1,6c-0.59,8.25 0,15 0,15c1.18,13.55 1.77,20.33 3,23c1.04,2.27 4.07,8.71 11,13c6.21,3.85 12.31,3.91 21,4c0.81,0 16.83,0.09 21,-4c0.22,-0.21 1.2,-1.26 2,-1c0.93,0.3 0.69,2.05 2,4c0,0 0.63,0.93 2,2c3.54,2.75 12.53,3.41 16,-1c0.62,-0.79 1.75,-2.64 2,-5c0.07,-0.68 0.05,-1.22 0.02,-1.76c-0.13,-2.75 -0.88,-4.83 -1.02,-5.24c-1.31,-3.73 -3.37,-15.69 -4,-23c-0.58,-6.71 0.42,-5.3 0,-19c-0.11,-3.63 -0.12,-9.19 -3,-15c-1.36,-2.75 -2.63,-4.09 -4,-5c-2.95,-1.96 -6.82,-2.22 -10,-1c-0.8,0.31 -3.48,1.34 -5,4c-1.36,2.37 -1.11,4.83 -1,6c0.66,6.98 0.45,14.01 1,21c0.72,9.13 0.57,11.51 -1,14c-1.8,2.86 -4.42,4.2 -6,5c-7.58,3.86 -15,0.82 -17,0c-2.54,-1.04 -3.49,-2.11 -4,-3c-0.74,-1.29 -0.88,-2.78 -1,-4c-0.13,-1.35 -0.05,-2.34 0,-3l0,-16c-0.19,-7.44 -0.28,-11.16 -2,-14c-0.52,-0.85 -3.02,-4.98 -8,-6z" class="cls-21" id="yuki-u"/>
|
||||
<path fill="url(#_未命名的渐变_9-3)" stroke-width="0px" d="m591.07715,272.0022c-6.65,0.3 -12.55,6.12 -12,13c0.53,6.56 6.7,11.28 13,11c6.65,-0.3 12.55,-6.12 12,-13c-0.53,-6.56 -6.7,-11.28 -13,-11z" class="cls-2" id="yuki-i-1-1"/>
|
||||
<path fill="url(#_未命名的渐变_9-4)" stroke-width="0px" d="m591.07715,303.0022c-15.96,1.04 -13.95,25.54 -13,36c1.45,14.24 -7.18,66.6 24,48c5.91,-7.41 -0.33,-25.41 1,-45c-2.16,-11.6 6.6,-38.49 -12,-39z" class="cls-23" id="yuki-i-1"/>
|
||||
<path fill="url(#_未命名的渐变_9-5)" stroke-width="0px" d="m545.07715,299.0022c-0.26,-0.11 -0.66,-0.27 -1.17,-0.44c0,0 -4.46,-1.53 -9.91,-1c-4.47,0.44 -18.59,11.34 -31.93,31.43c-0.35,-10.53 -0.23,-21.87 0.56,-33.9c0.37,-5.55 0.85,-10.92 1.44,-16.1c0.26,-1.2 0.52,-2.94 0.41,-5.03c-0.09,-1.81 -0.25,-4.96 -2.27,-8.04c-0.56,-0.85 -2.9,-4.42 -7.52,-5.51c-5.61,-1.33 -10.01,2.08 -10.62,2.58c-3.44,2.76 -4.45,6.67 -5,9c-3.6,15.32 -3,27 -3,27c0.42,8.15 1.67,32.57 1,50c-0.22,5.66 0.05,11.33 0,17c-0.05,6.02 -0.13,9.05 1,13c1.45,5.07 4.08,8.75 6,11c1.19,1.11 3.93,3.36 8,4c3.99,0.63 7.79,-0.51 9.84,-1.85c11.06,-7.21 1.65,-39.59 4.55,-40.56c0.39,-0.13 1.19,0.88 2.61,3.4c8.89,11.22 14.99,15.39 30,29c7.7,10.13 23.45,3.59 22,-8c-0.19,-16.59 -39.62,-33.08 -31.91,-39.47c1.89,-2.38 6,-8.21 6.91,-9.53c5.47,-7.8 10.83,-8.44 13,-15c1.34,-4.04 0.69,-6.57 0.43,-7.38c-1.01,-3.15 -3.39,-4.94 -4.43,-5.62l0.01,0.02z" class="cls-28" id="yuki-k"/>
|
||||
<path fill="url(#_未命名的渐变_12)" stroke-width="0px" d="m79.07715,274.0022c-1.75,-1 -2.61,-0.17 -10,1c-2.22,0.35 -7.11,1.06 -13,3c-1.36,0.45 -2.68,0.93 -3,2c-0.49,1.65 1.56,4.27 4,5c0.32,0.09 0.56,0.13 0.74,0.16c2.19,0.34 3.89,-0.56 5.26,-1.16c1.96,-0.87 3.64,-1.24 7,-2c0,0 4.44,-1 5,0c0.02,0.04 0.04,0.07 0.04,0.07c0.07,0.18 -0.02,0.5 -0.04,2.93c0,0.06 0,0.71 0,2c0,5.01 0.09,5.71 0,7c-0.2,2.74 -0.3,4.11 -1,5c-1.25,1.58 -3.23,1.52 -7,2c-5.62,0.72 -4.41,1.27 -10,2c-7.19,0.94 -9.49,0.07 -11,2c-1.32,1.68 -1.32,4.57 0,6c1.74,1.88 5.46,0.88 9,0c7.35,-1.82 19.55,-4.84 29,-6c4.81,-0.59 12,-1.09 13,-4c0.57,-1.65 0,-3 0,-3c-0.06,-0.14 -0.62,-1.43 -2,-2c-1.19,-0.49 -2.36,-0.18 -3,0c-6.78,1.91 -9.23,2.82 -10,2c-0.45,-0.48 -0.17,-1.39 0,-2c1.77,-6.29 1.22,-12.36 1,-14c-0.23,-1.67 -0.84,-6.18 -4,-8z" class="cls-24" id="lite-jp-1"/>
|
||||
<path fill="url(#_未命名的渐变_12-2)" stroke-width="0px" d="m75.07715,330.0022c-1.02,0.34 -1.72,1.18 -2,2c-0.31,0.93 -0.06,1.78 0,2c0.38,1.39 0.25,7.26 0,19c-0.12,5.74 -0.36,7.73 0,12c0.21,2.49 0.58,5.11 2,8c0.87,1.76 1.83,3.72 4,5c2.08,1.23 4.09,1.14 7,1c2.69,-0.13 6.51,-0.31 7,-2c0.37,-1.27 -1.18,-3.23 -3,-4c-2.78,-1.18 -4.84,0.97 -7,0c-2.17,-0.97 -2.69,-4.29 -3,-7c-1.07,-9.45 -0.9,-24.04 -1,-30c-0.02,-1.36 -0.12,-5.21 -2,-6c-0.58,-0.25 -1.37,-0.21 -2,0z" class="cls-14" id="liteecho-1"/>
|
||||
<path fill="url(#_未命名的渐变_72)" stroke-width="0px" d="m263.13715,280.9522c-2.11,0.2 -3.55,1.9 -3.93,2.35c-0.57,0.68 -0.91,1.34 -1.11,1.8c1.09,1.05 6.06,5.64 11.22,4.91c2.33,-0.33 5.1,-1.81 5.34,-2.63c0.03,-0.09 0.03,-0.17 0.03,-0.17c0,-0.29 -0.33,-0.71 -1.89,-1.83c-1.33,-0.96 -2.23,-1.61 -3.3,-2.18c-0.69,-0.37 -1.23,-0.6 -1.68,-0.79c-2.41,-1.04 -3.62,-1.55 -4.68,-1.45l0,-0.01z" class="cls-17" id="yuki-jp-4-1"/>
|
||||
<path fill="url(#_未命名的渐变_58)" stroke-width="0px" d="m248.07715,265.0022c0.37,-0.49 1.34,-1.8 3,-2c2.33,-0.28 3.93,1.89 4,2c0.33,0.46 0.44,0.81 1,3c0.59,2.3 0.88,3.45 1,4c0.33,1.56 0.71,3.58 1,6c0.21,1.71 0.24,2.18 0.42,3.8c0.24,2.17 0.38,2.81 1.58,10.2c0.66,4.08 0.73,4.55 1,6c0.31,1.64 0.58,3.06 1,5c0.77,3.51 1.13,4.83 1,7c-0.08,1.29 -0.29,2.25 -1,3c-1,1.07 -2.67,1.39 -4,1c-1.46,-0.43 -2.18,-1.63 -3,-3c-0.55,-0.91 -1.1,-2.1 -2,-6c-0.28,-1.22 -0.65,-2.92 -1,-5c-0.33,-2 -0.67,-4 -1,-6c-0.53,-3.92 -1.07,-7.83 -1.6,-11.75c-0.47,-2.42 -0.93,-4.83 -1.4,-7.25c-0.78,-2.06 -0.97,-3.84 -1,-5c-0.05,-1.81 -0.12,-3.5 1,-5z" class="cls-13" id="yuki-jp-4"/>
|
||||
<path fill="url(#_未命名的渐变_13)" stroke-width="0px" d="m101.07715,341.0022c-1.86,-0.04 -3.59,1.27 -4,3c-0.56,2.38 1.47,5.01 4,5c2.03,-0.01 4,-1.72 4,-4c0,-2.25 -1.92,-3.96 -4,-4z" class="cls-19" id="liteecho-i-1-1"/>
|
||||
<path fill="url(#_未命名的渐变_13-2)" stroke-width="0px" d="m102.07715,354.0022c-0.61,-0.3 -1.38,-0.31 -2,0c-0.14,0.07 -0.62,0.33 -1,1c-1.18,2.08 -1.01,7.51 -1,8c0.16,5.45 0,9 0,9c-0.08,1.78 -0.22,3.7 1,5c0.21,0.22 0.94,1 2,1c1.08,0 1.81,-0.8 2,-1c1.05,-1.14 1.02,-2.72 1,-4c-0.03,-2.33 0.04,-4.67 0,-7c-0.05,-3.31 0.17,-8.95 -1,-11c-0.12,-0.2 -0.41,-0.71 -1,-1z" class="cls-29" id="liteecho-i-1"/>
|
||||
<path fill="url(#_未命名的渐变_65)" stroke-width="0px" d="m119.55715,342.6422c-0.52,0.03 -1.3,0.07 -1.92,0.55c-0.25,0.19 -0.76,0.67 -1.1,2.68c-0.15,0.92 -0.29,2.26 -0.14,3.91c-1.17,0.01 -3.52,0.16 -5.32,1.22c-0.38,0.22 -0.77,0.51 -1,1c-0.39,0.85 -0.08,1.91 0.41,2.59c0.74,1.01 1.99,1.19 3.59,1.41c0.85,0.12 1.57,0.12 2.05,0.1c-0.06,0.76 -0.12,1.85 -0.14,3.16c-0.01,1.22 -0.03,2.48 0.14,4.12c0.15,1.49 0.3,2.92 0.96,4.62c0.39,1.03 0.78,1.67 1,2c0.25,0.39 0.74,1.08 1.48,1.76c1.12,1.05 2.67,2.5 4.52,2.24c1.2,-0.17 2.57,-1.06 2.76,-2.24c0.12,-0.76 -0.29,-1.46 -0.55,-1.92c-0.43,-0.76 -0.87,-0.99 -1.51,-1.65c-0.95,-0.97 -1.44,-1.93 -1.78,-2.61c-0.71,-1.41 -0.9,-2.61 -1.1,-3.85c-0.15,-0.97 -0.37,-2.35 -0.14,-4.12c0.09,-0.67 0.22,-1.22 0.32,-1.61c0.56,0 1.35,-0.04 2.29,-0.17c1.54,-0.21 2.32,-0.32 2.88,-0.82c0.8,-0.71 1.27,-2.03 0.83,-3.01c-0.13,-0.3 -0.38,-0.61 -1,-1c-1.52,-0.94 -3.22,-1.02 -4.36,-0.94c0.11,-0.49 0.27,-1.25 0.41,-2.2c0.49,-3.18 0.14,-3.81 -0.14,-4.12c-0.26,-0.3 -0.59,-0.43 -1.24,-0.69c-0.4,-0.16 -1.18,-0.47 -2.2,-0.41z" class="cls-9" id="liteecho-t"/>
|
||||
<path fill="url(#_未命名的渐变_66)" stroke-width="0px" d="m142.48715,359.4922c-1.24,0 -1.82,0.11 -2.17,0.53c-0.63,0.76 -0.29,2.24 0.31,3.15c1.51,2.31 5.53,2.25 7.8,1.77c1.4,-0.3 4.49,-0.94 5.98,-3.77c1.7,-3.21 -0.04,-6.62 -0.4,-7.27c-1.86,-3.4 -5.47,-4.29 -7.14,-4.7c-3.37,-0.83 -6.16,0.05 -7.27,0.4c-1.07,0.34 -2.81,0.91 -4.53,2.4c-3.45,2.98 -3.92,7.09 -4,8c-0.27,3.05 0.7,5.35 1,6c0.27,0.6 1.17,2.42 3,4c1.04,0.89 2.16,1.53 3,2c0.99,0.56 1.75,0.89 2,1c1.02,0.44 1.88,0.81 3,1c1.17,0.2 2.07,0.1 3,0c0.34,-0.04 1.85,-0.22 4,-1c3.3,-1.2 4.59,-2.54 5,-3c0.47,-0.53 0.68,-0.9 0.71,-1.34c0.06,-1.15 -1.21,-2.31 -2.48,-2.62c-0.51,-0.12 -0.94,-0.08 -1.23,-0.05c-2.01,0.26 -3.04,1.28 -4.31,1.86c-2.59,1.19 -5.84,0.41 -7.69,-0.86c-1.16,-0.8 -1.95,-1.93 -2,-2c-0.26,-0.38 -0.69,-1.07 -1,-2c-0.26,-0.77 -0.95,-2.86 0,-5c0.82,-1.85 2.41,-2.69 3,-3c2.34,-1.23 5.06,-1.05 7,0c0.69,0.37 1.64,0.9 2,2c0.29,0.87 0.26,2.23 -0.52,2.84c-1,0.8 -2.36,-0.34 -6.07,-0.36l0.01,0.02z" class="cls-25" id="liteecho-e"/>
|
||||
<path fill="url(#_未命名的渐变_68)" stroke-width="0px" d="m197.07715,346.0022c-1.16,0 -4.12,0.04 -7,2c-1.54,1.05 -2.49,2.26 -3,3c-1.62,2.38 -1.87,4.7 -2,6c-0.12,1.26 -0.36,4 1,7c2.14,4.72 6.61,6.52 8,7c1.04,0.36 4.93,1.64 9,0c0.94,-0.38 2.74,-1.13 4,-3c0.45,-0.66 1.99,-2.94 1,-5c-0.54,-1.12 -1.88,-2.25 -3,-2c-1.41,0.32 -1.04,2.48 -3,4c-1.92,1.48 -4.45,1.09 -5,1c-0.54,-0.08 -2.42,-0.41 -4,-2c-2.4,-2.42 -2.04,-5.66 -2,-6c0.07,-0.51 0.43,-3.37 3,-5c2.25,-1.43 5.33,-1.38 7,0c0.93,0.77 0.92,1.55 2,2c1.26,0.52 3.2,0.27 4,-1c0.72,-1.15 0.17,-2.56 0,-3c-0.15,-0.38 -0.69,-1.63 -3,-3c-0.98,-0.58 -3.45,-2.01 -7,-2z" class="cls-30" id="liteecho-c"/>
|
||||
<path fill="url(#_未命名的渐变_71)" stroke-width="0px" d="m217.07715,329.0022c-0.92,-0.49 -2.08,-0.44 -3,0c-1.8,0.87 -1.99,2.91 -2,3l0,9c0,4 0.04,8 0,12c-0.03,3 0.1,6 0,9c-0.1,3.04 -0.29,5.38 1,8c0.45,0.91 1.42,2.89 3,3c1.63,0.11 2.9,-1.85 3,-2c1.44,-2.28 0.03,-4.1 0,-9c-0.02,-2.85 0,-5.82 2,-8c0.37,-0.4 1.8,-1.97 4,-2c2.44,-0.04 3.91,1.89 4,2c0.92,1.22 1,2.52 1,3c-0.33,1.67 -0.67,3.33 -1,5c-0.41,1.12 -0.91,2.85 -1,5c-0.09,2.3 -0.16,3.99 1,5c1.28,1.11 3.61,1.02 5,0c1.65,-1.21 1.8,-3.61 2,-7c0.08,-1.27 0.04,-2.31 0,-3l0,-8c-0.02,-0.84 -0.16,-2.36 -1,-4c-0.51,-1 -1.73,-2.93 -4,-4c-3.65,-1.72 -7.65,-0.14 -8,0c-1.5,0.62 -2.24,1.36 -3,1c-0.71,-0.34 -0.94,-1.4 -1,-4c-0.12,-5.04 0.61,-6.66 0,-10c-0.56,-3.06 -1.54,-3.76 -2,-4z" class="cls-18" id="liteecho-h"/>
|
||||
<path fill="url(#_未命名的渐变_58-2)" stroke-width="0px" d="m252.07715,348.0022c-4.27,-0.18 -7.17,2.27 -8,3c-3.05,2.66 -3.79,6 -4,7c-0.18,0.88 -1.21,5.84 2,10c2.08,2.69 4.82,3.62 6,4c1.29,0.42 4.46,1.39 8,0c2.95,-1.16 4.51,-3.31 5,-4c1.43,-2.02 1.81,-3.97 2,-5c0.46,-2.51 0.1,-4.49 0,-5c-0.67,-3.35 -2.52,-5.47 -3,-6c-0.69,-0.76 -3.46,-3.8 -8,-4z" class="cls-33" id="liteecho-o"/>
|
||||
<path fill="#666" stroke-width="0px" d="m250.57715,354.5022c-2.49,0.24 -3.75,2.55 -4,3c-0.09,0.17 -1.81,3.42 0,6c1.68,2.39 5.6,3.06 8,1c2.1,-1.8 2.13,-4.93 1,-7c-0.84,-1.54 -2.7,-3.22 -5,-3z" class="cls-6" id="liteecho-o-white"/>
|
||||
<path fill="url(#_未命名的渐变_14)" stroke-width="0px" d="m108.07715,274.0022c-0.71,0.63 -1.32,1.81 -1,3c0.38,1.44 1.86,1.95 2,2c0.77,0.25 1.44,0.12 2,0c4.88,-1.04 5.31,-1.31 6,-1c1.18,0.54 1.54,2.08 2,4c0.41,1.72 0.73,3.08 0,4c-0.65,0.81 -2.11,0.89 -5,1c-1.25,0.05 -2.29,0.03 -3,0c-0.53,0.14 -1.96,0.61 -3,2c-0.4,0.54 -1.21,1.61 -1,3c0.24,1.62 1.7,2.78 3,3c0.44,0.08 0.79,0.03 1,0c4.59,-0.65 6.89,-0.98 7,-1c2.55,-0.43 3.84,-0.64 4.45,-0.34c1.26,0.62 1.41,1.86 2.55,5.34c0,0 1.48,4.5 4,10c0.8,1.75 1.53,3.13 2,4c0.16,0.13 2.42,1.9 5,1c0.16,-0.06 2.44,-0.88 3,-3c0.21,-0.78 0.1,-1.42 0,-2c-0.27,-1.54 -0.96,-2.26 -2,-4c-0.12,-0.21 0.03,0.04 -2,-4c-0.67,-1.33 -1.32,-2.67 -2,-4c-1.91,-3.76 -2.3,-4.3 -2,-5c0.2,-0.46 0.49,-0.53 4,-2c1.13,-0.48 2.45,-1.03 4,-2c1.13,-0.71 1.71,-1.21 2,-2c0.06,-0.17 0.35,-1.03 0,-2c-0.48,-1.32 -1.88,-2.03 -3,-2c-0.31,0 -0.54,0.07 -0.66,0.11c-2.2,0.63 -4.77,1.67 -5.34,1.89c-2.21,0.88 -3.31,1.31 -4,1c-0.42,-0.19 -0.77,-0.54 -2,-4c-1.21,-3.42 -1.11,-3.77 -1,-4c0.42,-0.85 1.37,-0.79 6,-2c2.46,-0.64 3.57,-1.02 4,-2c0.3,-0.68 0.14,-1.39 0,-2c-0.09,-0.4 -0.54,-2.17 -2,-3c-1.17,-0.66 -2.4,-0.38 -4,0c-3.65,0.88 -4.88,2.55 -6,2c-0.55,-0.27 -1.15,-1.13 -1,-4c-0.42,-2 -0.76,-3 -1,-3l0,0c-0.76,-1.48 -2.4,-2.29 -4,-2c-1.51,0.27 -2.7,1.47 -3,3c-0.17,2.45 0.41,4.01 1,5c0.57,0.95 1.14,1.36 1,2c-0.24,1.14 -2.45,1.64 -4,2c-2.38,0.55 -3.11,0.2 -4,1z" class="cls-34" id="lite-jp-2"/>
|
||||
<path fill="url(#_未命名的渐变_5)" stroke-width="0px" d="m153.07715,286.0022c-0.46,1.12 -0.62,2.69 0,4c0.19,0.41 0.79,1.49 2,2c1.22,0.51 2.43,0.16 3,0c5,-1.43 7.51,-2.15 8,-2c4.42,1.34 3.64,20.08 7,22c2.14,1.22 4,1 4,1c1.43,-0.17 3.03,-0.97 3.02,-1c0,0 0,0 -0.02,0c0.27,-0.2 0.65,-0.52 1,-1c1.53,-2.12 0.85,-5.32 -1,-10c-3.3,-8.33 -6.24,-11.07 -5,-13c0.71,-1.1 1.84,-0.46 5.57,-2.36c2.51,-1.27 4.06,-2.06 4.43,-3.64c0.46,-1.98 -1.11,-4.46 -3,-5c-1.01,-0.29 -2.23,0.21 -4.65,1.22c-3.32,1.39 -3.58,2.02 -4.35,1.78c-2.25,-0.7 -0.9,-6.34 -4,-8c-1.22,-0.65 -2.84,-0.54 -4,0c-0.31,0.14 -1.56,0.73 -2,2c-0.24,0.7 -0.14,1.29 0,2c0.51,2.66 2.08,3.57 2,5.99c0,0 0,0 0,0c0.01,0 0.02,0 0.02,0c0.01,0.1 -10.37,-0.01 -12.02,4l0,0.01z" class="cls-5" id="yuki-jp-1"/>
|
||||
<path fill="url(#_未命名的渐变_69)" stroke-width="0px" d="m230.07715,276.0022c-0.23,-0.2 -0.95,-0.74 -2,-1c-0.58,-0.14 -1.39,-0.34 -2,0c-0.79,0.44 -0.86,1.6 -1,3c0,0 -0.62,6.23 -2,9c-0.43,0.86 -0.97,1.66 -0.97,1.66c-0.12,0.18 -0.24,0.35 -0.35,0.5c-0.2,0.27 -0.42,0.55 -0.68,0.84c-1.47,1.68 -5.93,4.2 -5.93,4.2c-3.23,1.82 -3.66,2.04 -4.07,2.8c-0.61,1.13 -0.9,2.9 0,4c0.8,0.97 2.17,0.99 3,1c0.7,0 3.05,-0.07 7.17,-3.17c1.64,-1.24 3.78,-2.89 5.83,-5.83c3.15,-4.53 3.72,-8.76 4,-11c0.52,-4.2 -0.4,-5.48 -1,-6z" class="cls-32" id="yuki-jp-2-3"/>
|
||||
<path fill="url(#_未命名的渐变_67)" stroke-width="0px" d="m167.88715,359.1822c-1.13,0.52 -1.6,0.87 -1.74,1.41c-0.25,0.96 0.69,2.15 1.62,2.72c2.35,1.44 5.96,-0.31 7.81,-1.71c1.14,-0.86 3.66,-2.76 3.81,-5.96c0.17,-3.63 -2.85,-5.97 -3.45,-6.41c-3.13,-2.29 -6.78,-1.55 -8.46,-1.22c-3.41,0.68 -5.55,2.66 -6.41,3.45c-0.83,0.76 -2.15,2.02 -3.08,4.1c-1.85,4.16 -0.53,8.09 -0.22,8.94c1.05,2.88 2.91,4.54 3.46,5c0.5,0.42 2.09,1.69 4.42,2.34c1.32,0.37 2.61,0.46 3.57,0.53c1.13,0.08 1.96,0.06 2.24,0.05c1.11,-0.04 2.04,-0.06 3.14,-0.37c1.14,-0.32 1.92,-0.79 2.71,-1.28c0.29,-0.18 1.58,-0.98 3.19,-2.61c2.48,-2.49 3.07,-4.25 3.25,-4.84c0.2,-0.68 0.24,-1.11 0.07,-1.51c-0.43,-1.07 -2.08,-1.58 -3.36,-1.31c-0.51,0.11 -0.88,0.32 -1.13,0.48c-1.71,1.09 -2.2,2.45 -3.11,3.52c-1.84,2.18 -5.11,2.86 -7.32,2.49c-1.39,-0.23 -2.59,-0.92 -2.66,-0.96c-0.4,-0.23 -1.08,-0.67 -1.76,-1.38c-0.56,-0.59 -2.08,-2.19 -2.13,-4.52c-0.04,-2.02 1.04,-3.46 1.44,-3.99c1.59,-2.11 4.14,-3.1 6.33,-2.98c0.79,0.04 1.86,0.12 2.66,0.96c0.63,0.67 1.18,1.91 0.74,2.79c-0.57,1.15 -2.28,0.69 -5.65,2.26l0.02,0.01z" class="cls-15" id="liteecho-e2"/>
|
||||
</g>
|
||||
<circle id="svg_6" fill="url(#_未命名的渐变_6)" stroke-width="0px" r="4" cy="301.0022" cx="162.07715" class="cls-22"/>
|
||||
<ellipse id="svg_7" fill="url(#_未命名的渐变_7)" stroke-width="0px" ry="2.5" rx="2.51" cy="270.5022" cx="183.56715" class="cls-12"/>
|
||||
<ellipse id="svg_8" fill="url(#_未命名的渐变_7-2)" stroke-width="0px" ry="2.5" rx="2.51" cy="273.5022" cx="176.56715" class="cls-26"/>
|
||||
<circle id="svg_9" fill="url(#_未命名的渐变_7-3)" stroke-width="0px" r="4" cy="294.3922" cx="185.05715" class="cls-11"/>
|
||||
<circle id="svg_10" stroke-width="0px" fill="url(#_未命名的渐变_8)" r="4" cy="281.2622" cx="209.49715" class="cls-1"/>
|
||||
<circle id="svg_11" fill="url(#_未命名的渐变_55)" stroke-width="0px" r="4" cy="275.2622" cx="217.49715" class="cls-7"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 34 KiB |
148
docs/public/liteyuki.svg
Normal file
148
docs/public/liteyuki.svg
Normal file
@ -0,0 +1,148 @@
|
||||
|
||||
<svg width="617" height="419" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<defs>
|
||||
<linearGradient y2="0.50001" x2="0.99992" y1="0.50001" x1="-0.00009" data-name="未命名的渐变 11" id="_未命名的渐变_11">
|
||||
<stop stop-color="#73fff1" offset="0"/>
|
||||
<stop stop-color="#5cefff" offset="1"/>
|
||||
</linearGradient>
|
||||
<linearGradient xlink:href="#_未命名的渐变_11" y2="292.99" x2="584.05" y1="292.99" x1="549.98" data-name="未命名的渐变 11" id="_未命名的渐变_11-2"/>
|
||||
<linearGradient xlink:href="#_未命名的渐变_11" y2="212" x2="585" y1="212" x1="553" data-name="未命名的渐变 11" id="_未命名的渐变_11-3"/>
|
||||
<linearGradient xlink:href="#_未命名的渐变_11" y2="260.3" x2="698.34" y1="260.3" x1="609.82" data-name="未命名的渐变 11" id="_未命名的渐变_11-4"/>
|
||||
<linearGradient xlink:href="#_未命名的渐变_11" y2="282.24" x2="784.94" y1="282.24" x1="702.75" data-name="未命名的渐变 11" id="_未命名的渐变_11-5"/>
|
||||
<linearGradient y2="0.5" x2="1" y1="0.5" x1="0" data-name="未命名的渐变 240" id="_未命名的渐变_240">
|
||||
<stop stop-color="#eadeff" offset="0"/>
|
||||
<stop stop-color="#e0efff" offset="0.28"/>
|
||||
<stop stop-color="#d9ecff" offset="0.43"/>
|
||||
<stop stop-color="#aedbff" offset="0.59"/>
|
||||
<stop stop-color="#00d7ff" offset="1"/>
|
||||
</linearGradient>
|
||||
<linearGradient y2="0.50003" x2="1.00001" y1="0.50003" x1="0" data-name="未命名的渐变 9" id="_未命名的渐变_9">
|
||||
<stop stop-color="#2ebbff" offset="0"/>
|
||||
<stop stop-color="#006bff" offset="1"/>
|
||||
</linearGradient>
|
||||
<linearGradient xlink:href="#_未命名的渐变_9" y2="467.72" x2="842.05" y1="467.72" x1="761.74" data-name="未命名的渐变 9" id="_未命名的渐变_9-2"/>
|
||||
<linearGradient xlink:href="#_未命名的渐变_9" y2="394" x2="977.04" y1="394" x1="951.96" data-name="未命名的渐变 9" id="_未命名的渐变_9-3"/>
|
||||
<linearGradient xlink:href="#_未命名的渐变_9" y2="456.86" x2="977.56" y1="456.86" x1="952.19" data-name="未命名的渐变 9" id="_未命名的渐变_9-4"/>
|
||||
<linearGradient xlink:href="#_未命名的渐变_9" y2="437.15" x2="934.09" y1="437.15" x1="848.96" data-name="未命名的渐变 9" id="_未命名的渐变_9-5"/>
|
||||
<linearGradient y2="0.5" x2="0.99994" y1="0.5" x1="0" data-name="未命名的渐变 4" id="_未命名的渐变_4">
|
||||
<stop stop-color="#6445ff" offset="0"/>
|
||||
<stop stop-color="#5c86ff" offset="1"/>
|
||||
</linearGradient>
|
||||
<linearGradient xlink:href="#_未命名的渐变_4" y2="464.45" x2="466.06" y1="464.45" x1="445.81" data-name="未命名的渐变 4" id="_未命名的渐变_4-2"/>
|
||||
<linearGradient y2="-0.23832" x2="1.47603" y1="0.46345" x1="0.17906" data-name="未命名的渐变 72" id="_未命名的渐变_72">
|
||||
<stop stop-color="#c4aeff" offset="0"/>
|
||||
<stop stop-color="#ce9fff" offset="0.43"/>
|
||||
<stop stop-color="#af8eff" offset="0.55"/>
|
||||
<stop stop-color="#7d74ff" offset="0.72"/>
|
||||
<stop stop-color="#6333ff" offset="0.9"/>
|
||||
</linearGradient>
|
||||
<linearGradient y2="0.47289" x2="0.99937" y1="0.52818" x1="0.02548" data-name="未命名的渐变 58" id="_未命名的渐变_58">
|
||||
<stop stop-color="#b1ccff" offset="0"/>
|
||||
<stop stop-color="#b8c0ff" offset="0.34"/>
|
||||
<stop stop-color="#cba2ff" offset="0.94"/>
|
||||
<stop stop-color="#ce9fff" offset="1"/>
|
||||
</linearGradient>
|
||||
<linearGradient y2="0.50005" x2="1" y1="0.50005" x1="0.00045" data-name="未命名的渐变 3" id="_未命名的渐变_3">
|
||||
<stop stop-color="#5c86ff" offset="0"/>
|
||||
<stop stop-color="#64a2ff" offset="1"/>
|
||||
</linearGradient>
|
||||
<linearGradient xlink:href="#_未命名的渐变_3" y2="475.89" x2="477.01" y1="475.89" x1="470.95" data-name="未命名的渐变 3" id="_未命名的渐变_3-2"/>
|
||||
<linearGradient y2="0.4675" x2="1.0805" y1="0.53756" x1="0.11473" data-name="未命名的渐变 65" id="_未命名的渐变_65">
|
||||
<stop stop-color="#64a2ff" offset="0.11"/>
|
||||
<stop stop-color="#62a6ff" offset="0.28"/>
|
||||
<stop stop-color="#5cb5ff" offset="1"/>
|
||||
</linearGradient>
|
||||
<linearGradient y2="0.61626" x2="1.13145" y1="0.41032" x1="0.09443" data-name="未命名的渐变 66" id="_未命名的渐变_66">
|
||||
<stop stop-color="#5cb5ff" offset="0"/>
|
||||
<stop stop-color="#74e0ff" offset="1"/>
|
||||
</linearGradient>
|
||||
<linearGradient y2="0.48885" x2="1.08173" y1="0.51016" x1="0.08139" data-name="未命名的渐变 68" id="_未命名的渐变_68">
|
||||
<stop stop-color="#9cd1ff" offset="0"/>
|
||||
<stop stop-color="#bae0ff" offset="1"/>
|
||||
</linearGradient>
|
||||
<linearGradient y2="0.50005" x2="1.00016" y1="0.50005" x1="0.00009" data-name="未命名的渐变 71" id="_未命名的渐变_71">
|
||||
<stop stop-color="#a6d3ff" offset="0"/>
|
||||
<stop stop-color="#b1ccff" offset="0.7"/>
|
||||
</linearGradient>
|
||||
<linearGradient xlink:href="#_未命名的渐变_58" y2="466.98" x2="643.63" y1="473.79" x1="605.79" data-name="未命名的渐变 58" id="_未命名的渐变_58-2"/>
|
||||
<linearGradient xlink:href="#_未命名的渐变_3" y2="397.6" x2="514.17" y1="397.6" x1="479.91" data-name="未命名的渐变 3" id="_未命名的渐变_3-3"/>
|
||||
<linearGradient y2="0.4999" x2="0.99997" y1="0.4999" x1="-0.00009" data-name="未命名的渐变 5" id="_未命名的渐变_5">
|
||||
<stop stop-color="#64a2ff" offset="0"/>
|
||||
<stop stop-color="#5cc9ff" offset="1"/>
|
||||
</linearGradient>
|
||||
<linearGradient y2="0.50013" x2="0.99983" y1="0.50013" x1="0.00007" data-name="未命名的渐变 69" id="_未命名的渐变_69">
|
||||
<stop stop-color="#84c9ff" offset="0"/>
|
||||
<stop stop-color="#b1ccff" offset="0.42"/>
|
||||
</linearGradient>
|
||||
<linearGradient y2="0.36502" x2="1.03701" y1="0.61527" x1="0.09096" data-name="未命名的渐变 67" id="_未命名的渐变_67">
|
||||
<stop stop-color="#74e0ff" offset="0"/>
|
||||
<stop stop-color="#8dd6ff" offset="0.56"/>
|
||||
<stop stop-color="#9cd1ff" offset="1"/>
|
||||
</linearGradient>
|
||||
<linearGradient y2="0.5" x2="1" y1="0.5" x1="0" data-name="未命名的渐变 6" id="_未命名的渐变_6">
|
||||
<stop stop-color="#64a2ff" offset="0"/>
|
||||
<stop stop-color="#5cbfff" offset="1"/>
|
||||
</linearGradient>
|
||||
<linearGradient y2="0.5" x2="1" y1="0.5" x1="0" data-name="未命名的渐变 7" id="_未命名的渐变_7">
|
||||
<stop stop-color="#64ccff" offset="0"/>
|
||||
<stop stop-color="#84c9ff" offset="1"/>
|
||||
</linearGradient>
|
||||
<linearGradient xlink:href="#_未命名的渐变_7" y2="383.5" x2="552" y1="383.5" x1="546.98" data-name="未命名的渐变 7" id="_未命名的渐变_7-2"/>
|
||||
<linearGradient xlink:href="#_未命名的渐变_7" y2="404.39" x2="561.98" y1="404.39" data-name="未命名的渐变 7" id="_未命名的渐变_7-3"/>
|
||||
<linearGradient y2="0.5" x2="1" y1="0.5" x1="0" data-name="未命名的渐变 8" id="_未命名的渐变_8">
|
||||
<stop stop-color="#84c9ff" offset="0.01"/>
|
||||
<stop stop-color="#b1ccff" offset="1"/>
|
||||
</linearGradient>
|
||||
<linearGradient y2="0.5" x2="4.66625" y1="0.5" x1="4.61625" data-name="未命名的渐变 55" id="_未命名的渐变_55">
|
||||
<stop stop-color="#b1c9ff" offset="0"/>
|
||||
<stop stop-color="#b1ccff" offset="0.42"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<g>
|
||||
<title>Layer 1</title>
|
||||
<g data-name="图层 3" id="_图层_3">
|
||||
<path id="svg_1" fill="#9cf" stroke-width="0px" d="m74.07715,375.0022c11.04,8.56 30.35,0.82 33,0c14.37,-4.43 15.38,-15.33 26,-15c10.44,0.33 13.16,10.98 24,10c6.48,-0.59 7.04,-4.53 16,-6c8,-1.31 8.66,1.66 17,1c12.76,-1.01 15.73,-8.33 24,-6c8.42,4.47 11.58,6.51 22,5c0,0 11.3,-1.75 32,-10c1.42,-9.07 4.72,6.12 11.99,12c10.1,9.09 19.95,-0.4 31,8c10.86,8.26 13.5,26.66 13,31c-1.65,4.39 6.37,10.16 6,10c-4.41,-0.77 -20.72,-10.82 -25,-11c-6.8,-1.26 -15.53,-2.26 -24,-5c-17.1,-5.24 -15.17,-10.82 -34,-10c-7.53,0.04 -6.69,-0.47 -12,1c-2.54,-0.23 -9.74,0.73 -13,-1c-18.29,-11.87 -9.94,11.78 -36,3c-9.47,1.86 -14.72,0.65 -18.02,-1.12c-2.21,-1.19 -4.61,-5.33 -6.74,-4.61c-0.57,0.19 -0.74,0.58 -2.24,1.74c-1.2,0.93 -2.27,1.58 -3,2c-0.91,0.36 -1.91,0.7 -3,1c-2.93,0.8 -5.59,1.02 -7.75,1.02c-0.74,0 -1.87,-0.01 -3.25,-0.02c-3.97,0 -3.61,0.03 -4,0c-2.87,-0.21 -3.75,-0.93 -7,-1c-0.42,0 -0.77,0 -1,0l-6,0c-3,0.22 -5.39,0.65 -7,1c-1.72,0.37 -3.36,0.82 -10,3c-3.07,1.01 -4.59,1.59 -6,2c-1.81,0.53 -5.13,1.49 -9,2c-5.07,0.67 -9,0.3 -12,0c-6.54,-0.65 -11.56,-2.18 -14,-3c-4.6,-1.55 -26.12,-13.37 -20,-10c0,0 0,0 0,0c-0.03,-0.02 -20.88,-13.37 -28,-32c-1.09,-2.85 -2,-6 -2,-6c-0.52,-1.8 -1.44,-5.03 -2,-9c-0.44,-3.12 -1.34,-9.53 0,-10c1.69,-0.59 6.23,8.51 7.27,10.48c5.15,9.76 18.12,22.54 52.73,35.52l0.01,-0.01z" class="cls-32"/>
|
||||
<path id="svg_2" fill="#9cf" stroke-width="0px" d="m260.07715,284.0022c-13.63,7.43 -2.93,57.29 2,57c1.82,-0.11 1.13,-6.97 8,-16c3,-3.94 3.81,-3.37 5,-6c0.88,-3.8 2.06,-11.66 -2,-19c-0.61,-1.11 -1.29,-2.11 -2,-3c-0.31,-0.36 -0.8,-1.04 -1,-2c-0.06,-0.32 -0.19,-0.91 0,-1c0.26,-0.13 0.82,0.87 2,2c0.84,0.81 1.71,1.61 3,2c1.06,0.32 1.94,0.17 3,0c1.38,-0.23 2.37,-0.69 3,-1c1.31,-0.64 2.25,-1.09 3,-2c1.84,-2.22 1.14,-5.41 1,-6c-0.33,-1.36 -0.69,-1.29 -2,-4c-0.87,-1.8 -0.57,-1.55 -2,-5c-0.49,-1.19 -1,-2.79 -2,-6c-0.12,-0.39 -0.37,-1.22 0,-2c0,0 0.29,-0.6 1,-1c1.98,-1.11 11.6,0.55 15,1c2.66,0.35 13.92,1.61 15.83,2.95c0.42,0.3 0.8,0.64 1.17,1.05c2.43,2.73 0.93,4.31 3,9c3.83,8.69 13.16,12.57 19,15c5.09,2.12 8.54,2.5 13,3c6.22,0.69 7.53,-0.22 8,-1c0.74,-1.23 -0.27,-2.7 -1,-5c-1.53,-4.78 -0.3,-8.09 0,-12c0.51,-6.64 -1.77,-13.74 -4,-17c-9.26,-13.54 -41.29,4.77 -71,-2c-1.28,-0.29 -9.89,-2.3 -12,1c-1.87,2.94 2.99,7.55 1,12c-1.49,3.33 -5.5,3.63 -8,5z" class="cls-32"/>
|
||||
<path id="svg_3" fill="#9cf" stroke-width="0px" d="m310.07715,118.0022c-22.42,19.49 74.7,203.11 32,245c-9.08,8.91 -26.9,13.94 -28,28c-0.65,8.33 4.78,18.3 13,23c13.88,7.94 28.83,2.05 33,-1c2.68,-1.96 4,-4 4,-4c1.44,-2.22 1.56,-3.91 3,-8c0.76,-2.17 1.5,-3.9 2,-5c0.43,-1.13 0.95,-2.86 1,-5c0.04,-1.88 -0.32,-2.29 -0.25,-4.13c0.04,-1.01 0.22,-5.22 2.25,-5.87c0.27,-0.09 1.3,-0.35 3,1c-0.17,-0.41 -0.33,-0.87 -0.21,-0.95c0.24,-0.16 1.16,1.42 2.21,1.95c2.87,1.44 7.56,-0.07 8,-2c-10.79,-5.85 -14.4,-6.84 -15,-6c-0.79,1.11 3.49,5.56 3,6c-0.37,0.33 -3.05,-1.93 -3,-2c0.05,-0.07 3.08,1.97 4,3c0.08,0.09 0.44,0.51 1,1c0.23,0.2 0.43,0.36 0.55,0.46c0.1,0.12 0.26,0.31 0.45,0.54c2.9,3.62 4.2,7.01 4.62,8.05c0.92,2.28 2.67,5.77 6.38,10.95c15.05,16.54 34.83,19.18 54,9c16.33,3.81 7.85,10.54 33,5c25.9,-4.5 50.39,1.98 49,-25c-1.26,-10.66 -5.86,-19.02 0,-10c8.68,14.17 21.69,39.51 43,27c6.44,-4.99 5.9,-9.29 8,-16c1.22,-1.48 4.53,-4.38 6,-4c0.01,0.08 0.16,1 -0.41,1.5c-0.6,0.53 -1.8,0.43 -2.59,-0.5c2.97,10.52 12.31,17.02 21,16c7.93,-0.94 13.74,-10.13 14.27,-11.06c0.54,-0.96 0.98,-1.97 0.98,-1.97s0.41,-0.95 0.75,-1.97c1.19,-3.64 1.46,-7.4 1.46,-7.4c0.88,-11.86 0.98,-15.27 0.98,-15.27c0.35,-11.02 0.5,-9.78 0.67,-17.66c0.19,-8.34 -0.04,-7.03 0.13,-13.62c0,0 0.55,-21.79 -3.25,-26.05c-0.48,-0.54 -1,-1 -1,-1c-1.02,-0.91 -1.79,-1.28 -3,-2c0,0 -1,-0.67 -3,-2c-1,-0.67 -2.08,-1.22 -3,-2c-1.25,-1.06 -1.85,-1.94 -3,-2c-0.39,-0.02 -0.52,0.07 -1,0c-1.2,-0.18 -1.99,-1.02 -2,-1c-0.02,0.03 0.5,0.41 3,2c0.1,-0.62 1.27,-2.67 2,-3c5.54,-4.98 5.58,-5.38 8,-10c6.12,-15.03 -2.92,-19.21 -14,-26c-1.23,-1.16 -1.87,-0.9 -3,-1c-0.59,-2.25 -2.17,-3.18 -3,-6c-1.01,-2.71 -2.29,-10.67 -2,-12c0.57,-5.41 2.39,-8.84 4,-11c2.1,-2.82 3.85,-3.49 6,-7c1.22,-2 2.84,-4.65 3,-8c0.25,-5.38 -3.39,-9.63 -8,-15c-2.47,-2.87 -5.06,-5.17 -6,-6c-2.42,-2.13 -4.22,-3.42 -5,-4c-5.14,-3.8 -8.31,-10.94 -9.7,-14.15c-5.32,-12.26 -7.9,-23.86 -8.3,-32.85c-1.17,-26.5 -38.54,-51.77 -63,-35c-7.04,5.65 -2.81,10.14 -11,18c-10.42,10 -22.57,7.8 -34,13c-5.71,3.43 -10.18,0.22 -16,-2c-9.95,-3.09 -18.5,-13.48 -24,-22c-1.52,-1.57 -3.98,-3.73 -7.79,-5.64c-30.14,-4.8 -47.97,-7.45 -51.21,-7.36c-0.78,0.02 -4.27,0.18 -7.57,-1.74c-7.85,-9.54 -30.21,6.06 -40.43,13.74l0.01,0z" class="cls-32"/>
|
||||
<path id="svg_4" fill="#9cf" stroke-width="0px" d="m294.07715,64.0022c-4.66,0.36 -8.11,1.64 -10.79,3.57c-13.35,9.62 -7.67,24.47 -21.35,37.56c-3.88,3.71 -4.86,2.87 -33.86,12.87c-0.06,0.02 -2.46,0.85 -4,0c-0.23,-0.13 -0.75,-0.41 -1,-1c-0.23,-0.54 -0.15,-1.16 -0.12,-1.33c0.02,-0.16 0.09,-0.51 0.12,-0.67l0,0c1.24,-1.71 8.11,-11.55 4.97,-21.02c-3.76,-11.34 -20.93,-19.28 -39.97,-12.98c-12.11,2.27 -15.97,0.27 -17.33,-1.85c-1.94,-3.02 0.84,-6.86 5.33,-19.16c3.86,-10.58 5.89,-16.13 6,-22c0.1,-5.49 -1.43,-13.73 -10,-24c-23.21,-14.8 -47,-13.43 -56,-2c-1.46,1.85 -5.64,7.92 -4,20c-3.3,4.02 -9.31,1.14 -20,4c-10.63,1.96 -20.69,9.46 -13,23c5.63,9.91 94.53,145.36 192.09,115.49c50.17,-15.36 72.52,-65.2 72.91,-66.49c0.03,-0.1 0.51,-1.68 0,-2c-0.15,-0.1 -0.3,-0.03 -1,0l-3,0c-1.55,0 -3.44,-0.77 -4,-1c-0.5,-0.2 -1.8,-0.75 -5,-3c-1.04,-0.73 -2.42,-1.73 -4,-3c-3.1,-0.72 -4.4,-1.96 -5,-3c-0.3,-0.52 -0.38,-0.9 -1,-2c-1.21,-2.14 -1.61,-1.92 -2,-3c-0.34,-0.93 -0.04,-1.09 0,-5c0,-0.35 0,-0.06 0,-2c0,-3.6 0.02,-3.67 0,-4c-0.12,-1.85 -0.9,-3.16 -2,-5c-2.63,-4.41 -6.25,-6.57 -7,-7c-1.67,-0.96 -2.99,-1.38 -5,-2c-2.56,-0.79 -6.28,-1.69 -11,-2l0,0.01z" class="cls-32"/>
|
||||
</g>
|
||||
<g data-name="背景" id="_背景">
|
||||
<path id="svg_5" fill="#fff" stroke-width="0px" d="m199.07715,72.0022c-16.42,-3.08 -26.21,8.1 -31,4c-7.63,-6.54 18.33,-33.97 9,-55c-6.88,-15.51 -30.12,-21.13 -45,-21c-3.33,0.03 -16.49,0.44 -31,9c-30.76,18.15 -37.62,55.27 -56,109c-20.03,58.55 -43.71,77.3 -45,119c-0.4,13.04 0.88,31.37 1,33c2.91,40.64 9.7,47.97 12,64c0.27,1.85 0.98,7.34 5,12c0.83,0.96 1.59,1.64 2.01,2c0.47,0.48 1.15,1.18 1.99,2c10.84,10.63 27.33,20.18 27.33,20.18c17.89,10.12 26.84,15.18 35.66,13.82c3.19,-0.49 6.88,-1.68 16.62,-3.27c3.46,-0.56 6.46,-0.79 12.39,-1.73c3.95,-0.63 7.36,-0.89 10,-1c3.64,-0.15 6.36,-0.03 7,0c4.66,0.22 8.01,0.84 9,1c3.81,0.61 10.14,0.32 21,-4c3.96,0.13 4.67,2.49 9,4c4.59,1.6 5.73,-0.37 15,0c16.7,2.72 12.4,-0.95 25,-4c5.48,-0.56 5.78,1.97 14,3c9.86,2.45 36.35,-8.51 48,1c6.44,5.27 9.1,4.24 16,6c7.05,3.78 15.85,4.35 23,8c18.2,11.17 34.14,20.96 51,2c5.59,-6.98 3.83,-12.43 8,-14c7.13,-2.81 31.59,30.21 58,23c15.36,-6.57 13.31,-4.51 28,1c5.68,1.04 23.32,-3.84 33,-4c12.6,-0.87 15.51,0.92 21,-2c11.5,-6.32 8.35,-12.82 9,-21c6.52,3 17.82,31.94 43,18c6.28,-4.14 7.41,-10.27 11,-10c3.81,0.29 3.43,7.28 9,11c7.22,4.82 20.84,1.79 26,-6c1.88,-2.84 1.95,-5.28 2,-16c0.06,-12.86 2.5,-34.29 1,-57c-0.27,-4.14 -0.62,-6.12 -2,-8c-1.81,-2.47 -4.68,-3.81 -7.73,-8.57c-0.11,-0.18 -0.21,-0.33 -0.27,-0.43c0,-0.08 -0.02,-0.19 -0.02,-0.33c0.03,-1.6 1.77,-2.75 2.36,-3.17c1.19,-0.85 2.8,-2.4 4.66,-5.5c10.77,-33.99 -34.41,-21.61 -20,-59c3.12,-6.89 6.55,-6.37 8,-12c2.32,-9.04 -4.5,-18.26 -8,-23c-5.99,-8.1 -8.37,-6.45 -17,-17c-2.93,-3.58 -6.84,-8.36 -9.5,-15.5c-2.32,-6.24 -2.48,-11.05 -2.5,-12.5c0,-0.42 0,-0.77 0,-1c0.13,-4.73 -0.28,-14.34 -6,-24c-8.66,-14.64 -25.27,-22.26 -40,-22c-22.75,0.41 -39.01,19.59 -41,22c-4.52,5.48 -5.53,9.19 -11,12c-8.17,4.19 -19.82,3.24 -29,-1c-1.04,-0.48 -1.78,-0.88 -2,-1c-13.03,-7.1 -12.62,-18.04 -23,-25c-0.78,-0.52 -2.86,-1.85 -9.5,-3.93c-18.42,-5.77 -40.67,-7.51 -55.5,-8.07c-1.93,0.07 -4.76,-0.05 -8,-1c-3.67,-1.08 -6.36,-2.77 -8,-4c-5.34,-3.78 -6.82,-2.84 -9,-5c-3.59,-3.56 0.14,-10.39 -3,-17c-2.84,-5.97 -10.15,-7.96 -14,-9c-2.47,-0.67 -11.2,-2.68 -20,2c-1.88,1 -6.08,3.3 -9,8c-2.37,3.82 -1.56,5.46 -4,14c-2.08,7.28 -3.18,10.97 -6,13c-1.17,0.85 -5.1,3.27 -13,1c-3.07,-0.18 -7.45,0 -12,2c-8.86,3.88 -12,11.82 -14,11c-2.79,-1.15 5.52,-15.64 0,-28c-5.58,-12.5 -22.22,-15.85 -23,-16z" class="cls-18"/>
|
||||
<g data-name="主体" id="_主体">
|
||||
<path fill="url(#_未命名的渐变_11)" stroke-width="0px" d="m143.07715,31.0022c0.03,-1.1 -0.07,-2.97 -1,-5c-0.89,-1.93 -2.11,-3.1 -2.75,-3.7c-0.4,-0.38 -1.57,-1.42 -3.37,-2.29c-1.11,-0.53 -2.12,-0.83 -2.88,-1.01c-0.48,0 -1.16,0 -1.98,0.04c-1.4,0.09 -4.59,0.32 -7.8,1.81c-3.86,1.79 -6.04,4.59 -7.21,6.15c-1.03,1.36 -0.93,1.77 -4,8c-1.08,2.19 -1.79,3.57 -2,4c-5.9,11.79 -12,35 -12,35c-7.65,29.08 -7.7,25.76 -12,44c-2.8,11.87 -4.71,21.49 -6,28c-0.61,3.07 -2.21,11.25 -4,22c0,0 -1.57,9.87 -3,30c-0.1,1.34 -0.46,7.05 1,14c0.85,4.05 3,8 3,8l0,0c2.28,2.98 4.52,4.83 5.99,5.89c11.06,8 23.32,4.35 57.01,4.11c3.06,-0.02 5.53,-0.01 7,0c1.02,-0.28 2.51,-0.84 4,-2c0.52,-0.41 1.27,-1.06 2,-2c0.45,-0.58 1.59,-1.91 2,-4c0.21,-1.08 0.43,-2.87 0,-5c-0.36,-1.17 -0.7,-2.18 -1,-3c-0.3,-0.83 -0.53,-1.39 -1,-2c-0.36,-0.46 -0.73,-0.79 -1,-1c-0.24,-0.23 -0.76,-0.76 -1,-1c-1.73,-1.73 -1.87,-1.87 -2,-2c-1.25,-1.2 -2.82,-1.65 -4,-2c-5.84,-1.71 -23,-2 -23,-2c-4.12,-0.07 -10.14,-0.62 -17.53,-2.99c1.73,-17.19 5.56,-49.39 13.31,-81.46c4.95,-20.51 4.35,-18.09 8.22,-30.55c5.85,-18.81 8.77,-28.22 11,-34c0,0 3.24,-8.41 4.63,-15.69c0.44,-2.32 0.84,-4.8 0.84,-4.8c0.18,-1.17 0.35,-2.34 0.53,-3.5l-0.01,-0.01z" class="cls-3" id="lite-l"/>
|
||||
<path fill="url(#_未命名的渐变_11-2)" stroke-width="0px" d="m196.07715,130.0022c4.11,0.31 6.91,2.96 8,4c6.83,6.48 6.93,17.68 7,26c0,0.06 0,0.03 0,7c0,14.32 0,21.48 0,22c0.04,13.27 0.66,35.34 -4,42c-1.29,1.84 -3,3 -3,3c-2.97,2.01 -6.17,1.99 -8,2c-1.45,0 -7.74,-0.12 -13,-5c-6.34,-5.87 -6.06,-13.87 -6,-15c0,-0.07 0,-9.93 0,-10c0,-38.04 0,-37.85 0,-38c-0.02,-1.29 -0.01,-3.53 0,-8c0.03,-9.9 0.15,-12.06 1,-15c1.28,-4.42 3.15,-6.95 4,-8c1.1,-1.36 2.19,-2.34 3,-3c0.7,-0.58 1.7,-1.32 3,-2c0.98,-0.52 4.28,-2.28 8,-2z" class="cls-15" id="lite-i-1"/>
|
||||
<path fill="url(#_未命名的渐变_11-3)" stroke-width="0px" d="m193.85715,86.0722c8.85,-0.96 18.71,7.52 18.2,17.09c-0.5,9.28 -10.62,16.63 -19.33,14.46c-6.93,-1.73 -13.7,-9.65 -12.51,-18.4c1.06,-7.79 7.93,-12.52 13.65,-13.14l-0.01,-0.01z" class="cls-30" id="lite-i-1-1"/>
|
||||
<path fill="url(#_未命名的渐变_11-4)" stroke-width="0px" d="m270.07715,109.0022c-5.46,4.9 -13.26,-2.45 -24,3c-1.76,0.89 -7.94,4.02 -9,10c-0.72,4.08 0.94,7.61 1.38,8.43c0.48,0.92 2.17,4.11 5.62,5.57c1.09,0.46 1.2,0.26 6,1c5.06,0.78 4.17,0.82 6,1c2.02,0.19 4,0.68 6,1c0.54,0.09 1.55,0.25 2,1c0.42,0.71 0.13,1.6 0,2c-0.61,1.93 -0.44,4.05 -1,6c-1.01,3.51 -0.87,8.39 -1,13c-0.29,10.62 -1.82,12.94 -2,20c-0.11,4.19 -0.42,18.97 9,31c1.49,1.9 10.06,12.84 21,12c1.28,-0.1 5.89,-0.45 9,-4c4.48,-5.11 2.13,-12.61 2,-13c-1.5,-4.54 -4.19,-4.73 -8,-10c-4,-5.53 -5.15,-11 -6,-15c-1.67,-7.89 -0.83,-14.05 0,-20c1.22,-8.76 3.02,-10.14 4,-16c0.13,-0.81 0.46,-2.94 2,-5c0,0 0.85,-1.13 2,-2c2.17,-1.64 8.68,-0.49 12.72,-0.42c3.25,0.06 5.06,0.09 7.28,-0.58c1.17,-0.35 8.31,-2.66 10,-9c1.37,-5.14 -1.51,-11.55 -6,-14c-3.8,-2.07 -12.48,-2.26 -15,-3c-1.03,-0.3 -1.9,-0.93 -2,-1c-0.47,-0.34 -0.82,-0.6 -1,-1c-0.56,-1.23 0.89,-2.87 1,-3c0.54,-0.96 1.27,-2.32 2,-4c1.51,-3.46 2.99,-6.85 3,-11c0,-1.91 0.01,-6.19 -3,-10c-3.3,-4.17 -8.02,-4.99 -9.42,-5.24c-0.92,-0.16 -6.37,-1 -11.58,2.24c-1.61,1 -4.15,3 -7,9c-5.75,12.08 -1.75,17.18 -6,21z" class="cls-4" id="lite-t"/>
|
||||
<path fill="url(#_未命名的渐变_11-5)" stroke-width="0px" d="m362.07715,166.0022c-0.58,0.39 -5.22,3.65 -5,8c0.19,3.92 4.21,6.49 5,7c5.42,3.48 9.31,0.51 25,-1c10.15,-0.98 16.08,-0.47 21,-5c1.89,-1.74 2.72,-3.39 3,-4c2.32,-5.01 -0.29,-10.09 -1,-14c-1.83,-10.01 -12.11,-17.03 -15,-19c-1.39,-0.95 -8.43,-5.63 -19,-7c-4.47,-0.58 -13.46,-1.75 -23,3c-8.94,4.45 -13.31,11.6 -16,16c-5.84,9.56 -6.79,18.48 -7,21c-0.4,4.87 -1.09,14.86 5,25c4.96,8.26 11.87,12.39 15,14c8.24,4.25 15.6,4.12 23,4c9.99,-0.17 16.58,-2.61 23,-5c2,-0.74 5.17,-2.02 8,-5c1.68,-1.77 4.08,-4.29 4,-8c-0.07,-3.36 -2.16,-6.53 -5,-8c-2.95,-1.53 -5.9,-0.81 -9,0c-6.07,1.58 -6.81,4.01 -12,6c-6.49,2.49 -12.52,1.46 -15,1c-3.77,-0.69 -9.81,-1.81 -14,-7c-3.24,-4.01 -3.72,-8.41 -4,-11c-0.26,-2.43 -1.1,-10.07 4,-17c0.43,-0.59 7.21,-9.48 18,-9c1.43,0.06 9.65,0.59 14,7c1.16,1.71 2.94,4.33 2,6c-2.22,3.93 -15.76,-4.29 -25,2z" class="cls-7" id="lite-e"/>
|
||||
<path fill="url(#_未命名的渐变_240)" stroke-width="0px" d="m502.07715,162.0022l13.33,22l9.67,-15l-8,-20l4,-6l6,2l9,13l7,-11l9,1l3,7l-11,13l17,2l4,8l-5,4l-22,-4l-13.5,16.8l27.5,5.2l11,-14l7,1l3,7l-9,11l19,3l4,7l-4,5l-18,-3l7,14l-4,4l-8,-1l-10,-20l-40,-8l-2,-2l-20,-37l-23,-4l-3,-7l4,-5l16,3l-9,-15l4,-6l7,1l9,16l9,-10l7,2l3,6l-10,14z" class="cls-21" id="logo-snow"/>
|
||||
<path fill="url(#_未命名的渐变_9)" stroke-width="0px" d="m344.07715,316.0022c-18.61,-18.39 -62.05,-50.01 -50,-71c2.85,-4.5 7.34,-9.45 17,-7c15.19,5.99 14.64,39.18 45,55c8.67,-18.92 10.28,-33.18 10,-43c-0.12,-4.37 -0.74,-12.15 4,-16c0.82,-0.55 5.22,-3.4 10.85,-2.15c3.86,0.85 6.25,3.17 7.15,4.15c0.5,0.6 1.28,1.58 2.09,2.87c8.99,14.33 0.03,32.06 -11.49,69.45c-4.1,13.3 -6.65,21.58 -9.6,33.68c-5.81,23.86 -8.17,40.38 -20,51c-11.03,9.91 -22.32,0.03 -23,-9c-2.89,-15.02 23.13,-56.98 18,-68z" class="cls-27" id="yuki-y"/>
|
||||
<path fill="url(#_未命名的渐变_9-2)" stroke-width="0px" d="m403.07715,323.0022c-0.54,-0.11 -5.36,-1.02 -9,2c-2.31,1.92 -3.04,4.55 -4,8c-0.74,2.66 -0.91,4.8 -1,6c-0.59,8.25 0,15 0,15c1.18,13.55 1.77,20.33 3,23c1.04,2.27 4.07,8.71 11,13c6.21,3.85 12.31,3.91 21,4c0.81,0 16.83,0.09 21,-4c0.22,-0.21 1.2,-1.26 2,-1c0.93,0.3 0.69,2.05 2,4c0,0 0.63,0.93 2,2c3.54,2.75 12.53,3.41 16,-1c0.62,-0.79 1.75,-2.64 2,-5c0.07,-0.68 0.05,-1.22 0.02,-1.76c-0.13,-2.75 -0.88,-4.83 -1.02,-5.24c-1.31,-3.73 -3.37,-15.69 -4,-23c-0.58,-6.71 0.42,-5.3 0,-19c-0.11,-3.63 -0.12,-9.19 -3,-15c-1.36,-2.75 -2.63,-4.09 -4,-5c-2.95,-1.96 -6.82,-2.22 -10,-1c-0.8,0.31 -3.48,1.34 -5,4c-1.36,2.37 -1.11,4.83 -1,6c0.66,6.98 0.45,14.01 1,21c0.72,9.13 0.57,11.51 -1,14c-1.8,2.86 -4.42,4.2 -6,5c-7.58,3.86 -15,0.82 -17,0c-2.54,-1.04 -3.49,-2.11 -4,-3c-0.74,-1.29 -0.88,-2.78 -1,-4c-0.13,-1.35 -0.05,-2.34 0,-3l0,-16c-0.19,-7.44 -0.28,-11.16 -2,-14c-0.52,-0.85 -3.02,-4.98 -8,-6z" class="cls-22" id="yuki-u"/>
|
||||
<path fill="url(#_未命名的渐变_9-3)" stroke-width="0px" d="m591.07715,272.0022c-6.65,0.3 -12.55,6.12 -12,13c0.53,6.56 6.7,11.28 13,11c6.65,-0.3 12.55,-6.12 12,-13c-0.53,-6.56 -6.7,-11.28 -13,-11z" class="cls-2" id="yuki-i-1-1"/>
|
||||
<path fill="url(#_未命名的渐变_9-4)" stroke-width="0px" d="m591.07715,303.0022c-0.8,0.03 -3.41,0.23 -6,2c0,0 -1.73,1.18 -3,3c-3.44,4.92 -2.32,14.54 -2,25c0.14,4.66 0,9.33 0,14c0,11.92 0,17.88 0,18c-0.51,8.41 -1.54,11.05 0,16c0.77,2.5 1.84,5.92 5,8c3.21,2.11 6.76,1.77 8.36,1.62c0.77,-0.07 5.65,-0.54 8.64,-3.62c3.4,-3.49 2.66,-8.56 2,-17c-0.09,-1.11 -1.18,-15.43 -1,-28c0.1,-7.33 -0.76,-14.71 0,-22c0.11,-1.03 0.52,-4.77 -1,-9c-0.64,-1.78 -1.55,-4.19 -4,-6c-2.89,-2.13 -6.05,-2.04 -7,-2z" class="cls-24" id="yuki-i-1"/>
|
||||
<path fill="url(#_未命名的渐变_9-5)" stroke-width="0px" d="m545.07715,299.0022c-0.26,-0.11 -0.66,-0.27 -1.17,-0.44c0,0 -4.46,-1.53 -9.91,-1c-4.47,0.44 -18.59,11.34 -31.93,31.43c-0.35,-10.53 -0.23,-21.87 0.56,-33.9c0.37,-5.55 0.85,-10.92 1.44,-16.1c0.26,-1.2 0.52,-2.94 0.41,-5.03c-0.09,-1.81 -0.25,-4.96 -2.27,-8.04c-0.56,-0.85 -2.9,-4.42 -7.52,-5.51c-5.61,-1.33 -10.01,2.08 -10.62,2.58c-3.44,2.76 -4.45,6.67 -5,9c-3.6,15.32 -3,27 -3,27c0.42,8.15 1.67,32.57 1,50c-0.22,5.66 0.05,11.33 0,17c-0.05,6.02 -0.13,9.05 1,13c1.45,5.07 4.08,8.75 6,11c1.19,1.11 3.93,3.36 8,4c3.99,0.63 7.79,-0.51 9.84,-1.85c11.06,-7.21 1.65,-39.59 4.55,-40.56c0.39,-0.13 1.19,0.88 2.61,3.4c8.89,11.22 14.99,15.39 30,29c7.7,10.13 23.45,3.59 22,-8c-0.19,-16.59 -39.62,-33.08 -31.91,-39.47c1.89,-2.38 6,-8.21 6.91,-9.53c5.47,-7.8 10.83,-8.44 13,-15c1.34,-4.04 0.69,-6.57 0.43,-7.38c-1.01,-3.15 -3.39,-4.94 -4.43,-5.62l0.01,0.02z" class="cls-28" id="yuki-k"/>
|
||||
<path fill="url(#_未命名的渐变_4)" stroke-width="0px" d="m79.07715,274.0022c-1.75,-1 -2.61,-0.17 -10,1c-2.22,0.35 -7.11,1.06 -13,3c-1.36,0.45 -2.68,0.93 -3,2c-0.49,1.65 1.56,4.27 4,5c0.32,0.09 0.56,0.13 0.74,0.16c2.19,0.34 3.89,-0.56 5.26,-1.16c1.96,-0.87 3.64,-1.24 7,-2c0,0 4.44,-1 5,0c0.02,0.04 0.04,0.07 0.04,0.07c0.07,0.18 -0.02,0.5 -0.04,2.93c0,0.06 0,0.71 0,2c0,5.01 0.09,5.71 0,7c-0.2,2.74 -0.3,4.11 -1,5c-1.25,1.58 -3.23,1.52 -7,2c-5.62,0.72 -4.41,1.27 -10,2c-7.19,0.94 -9.49,0.07 -11,2c-1.32,1.68 -1.32,4.57 0,6c1.74,1.88 5.46,0.88 9,0c7.35,-1.82 19.55,-4.84 29,-6c4.81,-0.59 12,-1.09 13,-4c0.57,-1.65 0,-3 0,-3c-0.06,-0.14 -0.62,-1.43 -2,-2c-1.19,-0.49 -2.36,-0.18 -3,0c-6.78,1.91 -9.23,2.82 -10,2c-0.45,-0.48 -0.17,-1.39 0,-2c1.77,-6.29 1.22,-12.36 1,-14c-0.23,-1.67 -0.84,-6.18 -4,-8z" class="cls-19" id="lite-jp-1"/>
|
||||
<path fill="url(#_未命名的渐变_4-2)" stroke-width="0px" d="m75.07715,330.0022c-1.02,0.34 -1.72,1.18 -2,2c-0.31,0.93 -0.06,1.78 0,2c0.38,1.39 0.25,7.26 0,19c-0.12,5.74 -0.36,7.73 0,12c0.21,2.49 0.58,5.11 2,8c0.87,1.76 1.83,3.72 4,5c2.08,1.23 4.09,1.14 7,1c2.69,-0.13 6.51,-0.31 7,-2c0.37,-1.27 -1.18,-3.23 -3,-4c-2.78,-1.18 -4.84,0.97 -7,0c-2.17,-0.97 -2.69,-4.29 -3,-7c-1.07,-9.45 -0.9,-24.04 -1,-30c-0.02,-1.36 -0.12,-5.21 -2,-6c-0.58,-0.25 -1.37,-0.21 -2,0z" class="cls-34" id="liteecho-1"/>
|
||||
<path fill="url(#_未命名的渐变_72)" stroke-width="0px" d="m263.13715,280.9522c-2.11,0.2 -3.55,1.9 -3.93,2.35c-0.57,0.68 -0.91,1.34 -1.11,1.8c1.09,1.05 6.06,5.64 11.22,4.91c2.33,-0.33 5.1,-1.81 5.34,-2.63c0.03,-0.09 0.03,-0.17 0.03,-0.17c0,-0.29 -0.33,-0.71 -1.89,-1.83c-1.33,-0.96 -2.23,-1.61 -3.3,-2.18c-0.69,-0.37 -1.23,-0.6 -1.68,-0.79c-2.41,-1.04 -3.62,-1.55 -4.68,-1.45l0,-0.01z" class="cls-16" id="yuki-jp-4-1"/>
|
||||
<path fill="url(#_未命名的渐变_58)" stroke-width="0px" d="m248.07715,265.0022c0.37,-0.49 1.34,-1.8 3,-2c2.33,-0.28 3.93,1.89 4,2c0.33,0.46 0.44,0.81 1,3c0.59,2.3 0.88,3.45 1,4c0.33,1.56 0.71,3.58 1,6c0.65,5.37 0.61,6.78 1,10c0.79,6.47 1.28,10.5 3,15c0.07,0.18 1.23,3.21 1,7c-0.08,1.29 -0.29,2.25 -1,3c-1,1.07 -2.67,1.39 -4,1c-0.58,-0.17 -1.69,-0.65 -3,-3c-1.53,-2.75 -1.78,-5.26 -2,-7c-0.3,-2.39 -0.86,-5.79 -2,-10c-1,-6.33 -2,-12.67 -3,-19c-0.33,-1.67 -0.67,-3.33 -1,-5c-0.1,-0.53 -0.53,-2.95 1,-5z" class="cls-12" id="yuki-jp-4"/>
|
||||
<path fill="url(#_未命名的渐变_3)" stroke-width="0px" d="m101.07715,341.0022c-1.86,-0.04 -3.59,1.27 -4,3c-0.56,2.38 1.47,5.01 4,5c2.03,-0.01 4,-1.72 4,-4c0,-2.25 -1.92,-3.96 -4,-4z" class="cls-20" id="liteecho-i-1-1"/>
|
||||
<path fill="url(#_未命名的渐变_3-2)" stroke-width="0px" d="m102.07715,354.0022c-0.61,-0.3 -1.38,-0.31 -2,0c-0.14,0.07 -0.62,0.33 -1,1c-1.18,2.08 -1.01,7.51 -1,8c0.16,5.45 0,9 0,9c-0.08,1.78 -0.22,3.7 1,5c0.21,0.22 0.94,1 2,1c1.08,0 1.81,-0.8 2,-1c1.05,-1.14 1.02,-2.72 1,-4c-0.03,-2.33 0.04,-4.67 0,-7c-0.05,-3.31 0.17,-8.95 -1,-11c-0.12,-0.2 -0.41,-0.71 -1,-1z" class="cls-9" id="liteecho-i-1"/>
|
||||
<path fill="url(#_未命名的渐变_65)" stroke-width="0px" d="m119.55715,342.6422c-0.52,0.03 -1.3,0.07 -1.92,0.55c-0.25,0.19 -0.76,0.67 -1.1,2.68c-0.15,0.92 -0.29,2.26 -0.14,3.91c-1.17,0.01 -3.52,0.16 -5.32,1.22c-0.38,0.22 -0.77,0.51 -1,1c-0.39,0.85 -0.08,1.91 0.41,2.59c0.74,1.01 1.99,1.19 3.59,1.41c0.85,0.12 1.57,0.12 2.05,0.1c-0.06,0.76 -0.12,1.85 -0.14,3.16c-0.01,1.22 -0.03,2.48 0.14,4.12c0.15,1.49 0.3,2.92 0.96,4.62c0.39,1.03 0.78,1.67 1,2c0.25,0.39 0.74,1.08 1.48,1.76c1.12,1.05 2.67,2.5 4.52,2.24c1.2,-0.17 2.57,-1.06 2.76,-2.24c0.12,-0.76 -0.29,-1.46 -0.55,-1.92c-0.43,-0.76 -0.87,-0.99 -1.51,-1.65c-0.95,-0.97 -1.44,-1.93 -1.78,-2.61c-0.71,-1.41 -0.9,-2.61 -1.1,-3.85c-0.15,-0.97 -0.37,-2.35 -0.14,-4.12c0.09,-0.67 0.22,-1.22 0.32,-1.61c0.56,0 1.35,-0.04 2.29,-0.17c1.54,-0.21 2.32,-0.32 2.88,-0.82c0.8,-0.71 1.27,-2.03 0.83,-3.01c-0.13,-0.3 -0.38,-0.61 -1,-1c-1.52,-0.94 -3.22,-1.02 -4.36,-0.94c0.11,-0.49 0.27,-1.25 0.41,-2.2c0.49,-3.18 0.14,-3.81 -0.14,-4.12c-0.26,-0.3 -0.59,-0.43 -1.24,-0.69c-0.4,-0.16 -1.18,-0.47 -2.2,-0.41z" class="cls-8" id="liteecho-t"/>
|
||||
<path fill="url(#_未命名的渐变_66)" stroke-width="0px" d="m142.48715,359.4922c-1.24,0 -1.82,0.11 -2.17,0.53c-0.63,0.76 -0.29,2.24 0.31,3.15c1.51,2.31 5.53,2.25 7.8,1.77c1.4,-0.3 4.49,-0.94 5.98,-3.77c1.7,-3.21 -0.04,-6.62 -0.4,-7.27c-1.86,-3.4 -5.47,-4.29 -7.14,-4.7c-3.37,-0.83 -6.16,0.05 -7.27,0.4c-1.07,0.34 -2.81,0.91 -4.53,2.4c-3.45,2.98 -3.92,7.09 -4,8c-0.27,3.05 0.7,5.35 1,6c0.27,0.6 1.17,2.42 3,4c1.04,0.89 2.16,1.53 3,2c0.99,0.56 1.75,0.89 2,1c1.02,0.44 1.88,0.81 3,1c1.17,0.2 2.07,0.1 3,0c0.34,-0.04 1.85,-0.22 4,-1c3.3,-1.2 4.59,-2.54 5,-3c0.47,-0.53 0.68,-0.9 0.71,-1.34c0.06,-1.15 -1.21,-2.31 -2.48,-2.62c-0.51,-0.12 -0.94,-0.08 -1.23,-0.05c-2.01,0.26 -3.04,1.28 -4.31,1.86c-2.59,1.19 -5.84,0.41 -7.69,-0.86c-1.16,-0.8 -1.95,-1.93 -2,-2c-0.26,-0.38 -0.69,-1.07 -1,-2c-0.26,-0.77 -0.95,-2.86 0,-5c0.82,-1.85 2.41,-2.69 3,-3c2.34,-1.23 5.06,-1.05 7,0c0.69,0.37 1.64,0.9 2,2c0.29,0.87 0.26,2.23 -0.52,2.84c-1,0.8 -2.36,-0.34 -6.07,-0.36l0.01,0.02z" class="cls-25" id="liteecho-e"/>
|
||||
<path fill="url(#_未命名的渐变_68)" stroke-width="0px" d="m197.07715,346.0022c-1.16,0 -4.12,0.04 -7,2c-1.54,1.05 -2.49,2.26 -3,3c-1.62,2.38 -1.87,4.7 -2,6c-0.12,1.26 -0.36,4 1,7c2.14,4.72 6.61,6.52 8,7c1.04,0.36 4.93,1.64 9,0c0.94,-0.38 2.74,-1.13 4,-3c0.45,-0.66 1.99,-2.94 1,-5c-0.54,-1.12 -1.88,-2.25 -3,-2c-1.41,0.32 -1.04,2.48 -3,4c-1.92,1.48 -4.45,1.09 -5,1c-0.54,-0.08 -2.42,-0.41 -4,-2c-2.4,-2.42 -2.04,-5.66 -2,-6c0.07,-0.51 0.43,-3.37 3,-5c2.25,-1.43 5.33,-1.38 7,0c0.93,0.77 0.92,1.55 2,2c1.26,0.52 3.2,0.27 4,-1c0.72,-1.15 0.17,-2.56 0,-3c-0.15,-0.38 -0.69,-1.63 -3,-3c-0.98,-0.58 -3.45,-2.01 -7,-2z" class="cls-29" id="liteecho-c"/>
|
||||
<path fill="url(#_未命名的渐变_71)" stroke-width="0px" d="m217.07715,329.0022c-0.92,-0.49 -2.08,-0.44 -3,0c-1.8,0.87 -1.99,2.91 -2,3l0,9c0,4 0.04,8 0,12c-0.03,3 0.1,6 0,9c-0.1,3.04 -0.29,5.38 1,8c0.45,0.91 1.42,2.89 3,3c1.63,0.11 2.9,-1.85 3,-2c1.44,-2.28 0.03,-4.1 0,-9c-0.02,-2.85 0,-5.82 2,-8c0.37,-0.4 1.8,-1.97 4,-2c2.44,-0.04 3.91,1.89 4,2c0.92,1.22 1,2.52 1,3c-0.33,1.67 -0.67,3.33 -1,5c-0.41,1.12 -0.91,2.85 -1,5c-0.09,2.3 -0.16,3.99 1,5c1.28,1.11 3.61,1.02 5,0c1.65,-1.21 1.8,-3.61 2,-7c0.08,-1.27 0.04,-2.31 0,-3l0,-8c-0.02,-0.84 -0.16,-2.36 -1,-4c-0.51,-1 -1.73,-2.93 -4,-4c-3.65,-1.72 -7.65,-0.14 -8,0c-1.5,0.62 -2.24,1.36 -3,1c-0.71,-0.34 -0.94,-1.4 -1,-4c-0.12,-5.04 0.61,-6.66 0,-10c-0.56,-3.06 -1.54,-3.76 -2,-4z" class="cls-17" id="liteecho-h"/>
|
||||
<path fill="url(#_未命名的渐变_58-2)" stroke-width="0px" d="m252.07715,348.0022c-4.27,-0.18 -7.17,2.27 -8,3c-3.05,2.66 -3.79,6 -4,7c-0.18,0.88 -1.21,5.84 2,10c2.08,2.69 4.82,3.62 6,4c1.29,0.42 4.46,1.39 8,0c2.95,-1.16 4.51,-3.31 5,-4c1.43,-2.02 1.81,-3.97 2,-5c0.46,-2.51 0.1,-4.49 0,-5c-0.67,-3.35 -2.52,-5.47 -3,-6c-0.69,-0.76 -3.46,-3.8 -8,-4z" class="cls-33" id="liteecho-o"/>
|
||||
<path stroke-miterlimit="10" stroke="#fff" fill="#fff" d="m250.57715,354.5022c-2.49,0.24 -3.75,2.55 -4,3c-0.09,0.17 -1.81,3.42 0,6c1.68,2.39 5.6,3.06 8,1c2.1,-1.8 2.13,-4.93 1,-7c-0.84,-1.54 -2.7,-3.22 -5,-3z" class="cls-35" id="liteecho-o-white"/>
|
||||
<path fill="url(#_未命名的渐变_3-3)" stroke-width="0px" d="m108.07715,274.0022c-0.71,0.63 -1.32,1.81 -1,3c0.38,1.44 1.86,1.95 2,2c0.77,0.25 1.44,0.12 2,0c4.88,-1.04 5.31,-1.31 6,-1c1.18,0.54 1.54,2.08 2,4c0.41,1.72 0.73,3.08 0,4c-0.65,0.81 -2.11,0.89 -5,1c-1.25,0.05 -2.29,0.03 -3,0c-0.53,0.14 -1.96,0.61 -3,2c-0.4,0.54 -1.21,1.61 -1,3c0.24,1.62 1.7,2.78 3,3c0.44,0.08 0.79,0.03 1,0c4.59,-0.65 6.89,-0.98 7,-1c2.55,-0.43 3.84,-0.64 4.45,-0.34c1.26,0.62 1.41,1.86 2.55,5.34c0,0 1.48,4.5 4,10c0.8,1.75 1.53,3.13 2,4c0.16,0.13 2.42,1.9 5,1c0.16,-0.06 2.44,-0.88 3,-3c0.21,-0.78 0.1,-1.42 0,-2c-0.27,-1.54 -0.96,-2.26 -2,-4c-0.12,-0.21 0.03,0.04 -2,-4c-0.67,-1.33 -1.32,-2.67 -2,-4c-1.91,-3.76 -2.3,-4.3 -2,-5c0.2,-0.46 0.49,-0.53 4,-2c1.13,-0.48 2.45,-1.03 4,-2c1.13,-0.71 1.71,-1.21 2,-2c0.06,-0.17 0.35,-1.03 0,-2c-0.48,-1.32 -1.88,-2.03 -3,-2c-0.31,0 -0.54,0.07 -0.66,0.11c-2.2,0.63 -4.77,1.67 -5.34,1.89c-2.21,0.88 -3.31,1.31 -4,1c-0.42,-0.19 -0.77,-0.54 -2,-4c-1.21,-3.42 -1.11,-3.77 -1,-4c0.42,-0.85 1.37,-0.79 6,-2c2.46,-0.64 3.57,-1.02 4,-2c0.3,-0.68 0.14,-1.39 0,-2c-0.09,-0.4 -0.54,-2.17 -2,-3c-1.17,-0.66 -2.4,-0.38 -4,0c-3.65,0.88 -4.88,2.55 -6,2c-0.55,-0.27 -1.15,-1.13 -1,-4c-0.42,-2 -0.76,-3 -1,-3l0,0c-0.76,-1.48 -2.4,-2.29 -4,-2c-1.51,0.27 -2.7,1.47 -3,3c-0.17,2.45 0.41,4.01 1,5c0.57,0.95 1.14,1.36 1,2c-0.24,1.14 -2.45,1.64 -4,2c-2.38,0.55 -3.11,0.2 -4,1z" class="cls-13" id="lite-jp-2"/>
|
||||
<path fill="url(#_未命名的渐变_5)" stroke-width="0px" d="m153.07715,286.0022c-0.46,1.12 -0.62,2.69 0,4c0.19,0.41 0.79,1.49 2,2c1.22,0.51 2.43,0.16 3,0c5,-1.43 7.51,-2.15 8,-2c4.42,1.34 3.64,20.08 7,22c2.14,1.22 4,1 4,1c1.43,-0.17 3.03,-0.97 3.02,-1c0,0 0,0 -0.02,0c0.27,-0.2 0.65,-0.52 1,-1c1.53,-2.12 0.85,-5.32 -1,-10c-3.3,-8.33 -6.24,-11.07 -5,-13c0.71,-1.1 1.84,-0.46 5.57,-2.36c2.51,-1.27 4.06,-2.06 4.43,-3.64c0.46,-1.98 -1.11,-4.46 -3,-5c-1.01,-0.29 -2.23,0.21 -4.65,1.22c-3.32,1.39 -3.58,2.02 -4.35,1.78c-2.25,-0.7 -0.9,-6.34 -4,-8c-1.22,-0.65 -2.84,-0.54 -4,0c-0.31,0.14 -1.56,0.73 -2,2c-0.24,0.7 -0.14,1.29 0,2c0.51,2.66 2.08,3.57 2,5.99c0,0 0,0 0,0c0.01,0 0.02,0 0.02,0c0.01,0.1 -10.37,-0.01 -12.02,4l0,0.01z" class="cls-5" id="yuki-jp-1"/>
|
||||
<path fill="url(#_未命名的渐变_69)" stroke-width="0px" d="m230.07715,276.0022c-0.23,-0.2 -0.95,-0.74 -2,-1c-0.58,-0.14 -1.39,-0.34 -2,0c-0.79,0.44 -0.86,1.6 -1,3c0,0 -0.62,6.23 -2,9c-0.43,0.86 -0.97,1.66 -0.97,1.66c-0.12,0.18 -0.24,0.35 -0.35,0.5c-0.2,0.27 -0.42,0.55 -0.68,0.84c-1.47,1.68 -5.93,4.2 -5.93,4.2c-3.23,1.82 -3.66,2.04 -4.07,2.8c-0.61,1.13 -0.9,2.9 0,4c0.8,0.97 2.17,0.99 3,1c0.7,0 3.05,-0.07 7.17,-3.17c1.64,-1.24 3.78,-2.89 5.83,-5.83c3.15,-4.53 3.72,-8.76 4,-11c0.52,-4.2 -0.4,-5.48 -1,-6z" class="cls-31" id="yuki-jp-2-3"/>
|
||||
<path fill="url(#_未命名的渐变_67)" stroke-width="0px" d="m167.88715,359.1822c-1.13,0.52 -1.6,0.87 -1.74,1.41c-0.25,0.96 0.69,2.15 1.62,2.72c2.35,1.44 5.96,-0.31 7.81,-1.71c1.14,-0.86 3.66,-2.76 3.81,-5.96c0.17,-3.63 -2.85,-5.97 -3.45,-6.41c-3.13,-2.29 -6.78,-1.55 -8.46,-1.22c-3.41,0.68 -5.55,2.66 -6.41,3.45c-0.83,0.76 -2.15,2.02 -3.08,4.1c-1.85,4.16 -0.53,8.09 -0.22,8.94c1.05,2.88 2.91,4.54 3.46,5c0.5,0.42 2.09,1.69 4.42,2.34c1.32,0.37 2.61,0.46 3.57,0.53c1.13,0.08 1.96,0.06 2.24,0.05c1.11,-0.04 2.04,-0.06 3.14,-0.37c1.14,-0.32 1.92,-0.79 2.71,-1.28c0.29,-0.18 1.58,-0.98 3.19,-2.61c2.48,-2.49 3.07,-4.25 3.25,-4.84c0.2,-0.68 0.24,-1.11 0.07,-1.51c-0.43,-1.07 -2.08,-1.58 -3.36,-1.31c-0.51,0.11 -0.88,0.32 -1.13,0.48c-1.71,1.09 -2.2,2.45 -3.11,3.52c-1.84,2.18 -5.11,2.86 -7.32,2.49c-1.39,-0.23 -2.59,-0.92 -2.66,-0.96c-0.4,-0.23 -1.08,-0.67 -1.76,-1.38c-0.56,-0.59 -2.08,-2.19 -2.13,-4.52c-0.04,-2.02 1.04,-3.46 1.44,-3.99c1.59,-2.11 4.14,-3.1 6.33,-2.98c0.79,0.04 1.86,0.12 2.66,0.96c0.63,0.67 1.18,1.91 0.74,2.79c-0.57,1.15 -2.28,0.69 -5.65,2.26l0.02,0.01z" class="cls-14" id="liteecho-e2"/>
|
||||
</g>
|
||||
<circle id="svg_6" fill="url(#_未命名的渐变_6)" stroke-width="0px" r="4" cy="301.0022" cx="162.07715" class="cls-23"/>
|
||||
<ellipse id="svg_7" fill="url(#_未命名的渐变_7)" stroke-width="0px" ry="2.5" rx="2.51" cy="270.5022" cx="183.56715" class="cls-11"/>
|
||||
<ellipse id="svg_8" fill="url(#_未命名的渐变_7-2)" stroke-width="0px" ry="2.5" rx="2.51" cy="273.5022" cx="176.56715" class="cls-26"/>
|
||||
<circle id="svg_9" fill="url(#_未命名的渐变_7-3)" stroke-width="0px" r="4" cy="294.3922" cx="185.05715" class="cls-10"/>
|
||||
<circle id="svg_10" stroke-width="0px" fill="url(#_未命名的渐变_8)" r="4" cy="281.2622" cx="209.49715" class="cls-1"/>
|
||||
<circle id="svg_11" fill="url(#_未命名的渐变_55)" stroke-width="0px" r="4" cy="275.2622" cx="217.49715" class="cls-6"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 34 KiB |
95
docs/public/plugins.json
Normal file
95
docs/public/plugins.json
Normal file
@ -0,0 +1,95 @@
|
||||
[
|
||||
{
|
||||
"module_name": "liteyukibot-plugin-nonebot",
|
||||
"project_link": "liteyukibot-plugin-nonebot",
|
||||
"name": "NoneBot插件",
|
||||
"desc": "在轻雪中使用NoneBot,内置轻雪--NoneBot会话控制器插件,为NoneBot开发者提供了更多便捷功能(已内置)",
|
||||
"author": "snowykami",
|
||||
"homepage": "https://github.com/LiteyukiStudio/liteyukibot-plugin-nonebot",
|
||||
"tags": [
|
||||
{
|
||||
"label": "app",
|
||||
"color": "#aeeaa8"
|
||||
}
|
||||
],
|
||||
"is_official": true,
|
||||
"type": "application",
|
||||
"valid": true,
|
||||
"version": "rolling"
|
||||
},
|
||||
{
|
||||
"module_name": "liteyukibot-plugin-antidislink",
|
||||
"project_link": "liteyukibot-plugin-antidislink",
|
||||
"name": "防断联插件",
|
||||
"desc": "防止你的群友断联化",
|
||||
"author": "snowykami",
|
||||
"homepage": "https://github.com/snowykami/liteyukibot-plugin-antidislink",
|
||||
"tags": [
|
||||
{
|
||||
"label": "app",
|
||||
"color": "#aeeaa8"
|
||||
},
|
||||
{
|
||||
"label": "dislink",
|
||||
"color": "#d0e9ff"
|
||||
}
|
||||
],
|
||||
"is_official": true,
|
||||
"type": "application",
|
||||
"valid": true,
|
||||
"version": "rolling"
|
||||
},
|
||||
{
|
||||
"module_name": "liteyukibot-plugin-lagrange",
|
||||
"project_link": "liteyukibot-plugin-lagrange",
|
||||
"name": "LagrangePlugin",
|
||||
"desc": "在轻雪中使用lagrange-python!",
|
||||
"author": "snowykami",
|
||||
"homepage": "https://github.com/snowykami/liteyukibot-plugin-lagrange",
|
||||
"tags": [
|
||||
{
|
||||
"label": "app",
|
||||
"color": "#aeeaa8"
|
||||
},
|
||||
{
|
||||
"label": "lagrange",
|
||||
"color": "#ff89f7"
|
||||
}
|
||||
],
|
||||
"is_official": true,
|
||||
"type": "application",
|
||||
"valid": true,
|
||||
"version": "rolling"
|
||||
},
|
||||
{
|
||||
"module_name": "liteyukibot-plugin-htmlrender",
|
||||
"project_link": "liteyukibot-plugin-htmlrender",
|
||||
"name": "htmlrender",
|
||||
"desc": "使用系统中的浏览器对网页进行渲染,便于其他插件使用",
|
||||
"author": "EillesWan",
|
||||
"homepage": "https://github.com/LiteyukiStudio/liteyukibot-plugin-htmlrender",
|
||||
"tags": [
|
||||
{
|
||||
"label": "ch",
|
||||
"color": "#4285f4"
|
||||
},
|
||||
{
|
||||
"label": "ro",
|
||||
"color": "#db4437"
|
||||
},
|
||||
{
|
||||
"label": "mi",
|
||||
"color": "#f4b400"
|
||||
},
|
||||
{
|
||||
"label": "um",
|
||||
"color": "#0f9d58"
|
||||
|
||||
}
|
||||
],
|
||||
"is_official": true,
|
||||
"type": "application",
|
||||
"valid": true,
|
||||
"version": "rolling"
|
||||
}
|
||||
]
|
77
docs/public/resources.json
Normal file
77
docs/public/resources.json
Normal file
@ -0,0 +1,77 @@
|
||||
[
|
||||
{
|
||||
"name": "KawaiiStatus",
|
||||
"author": "SnowyKami",
|
||||
"description": "可爱的状态卡片,仿照koishi的制作",
|
||||
"link": "https://cdn.liteyuki.icu/static/lrp/KawaiiStatus.zip"
|
||||
},
|
||||
{
|
||||
"name": "MiSans字体包",
|
||||
"author": "SnowyKami",
|
||||
"description": "小米官方字体MiSans",
|
||||
"link": "https://cdn.liteyuki.icu/static/lrp/MiSansFonts.zip"
|
||||
},
|
||||
{
|
||||
"name": "MapleMono字体包",
|
||||
"author": "SnowyKami",
|
||||
"description": "适用于字母的字体包",
|
||||
"link": "https://cdn.liteyuki.icu/static/lrp/MapleMonoFonts.zip"
|
||||
},
|
||||
{
|
||||
"name": "野兽先辈主题HomoTheme",
|
||||
"author": "SnowyKami",
|
||||
"description": "野兽先辈主题包,114514!",
|
||||
"link": "https://cdn.liteyuki.icu/static/lrp/HomoTheme.zip"
|
||||
},
|
||||
{
|
||||
"name": "自定义设备信息",
|
||||
"author": "SnowyKami",
|
||||
"description": "自定义服务端的设备信息,自行修改使用",
|
||||
"link": "https://cdn.liteyuki.icu/static/lrp/custom-device.zip"
|
||||
},
|
||||
{
|
||||
"name": "轻雪傲娇系词库",
|
||||
"author": "SnowyKami",
|
||||
"description": "使用https://github.com/Kyomotoi/AnimeThesaurus的词库",
|
||||
"link": "https://cdn.liteyuki.icu/static/lrp/liteyuki_words_aojiao.zip"
|
||||
},
|
||||
{
|
||||
"name": "轻雪可爱系词库",
|
||||
"author": "SnowyKami",
|
||||
"description": "使用https://github.com/Kyomotoi/AnimeThesaurus的词库",
|
||||
"link": "https://cdn.liteyuki.icu/static/lrp/liteyuki_words_kawaii.zip"
|
||||
},
|
||||
{
|
||||
"name": "轻雪Kakyo语言包",
|
||||
"author": "Nanaloveyuki",
|
||||
"description": "Liteyuki Bot的语言包,用于提供多种语言的翻译。",
|
||||
"link": "https://github.com/Nanaloveyuki/liteyuki-langpack/releases/download/KakyoVer/Kakyo-pack.zip"
|
||||
},
|
||||
{
|
||||
"name": "更多背景模板包",
|
||||
"author": "snowykami",
|
||||
"description": "自定义各种卡片的背景",
|
||||
"link": "https://cdn.liteyuki.icu/static/lrp/morebg.zip"
|
||||
},
|
||||
{
|
||||
"name": "测试资源包",
|
||||
"desc": "test",
|
||||
"link": "https://cdn.liteyuki.icu/static/lrp/liteyuki_words_aojiao.zip",
|
||||
"homepage": "",
|
||||
"author": "snowykami"
|
||||
},
|
||||
{
|
||||
"name": "轻雪Kakyo语言包 稳定版",
|
||||
"description": "Liteyuki Bot的语言包,用于提供多种语言的翻译。",
|
||||
"link": "https://cdn.liteyuki.icu/static/lrp/kakyo.zip",
|
||||
"homepage": "https://github.com/Nanaloveyuki/liteyuki-langpack",
|
||||
"author": "Nanaloveyuki"
|
||||
},
|
||||
{
|
||||
"name": "测试资源包2",
|
||||
"description": "test",
|
||||
"link": "https://cdn.liteyuki.icu/static/lrp/liteyuki_words_aojiao.zip",
|
||||
"homepage": "",
|
||||
"author": "snowykami"
|
||||
}
|
||||
]
|
15
docs/tsconfig.json
Normal file
15
docs/tsconfig.json
Normal file
@ -0,0 +1,15 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es6",
|
||||
"module": "commonjs",
|
||||
"esModuleInterop": true,
|
||||
"skipLibCheck": true,
|
||||
"forceConsistentCasingInFileNames": true
|
||||
},
|
||||
"include": [
|
||||
"components/**/*.ts",
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules"
|
||||
]
|
||||
}
|
71
docs/zh/deploy/config.md
Normal file
71
docs/zh/deploy/config.md
Normal file
@ -0,0 +1,71 @@
|
||||
---
|
||||
title: 配置
|
||||
order: 2
|
||||
---
|
||||
# 配置
|
||||
|
||||
轻雪支持`yaml`、`json`和`toml`作为配置文件,取决于你个人的喜好
|
||||
|
||||
首次运行后生成`config.yml`和`config`目录,你可修改配置项后重启轻雪,绝大多数情况下,你只需要修改`superusers`及`nickname`字段即可
|
||||
|
||||
启动时会加载项目目录下`config.yml/yaml/json/toml`和`config`目录下的所有配置文件,你可在`config`目录下创建多个配置文件,轻雪会自动合并这些配置文件
|
||||
|
||||
## **基础配置项**
|
||||
|
||||
```yaml
|
||||
nonebot:
|
||||
# Nonebot机器人的配置,以前的最外层配置项仍可为Nonebot服务,但是部分内容会被覆盖,请尽快迁移
|
||||
command_start: [ "/", "" ] # 指令前缀,若没有""空命令头,请开启alconna_use_command_start保证alconna解析正常
|
||||
host: 127.0.0.1 # 监听地址,默认为本机,若要接收外部请求请填写0.0.0.0
|
||||
port: 20216 # 绑定端口
|
||||
nickname: [ "liteyuki" ] # 机器人昵称列表
|
||||
superusers: [ "1919810" ] # 超级用户列表
|
||||
liteyuki:
|
||||
# 写在外层的配置项将会被覆盖,建议迁移到liteyuki下
|
||||
log_level: "INFO" # 日志等级
|
||||
log_icon: true # 是否显示日志等级图标(某些控制台字体不可用)
|
||||
auto_report: true # 是否自动上报问题给轻雪服务器
|
||||
auto_update: true # 是否自动更新轻雪,每天4点检查更新
|
||||
plugins: [ ] # 轻雪插件列表
|
||||
plugin_dirs: [ ] # 轻雪插件目录列表
|
||||
```
|
||||
|
||||
## **其他配置**
|
||||
|
||||
以下为默认值,如需自定义请手动添加
|
||||
|
||||
```yaml
|
||||
# 高级NoneBot配置
|
||||
nonebot:
|
||||
onebot_access_token: "" # 访问令牌,对公开放时建议设置
|
||||
default_language: "zh-CN" # 默认语言
|
||||
alconna_auto_completion: false # alconna是否自动补全指令,默认false,建议开启
|
||||
safe_mode: false # 安全模式,开启后将不会加载任何第三方NoneBot插件
|
||||
# 其他Nonebot插件的配置项
|
||||
custom_config_1: "custom_value1"
|
||||
custom_config_2: "custom_value2"
|
||||
|
||||
# 开发者选项
|
||||
liteyuki:
|
||||
allow_update: true # 是否允许更新
|
||||
debug: false # 轻雪调试,开启会自动重载Bot或者资源,其他插件自带的调试功能也将开启
|
||||
dev_mode: false # 开发者模式,开启后将会启动监视者,监视文件变化并自动重载
|
||||
|
||||
...
|
||||
```
|
||||
|
||||
## **示例:与NoneBot对接的OneBot实现端配置**
|
||||
|
||||
生产环境中推荐反向WebSocket
|
||||
不同的实现端给出的字段可能不同,但是基本上都是一样的,这里给出一个参考值
|
||||
|
||||
| 字段 | 参考值 | 说明 |
|
||||
|-------------|------------------------------------|----------------------------------|
|
||||
| 协议 | 反向WebSocket | 推荐使用反向ws协议进行通信,即轻雪作为服务端 |
|
||||
| 地址 | ws://127.0.0.1:20216/onebot/v11/ws | 地址取决于配置文件,本机默认为`127.0.0.1:20216` |
|
||||
| AccessToken | `""` | 如果你给轻雪配置了`AccessToken`,请在此填写相同的值 |
|
||||
- 要使用其他通信方式请访问[OneBot Adapter](https://onebot.adapters.nonebot.dev/)获取详细信息
|
||||
|
||||
## **其他**
|
||||
|
||||
- 轻雪不局限于OneBot适配器,你可以使用NoneBot2支持的任何适配器或使用轻雪讯息传递插件
|
52
docs/zh/deploy/fandq.md
Normal file
52
docs/zh/deploy/fandq.md
Normal file
@ -0,0 +1,52 @@
|
||||
---
|
||||
title: 答疑
|
||||
order: 3
|
||||
---
|
||||
# 答疑
|
||||
## **常见问题**
|
||||
|
||||
- 设备上Python环境太乱了,pip和python不对应怎么办?
|
||||
- 请使用`/path/to/python -m pip install -r requirements.txt`来安装依赖,
|
||||
然后用`/path/to/python main.py`来启动Bot,
|
||||
其中`/path/to/python`是你要用来运行Bot的可执行文件
|
||||
|
||||
- 为什么我启动后机器人没有反应?
|
||||
- 请检查配置文件的`command_start`或`superusers`,确认你有权限使用命令并按照正确的命令发送
|
||||
- 确认命令头没有和`nickname{}`冲突,例如一个命令是`help`,但是`Bot`昵称有一个`help`,那么将会被解析为nickname而不是命令
|
||||
|
||||
- 更新轻雪失败,报错`InvalidGitRepositoryError`
|
||||
- 请正确安装`Git`,并使用克隆而非直接下载的方式部署轻雪
|
||||
|
||||
- 怎么登录聊天平台,例如QQ?
|
||||
- 你有这个问题说明你不是很了解这个项目,本项目不负责实现登录功能,只负责处理和回应消息,登录功能由实现端(协议端)提供,
|
||||
实现端本身不负责处理响应逻辑,将消息按照OneBot标准处理好上报给轻雪
|
||||
你需要使用Onebot标准的实现端来连接到轻雪并将消息上报给轻雪,下面已经列出一些推荐的实现端
|
||||
- `Playwright`安装失败
|
||||
- 输入`playwright install`安装浏览器
|
||||
- 有的插件安装后报错无法启动
|
||||
- 请先查阅插件文档,确认插件必要配置项完好后,仍然出现问题,请联系插件作者或在安全模式`safe_mode: true`下启动轻雪,在安全模式下你可以使用`npm uninstall`卸载问题插件
|
||||
- 其他问题
|
||||
-
|
||||
加入QQ群[775840726](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=SzmDYbfR6jY94o9KFNon7AwelRyI6M_u&authKey=ygeBdEmdFNyCWuNR4w0M1M8%2B5oDg7k%2FDfN0tzBkYcnbB%2FGHNnlVEnCIGbdftsnn7&noverify=0&group_code=775840726)
|
||||
|
||||
## **推荐方案(QQ)**
|
||||
|
||||
1. [Lagrange.OneBot](https://github.com/KonataDev/Lagrange.Core),基于NTQQ的OneBot实现,目前Markdown消息支持Lagrange
|
||||
2. [LLOneBot](https://github.com/LLOneBot/LLOneBot),NTQQ的OneBot插件,需要安装NTQQ
|
||||
3. [OpenShamrock](https://github.com/whitechi73/OpenShamrock),基于Lsposed的OneBot11实现
|
||||
4. [TRSS-Yunzai](https://github.com/TimeRainStarSky/Yunzai),基于`node.js`,可使用`ws-plugin`进行通信
|
||||
5. [go-cqhttp](https://github.com/Mrs4s/go-cqhttp),`go`语言实现的OneBot11实现端,目前可用性较低
|
||||
6. [Gensokyo](https://github.com/Hoshinonyaruko/Gensokyo),基于 OneBot QQ官方机器人Api Golang 原生实现,需要官方机器人权限
|
||||
7. 人工实现的`Onebot`协议,自己整一个WebSocket客户端,看着QQ的消息,然后给轻雪传输数据
|
||||
|
||||
## **推荐方案(Minecraft)**
|
||||
|
||||
1. [MinecraftOneBot](https://github.com/snowykami/MinecraftOnebot),我们专门为Minecraft开发的服务器Bot,支持OneBotV11标准
|
||||
|
||||
使用其他项目连接请先自行查阅文档,若有困难请联系对应开发者而不是Liteyuki的开发者
|
||||
|
||||
## **鸣谢**
|
||||
|
||||
- [Nonebot2](https://nonebot.dev)提供的框架支持
|
||||
- [nonebot-plugin-alconna](https://github.com/ArcletProject/nonebot-plugin-alconna)提供的命令解析功能
|
||||
- [MiSans](https://hyperos.mi.com/font/zh/),[MapleMono](https://gitee.com/mirrors/Maple-Mono)提供的字体,且遵守了相关字体开源协议
|
56
docs/zh/deploy/install.md
Normal file
56
docs/zh/deploy/install.md
Normal file
@ -0,0 +1,56 @@
|
||||
---
|
||||
title: 安装
|
||||
order: 1
|
||||
---
|
||||
# 安装
|
||||
|
||||
## **常规部署**
|
||||
|
||||
1. 安装 [`Git`](https://git-scm.com/download/) 和 [`Python3.10+`](https://www.python.org/downloads/release/python-31010/) 环境
|
||||
|
||||
```bash
|
||||
# 克隆项目到本地,轻雪使用Git进行版本管理,该步骤为必要项
|
||||
git clone https://github.com/LiteyukiStudio/LiteyukiBot --depth=1 # 若你不能访问Github,可以使用Liteyuki镜像:https://git.liteyuki.icu/LiteyukiStudio/LiteyukiBot
|
||||
|
||||
# 切换到Bot目录下
|
||||
cd LiteyukiBot
|
||||
# 安装依赖
|
||||
pip install -r requirements.txt
|
||||
# 启动Bot
|
||||
python main.py
|
||||
```
|
||||
|
||||
> [!tip]
|
||||
> 推荐使用虚拟环境来运行轻雪,以避免依赖冲突,你可以使用`python -m venv .venv`来创建虚拟环境,然后使用`.venv\Scripts\activate`来激活虚拟环境(Linux下使用`source .venv/bin/activate`激活)
|
||||
|
||||
## **使用Docker构建**
|
||||
|
||||
1. 安装 [`Docker`](https://docs.docker.com/get-docker/)
|
||||
2. 克隆项目 `git clone https://github.com/LiteyukiStudio/LiteyukiBot --depth=1`
|
||||
3. 进入轻雪目录 `cd LiteyukiBot`
|
||||
4. 构建镜像 `docker build -t liteyukibot .`
|
||||
5. 启动容器 `docker run -p 20216:20216 -v $(pwd):/liteyukibot -v $(pwd)/.cache:/root/.cache liteyukibot`
|
||||
|
||||
> [!tip]
|
||||
> Windows请使用项目绝对目录`/path/to/LiteyukiBot`代替`$(pwd)` <br>
|
||||
> 若你修改了端口号请将`20216:20216`中的`20216`替换为你的端口号
|
||||
|
||||
## **使用TRSS Scripts部署**
|
||||
[TRSS_Liteyuki轻雪机器人管理脚本](https://timerainstarsky.github.io/TRSS_Liteyuki/),该功能由TRSS提供支持,不是LiteyukiBot官方提供的功能,推荐使用`Arch Linux`
|
||||
|
||||
|
||||
## **装置要求**
|
||||
|
||||
- Windows系统版本最低`Windows10+`/`Windows Server 2019+`
|
||||
- Linux系统要支持Python3.10+,推荐`Ubuntu 20.04+`(~~别用你那b CentOS~~)
|
||||
- CPU: 至少`1vCPU`
|
||||
- 内存: Bot无其他插件会占用`300~500MB`,包括`chromium` 及 `node`等进程,其他插件占用视具体插件而定,建议`1GB`以上
|
||||
- 硬盘: 至少`1GB`空间
|
||||
|
||||
> [!warning]
|
||||
> 如果装置上有多个环境,请使用`path/to/python -m pip install -r requirements.txt`来安装依赖,`path/to/python`为你的Python可执行文件路径
|
||||
|
||||
> [!warning]
|
||||
> 轻雪的更新功能依赖Git,如果你没有安装Git直接下载源代码运行,你将无法使用更新功能
|
||||
|
||||
#### 其他问题请移步至[答疑](./fandq)
|
21
docs/zh/dev/best_practices.md
Normal file
21
docs/zh/dev/best_practices.md
Normal file
@ -0,0 +1,21 @@
|
||||
---
|
||||
title: 最佳实践
|
||||
order: 10
|
||||
---
|
||||
|
||||
# 最佳实践
|
||||
|
||||
## 机器人应用
|
||||
- [LiteyukiBot](https://github.com/LiteyukiStudio/LiteyukiBot):轻雪官方机器人
|
||||
- [LiteyukiBot-TriM](https://github.com/TriM-Organization/LiteyukiBot-TriM):TriM 官方定制轻雪机器人
|
||||
- [Liteyuki Marsho](https://git.liteyuki.icu/LiteyukiStudio/marsho-alpha):基于 轻雪 和 github model 的可爱猫娘机器人
|
||||
|
||||
## 插件
|
||||
- [liteyukibot-plugin-nonebot](https://github.com/LiteyukiStudio/liteyukibot-plugin-nonebot):轻雪机器人 NoneBot 插件,让轻雪支持 NoneBot
|
||||
- [nonebot-plugin-liteyukibot](https://github.com/LiteyukiStudio/nonebot-plugin-liteyukibot):NoneBot 插件,让 NoneBot 支持轻雪
|
||||
|
||||
## 其他
|
||||
- [liteyuki starmap](https://starmap.liteyuki.icu):轻雪官方星图,展示轻雪的所有实例及其位置分布
|
||||
- [TRSS_Liteyuki](https://timerainstarsky.github.io/TRSS_Liteyuki/):TRSS LiteyukiBot 管理脚本
|
||||
- [litedoc](https://github.com/LiteyukiStudio/litedoc):轻雪API文档生成器
|
||||
- liteyukibot-api (闭源):轻雪机器人统计接口
|
99
docs/zh/dev/comm.md
Normal file
99
docs/zh/dev/comm.md
Normal file
@ -0,0 +1,99 @@
|
||||
---
|
||||
title: 进程通信
|
||||
order: 4
|
||||
---
|
||||
|
||||
# **通道通信**
|
||||
|
||||
### 简介
|
||||
|
||||
轻雪运行在主进程 MainProcess 里,其他插件框架进程是伴随的子进程,因此无法通过内存共享和直接对象传递的方式进行通信,
|
||||
轻雪提供了一个通道[`Channel`](./api/comm/channel#class-channel-generic-t)用于跨进程通信,
|
||||
你可以通过[`Channel`](./api/comm/channel#class-channel-generic-t)发送消息给其他进程,也可以监听其他进程的消息。
|
||||
|
||||
例如子进程接收到用户信息需要重启机器人,这时可以通过通道对主进程发送消息,主进程接收到消息后重启对应子进程。
|
||||
|
||||
### 示例
|
||||
|
||||
通道是全双工的,有两种接收模式,但一个通道只能使用一种,即被动模式和主动模式,被动模式由`chan.on_receive()`装饰回调函数实现,主动模式需调用`chan.receive()`实现
|
||||
|
||||
- 创建子进程的同时会初始化一个被动通道和一个主动通道,且通道标识为`{process_name}-active`和`{process_name}-passive`,
|
||||
- 主进程中通过`get_channel`函数获取通道对象
|
||||
- 子进程中导入单例`active_channel`及`passive_channel`即可
|
||||
|
||||
> 在轻雪插件中(主进程中)
|
||||
|
||||
```python
|
||||
import asyncio
|
||||
|
||||
from liteyuki.comm import get_channel, Channel
|
||||
from liteyuki import get_bot
|
||||
|
||||
# get_channel函数获取通道对象,参数为调用set_channel时的通道标识
|
||||
channel_passive = get_channel("nonebot-passive") # 获取被动通道
|
||||
channel_active = get_channel("nonebot-active") # 获取主动通道
|
||||
liteyuki_bot = get_bot()
|
||||
|
||||
|
||||
# 注册一个函数在轻雪启动后运行
|
||||
@liteyuki_bot.on_after_start
|
||||
async def send_data():
|
||||
while True:
|
||||
channel_passive.send("I am liteyuki main process passive")
|
||||
channel_active.send("I am liteyuki main process active")
|
||||
await asyncio.sleep(3) # 每3秒发送一次消息
|
||||
```
|
||||
|
||||
> 在子进程中(例如NoneBot插件中)
|
||||
|
||||
```python
|
||||
from nonebot import get_driver
|
||||
from liteyuki.comm import active_channel, passive_channel # 子进程中获取通道直接导入进程全局单例即可
|
||||
from liteyuki.log import logger
|
||||
|
||||
driver = get_driver()
|
||||
|
||||
|
||||
# 被动模式,通过装饰器注册一个函数在接收到消息时运行,每次接收到字符串数据时都会运行
|
||||
@passive_channel.on_receive(filter_func=lambda data: isinstance(data, str))
|
||||
async def on_passive_receive(data):
|
||||
logger.info(f"Passive receive: {data}")
|
||||
|
||||
|
||||
# 注册一个函数在NoneBot启动后运行
|
||||
@driver.on_startup
|
||||
def on_startup():
|
||||
while True:
|
||||
data = active_channel.receive()
|
||||
logger.info(f"Active receive: {data}")
|
||||
```
|
||||
|
||||
> 启动后控制台输出
|
||||
|
||||
```log
|
||||
0000-00-00 00:00:00 [ℹ️信息] Passive receive: I am liteyuki main process passive
|
||||
0000-00-00 00:00:00 [ℹ️信息] Active receive: I am liteyuki main process active
|
||||
0000-00-00 00:00:03 [ℹ️信息] Passive receive: I am liteyuki main process passive
|
||||
0000-00-00 00:00:03 [ℹ️信息] Active receive: I am liteyuki main process active
|
||||
...
|
||||
```
|
||||
|
||||
## **共享内存通信**
|
||||
|
||||
### 简介
|
||||
|
||||
- 相比于普通进程通信,内存共享使得代码编写更加简洁,轻雪框架提供了一个内存共享通信的接口,你可以通过`storage`模块实现内存共享通信,该模块封装通道实现
|
||||
- 内存共享是线程安全的,你可以在多个线程中读写共享内存,线程锁会自动保护共享内存的读写操作
|
||||
|
||||
### 示例
|
||||
|
||||
> 在任意进程中均可使用
|
||||
|
||||
```python
|
||||
from liteyuki.comm.storage import shared_memory
|
||||
|
||||
shared_memory.set("key", "value") # 设置共享内存
|
||||
value = shared_memory.get("key") # 获取共享内存
|
||||
```
|
||||
|
||||
源代码:[liteyuki/comm/storage.py](https://github.com/LiteyukiStudio/LiteyukiBot/blob/main/liteyuki/comm/storage.py)
|
71
docs/zh/dev/guide.md
Normal file
71
docs/zh/dev/guide.md
Normal file
@ -0,0 +1,71 @@
|
||||
---
|
||||
title: 开发指南
|
||||
order: 0
|
||||
---
|
||||
|
||||
|
||||
<script setup>
|
||||
import ContributorBar from '../../components/ContributorBar.vue'
|
||||
</script>
|
||||
|
||||
|
||||
# 开发指南
|
||||
|
||||
## 如何开发
|
||||
插件开发无需阅读本文档,请阅读[插件开发文档](./plugin)和[API 引用](./api/)。
|
||||
|
||||
如需往轻雪仓库提交代码,请阅读以下内容:
|
||||
1. 首先[fork](https://github.com/LiteyukiStudio/LiteyukiBot/fork)一份轻雪仓库到你的个人/组织账户下。
|
||||
2. 在你的仓库中进行开发。
|
||||
3. 在你的仓库中创建一个新的分支,将你的代码提交到这个分支。
|
||||
4. 在你的仓库中创建一个Pull Request,将你的分支合并到轻雪仓库的`main`分支。
|
||||
|
||||
参与开发默认你已经了解Python语言和轻雪框架的基本使用方法,如果是文档相关的开发,请确保你了解Markdown语法和基本前端知识。
|
||||
出现冲突请与仓库维护者联系。
|
||||
|
||||
### 建议
|
||||
- 开发过程中可以使用`mypy`, `flake8`, `black`等工具进行代码检查和格式化。
|
||||
- 启用开发者模式,可以在`config.yml`中设置`dev_mode: true`,这样可以在控制台看到更多的调试信息。
|
||||
|
||||
## 项目结构
|
||||
- `main.py`: 机器人入口文件
|
||||
- `liteyuki`: 框架目录(liteyukibot)
|
||||
- `src`: 机器人源码目录
|
||||
- `liteyuki_plugins`: 轻雪插件目录
|
||||
- `nonebot_plugins`: Nonebot插件目录
|
||||
- `resources`: 内置资源包目录
|
||||
- `utils`: 工具目录
|
||||
- `tests`: 单元测试目录
|
||||
- `docs`: 文档目录
|
||||
- `en`: 英文文档
|
||||
- `zh`: 中文文档
|
||||
- `docker`: Docker相关内容目录
|
||||
|
||||
其中,`liteyuki`框架是包含在`LiteyukiBot`应用中的,所以此项目既是`LiteyukiBot`应用仓库,同时也是`liteyuki`框架的仓库
|
||||
|
||||
`src`目录下为应用程序部分,业务逻辑请在此目录下进行开发,`liteyuki_plugins`和`nonebot_plugins`目录下为插件目录,插件开发请在此目录下进行开发。
|
||||
|
||||
## 规范化
|
||||
- 代码请遵循[`PEP8`](https://pep8.org/)和[`Google Python Style Guide`](https://google.github.io/styleguide/pyguide.html)
|
||||
- 提交到轻雪仓库的代码,请遵循以下规范:
|
||||
- 请确保代码是可运行的,没有危害的。
|
||||
- 请确保代码的类型提示是正确的。
|
||||
- 请确保注释风格为[`Google Docstring`](https://google.github.io/styleguide/pyguide.html)或[`Liteyuki Docstring`](https://github.com/LiteyukiStudio/litedoc?tab=readme-ov-file#liteyuki-docstring)(推荐)以保证Litedoc能够正确解析并生成文档。
|
||||
- 字符串请使用双引号`"`,尽量避免使用单引号`'`,但在嵌套字符串时可以使用单引号。
|
||||
- 若有面向普通用户部分,请确保文档是完善的(每种语言都有对应的文档)。
|
||||
- commit message请遵循以下规范:
|
||||
- 应在每次完成一个功能或修复一个bug后提交代码,不要将多个功能或多个bug的代码混在一起提交。
|
||||
- 我们使用gitmoji来标记commit的类型,如`:sparkles:`表示引入新功能,`:bug:`表示修复bug等,具体请参考[gitmoji](https://gitmoji.dev/)
|
||||
- commit message的格式为`<emoji> [type:] <message>`,如`:sparkles: feat: 给event添加新字段`,
|
||||
其中`type`字段为commit类型且**可选**,message字段为简短的描述,总结来说就是你干了什么。
|
||||
- 不限制commit message的语言,有条件可以使用中英双语。
|
||||
- 文档请遵循[`Markdown`](https://www.markdownguide.org/)语法,并且支持vitepress相关内容:
|
||||
- 修订文档时,每个语言的文档都要修订。
|
||||
- 请确保文档内的链接是正确的,不要出现无法访问的死链接。
|
||||
- 请确保**用户文档**是通俗易懂的,**开发文档**是详细的。
|
||||
|
||||
## 最后
|
||||
- 本项目是一个非盈利的开源项目,我们欢迎任何人参与开发,你的贡献将会使轻雪变得更好。
|
||||
|
||||
|
||||
<ContributorBar />
|
72
docs/zh/dev/lyfunc.md
Normal file
72
docs/zh/dev/lyfunc.md
Normal file
@ -0,0 +1,72 @@
|
||||
---
|
||||
title: 轻雪函数
|
||||
order: 2
|
||||
---
|
||||
|
||||
# **轻雪函数**
|
||||
|
||||
轻雪函数 Liteyuki Function 是轻雪的一个功能,它允许你在轻雪中运行一些自定义的由数据驱动的命令,类似于Minecraft的mcfunction,属于资源包的一部分,但需单独起篇幅.
|
||||
|
||||
### **函数文件**
|
||||
|
||||
函数文件放在资源包的`functions`目录下,文件名以`.mcfunction` `.lyfunction` `.lyf`结尾,例如`test.mcfunction`,文件内容为一系列的命令,每行一个命令,支持单行注释`#`(编辑时的语法高亮可采取`shell`格式),例如:
|
||||
|
||||
```shell
|
||||
# 在发信器输出"hello world"
|
||||
cmd echo hello world
|
||||
|
||||
# 如果你想同时输出多行内容可以尝试换行符(Python格式)
|
||||
cmd echo hello world\nLiteyuki bot
|
||||
```
|
||||
|
||||
也支持句末注释,例如:
|
||||
```shell
|
||||
cmd echo hello world # 输出"hello world"
|
||||
```
|
||||
|
||||
### **命令文档**
|
||||
|
||||
```shell
|
||||
var <var1=value1> [var2=value2] ... # 定义变量
|
||||
cmd <command> # 在设备上执行命令
|
||||
api <api_name> [var=value...] # 调用Bot API
|
||||
function <func_name> # 调用函数,可递归
|
||||
sleep <time> # 异步等待,单位s
|
||||
nohup <command> # 使用新的task执行命令,即不等待
|
||||
end # 结束函数关键字,包括子task
|
||||
await # 等待所有异步任务结束,若函数中启动了其他task,需要在最后调用,否则task对象会被销毁
|
||||
```
|
||||
|
||||
|
||||
#### **示例**
|
||||
|
||||
```shell
|
||||
# 疯狂戳好友
|
||||
# 使用 /function poke user_id=123456 执行
|
||||
# 每隔0.2s戳两次,无限戳,会触发最大递归深度限制
|
||||
# 若要戳20s后停止,则需要删除await,添加sleep 20和end
|
||||
api friend_poke user_id=user_id
|
||||
api friend_poke user_id=user_id
|
||||
sleep 0.2
|
||||
nohup function poke
|
||||
await
|
||||
```
|
||||
|
||||
### **API**
|
||||
|
||||
理论上所有基于onebotv11的api都可调用,不同Adapter api也有差别.
|
||||
|
||||
[Onebot v11 API文档](https://283375.github.io/onebot_v11_vitepress/api/index.html)
|
||||
|
||||
### **结束关键字**
|
||||
|
||||
由于LiteyukiBot基于异步运行, 所以在编写lyfunction时也要注意异步的调用,避免出现"单线程走到底"的情况是效率提升的关键.
|
||||
|
||||
`await` 异步任务结束关键字,用于结束当前已完成function的执行
|
||||
|
||||
> [!warning]
|
||||
> 但若出现非单function的情况,有一个task任务没有完成而await被执行了,那么当前所有函数包的task都会被截停销毁
|
||||
|
||||
|
||||
> [!tip]
|
||||
> 编写轻雪函数推荐你使用VS Code插件[Liteyuki Function](https://github.com/LiteyukiStudio/lyfunctionTextmate)实现语法高亮
|
80
docs/zh/dev/plugin.md
Normal file
80
docs/zh/dev/plugin.md
Normal file
@ -0,0 +1,80 @@
|
||||
---
|
||||
title: 轻雪插件开发
|
||||
order: 3
|
||||
---
|
||||
|
||||
# 简介
|
||||
|
||||
轻雪插件是轻雪内置的一部分功能,运行在主进程中,可以很高程度地扩展轻雪的功能
|
||||
|
||||
## 开始
|
||||
|
||||
### 创建插件
|
||||
|
||||
一个`.py`文件或一个包含`__init__.py`的文件夹即可被识别为插件
|
||||
|
||||
首先创建一个文件夹,例如`watchdog_plugin`,并在其中创建一个`__init__.py`文件,即可创建一个插件
|
||||
|
||||
`__init__.py`
|
||||
```python
|
||||
from liteyuki.plugin import PluginMetadata, PluginType
|
||||
from .watch_dog import * # 导入逻辑部分
|
||||
|
||||
# 定义插件元数据
|
||||
__plugin_meta__ = PluginMetadata(
|
||||
name="NoneDog", # 插件名称
|
||||
version="1.0.0", # 插件版本
|
||||
description="A simple plugin for nonebot developer", # 插件描述
|
||||
type=PluginType.SERVICE # 插件类型
|
||||
)
|
||||
|
||||
# 你的插件代码
|
||||
...
|
||||
```
|
||||
|
||||
### 编写逻辑部分
|
||||
|
||||
轻雪主进程不涉及聊天部分,因此插件主要是一些后台任务或者与聊天机器人的通信
|
||||
以下我们会编写一个简单的插件,用于开发NoneBot时进行文件系统变更重载
|
||||
`watch_dog.py`
|
||||
```python
|
||||
import os
|
||||
from liteyuki.dev import observer # 导入文件系统观察器
|
||||
from liteyuki import get_bot, logger # 导入轻雪Bot和日志
|
||||
from watchdog.events import FileSystemEvent # 导入文件系统事件
|
||||
|
||||
liteyuki = get_bot() # 获取唯一的轻雪Bot实例
|
||||
|
||||
exclude_extensions = (".pyc", ".pyo") # 排除的文件扩展名
|
||||
|
||||
|
||||
# 用observer的on_file_system_event装饰器监听文件系统事件
|
||||
@observer.on_file_system_event(
|
||||
directories=("src/nonebot_plugins",),
|
||||
event_filter=lambda event: not event.src_path.endswith(exclude_extensions) and ("__pycache__" not in event.src_path) and os.path.isfile(event.src_path)
|
||||
)
|
||||
def restart_nonebot_process(event: FileSystemEvent):
|
||||
logger.debug(f"File {event.src_path} changed, reloading nonebot...")
|
||||
liteyuki.restart_process("nonebot") # 调用重启进程方法
|
||||
```
|
||||
|
||||
### 加载插件
|
||||
|
||||
#### 方法1
|
||||
|
||||
- 在配置文件中的`liteyuki.plugins`中添加你的插件路径,例如`watchdog_plugin`,重启轻雪即可加载插件。
|
||||
|
||||
#### 方法2
|
||||
|
||||
- 使用开发工具快速运行插件,无需手动创建实例
|
||||
- 创建入口文件,例如`main.py`,并在其中写入以下代码
|
||||
|
||||
```python
|
||||
from liteyuki.dev.plugin import run_plugins
|
||||
|
||||
run_plugins("watchdog_plugin")
|
||||
```
|
||||
|
||||
然后运行`python main.py`即可启动插件
|
||||
|
||||
启用插件后,我们在src/nonebot_plugins下创建一个文件,例如`test.py`,并在其中写入一些代码,保存后轻雪会自动重载NoneBot进程
|
51
docs/zh/dev/resource.md
Normal file
51
docs/zh/dev/resource.md
Normal file
@ -0,0 +1,51 @@
|
||||
---
|
||||
title: 资源包开发
|
||||
order: 1
|
||||
---
|
||||
|
||||
# 简介
|
||||
|
||||
资源包,亦可根据用途称为主题包、字体包、语言包等,它允许你一定程度上自定义轻雪的外观,并且不用修改源代码
|
||||
|
||||
- [资源/主题商店](../store/resource)提供了一些资源包供你选择,你也可以自己制作资源包
|
||||
- 资源包的制作很简单,如果你接触过`Minecraft`的资源包,那么你能够很快就上手,仅需按照原有路径进行文件替换即可,讲起打包成一个新的资源包。
|
||||
- 部分内容制作需要一点点前端基础,例如`html`,`css`
|
||||
- 轻雪原版资源包请查看`LiteyukiBot/liteyuki/resources`,可以在此基础上进行修改
|
||||
- 欢迎各位投稿资源包到轻雪资源商店
|
||||
|
||||
请注意,主题包中的html渲染使用Js来规定数据的渲染位置,请确保您所编写的html代码能被Bot解析,否则会导致渲染失败或渲染结果不理想/异常/错位等无法预料的事情发生。推荐在编写html时同时更改对应Js代码,以避免出现无法预料的问题。
|
||||
|
||||
---
|
||||
|
||||
## 加载资源包
|
||||
|
||||
- 资源包通常是以`.zip`格式压缩的,只需要将其解压到根目录`resources`目录下即可,注意不要嵌套文件夹,正常的路径应该是这样的
|
||||
|
||||
```shell
|
||||
main.py
|
||||
resources
|
||||
└─resource_pack_1
|
||||
├─metadata.yml
|
||||
├─templates
|
||||
└───...
|
||||
└─resource_pack_2
|
||||
├─metadata.yml
|
||||
└─...
|
||||
```
|
||||
|
||||
- 你自己制作的资源包也应该遵循这个规则,并且应该在`metadata.yml`中填写一些信息
|
||||
- 若没有`metadata.yml`文件,则该文件夹不会被识别为资源包
|
||||
|
||||
```yaml
|
||||
name: "资源包名称"
|
||||
version: "1.0.0"
|
||||
description: "资源包描述"
|
||||
# 你可以自定义一些信息,但请保证以上三个字段
|
||||
...
|
||||
```
|
||||
|
||||
- 资源包加载遵循一个优先级,即后加载的资源包会覆盖前面的资源包,例如,你在A包中定义了一个`index.html`文件,B包也定义了一个`index.html`文件,那么加载B包后,A包中的`index.html`文件会被覆盖
|
||||
- 对于不同资源包的不同文件,是可以相对引用的,例如你在A中定义了`templates/index.html`,在B中定义了`templates/style.css`,可以在A的`index.html`中用`./style.css`相对路径引用B中的css
|
||||
|
||||
> [!tip]
|
||||
> 资源包的结构会随着轻雪的更新而有变动,第三方资源包开发者需要注意版本兼容性,同时用户也应该自行选择可用的资源包
|
63
docs/zh/index.md
Normal file
63
docs/zh/index.md
Normal file
@ -0,0 +1,63 @@
|
||||
---
|
||||
layout: home
|
||||
|
||||
hero:
|
||||
name: "轻雪机器人"
|
||||
tagline: "一个综合性的聊天机器人应用及管理框架"
|
||||
actions:
|
||||
- theme: brand
|
||||
text: 🚀 快速开始
|
||||
link: ./deploy/install
|
||||
- theme: alt
|
||||
text: 📖 使用手册
|
||||
link: ./usage/basic
|
||||
- theme: alt
|
||||
text: 📦 扩展
|
||||
link: ./store/resource
|
||||
- theme: alt
|
||||
text: 🛠️ 开发
|
||||
link: ./dev/guide
|
||||
image:
|
||||
light: ./liteyuki.svg
|
||||
dark: ./liteyuki-dark.svg
|
||||
alt: Liteyuki Logo
|
||||
|
||||
features:
|
||||
- title: 生态良好
|
||||
icon: 🛠️
|
||||
details: 基于轻雪框架,原生支持任意Python Bot框架和应用程序
|
||||
link: ./dev/api
|
||||
|
||||
- title: 插件管理
|
||||
icon: 🧩
|
||||
details: 使用包管理器,便捷管理插件及资源包
|
||||
link: ./store/plugin
|
||||
|
||||
- title: 自由定制
|
||||
icon: 🎨
|
||||
details: 使用资源包对外观进行完全自定义
|
||||
link: ./store/resource
|
||||
|
||||
- title: 国际化
|
||||
icon: 🌐
|
||||
details: 通过资源包支持多种语言
|
||||
link: https://baike.baidu.com/item/i18n/6771940
|
||||
|
||||
- title: 简易使用
|
||||
icon: ⚙️
|
||||
details: 无需繁琐前期过程,开箱即用
|
||||
link: ./deploy/config
|
||||
|
||||
- title: 超高性能
|
||||
icon: 🚀
|
||||
details: 500个插件,2秒内启动
|
||||
|
||||
- title: 滚动更新
|
||||
icon: ☁️
|
||||
details: 让你的机器人保持最新提交
|
||||
|
||||
- title: 开源项目
|
||||
icon: 📄
|
||||
details: 项目遵循LSO LICENCE开源,欢迎各位的贡献
|
||||
link: https://github.com/LiteyukiStudio/LiteyukiStudioOpensourceLICENSE
|
||||
---
|
6
docs/zh/store/plugin.md
Normal file
6
docs/zh/store/plugin.md
Normal file
@ -0,0 +1,6 @@
|
||||
---
|
||||
title: 插件商店
|
||||
order: 2
|
||||
---
|
||||
|
||||
<PluginStore />
|
6
docs/zh/store/resource.md
Normal file
6
docs/zh/store/resource.md
Normal file
@ -0,0 +1,6 @@
|
||||
---
|
||||
title: 资源商店
|
||||
order: 1
|
||||
---
|
||||
|
||||
<ResStore />
|
15
docs/zh/usage/agreement.md
Normal file
15
docs/zh/usage/agreement.md
Normal file
@ -0,0 +1,15 @@
|
||||
---
|
||||
title: 用户协议
|
||||
order: 3
|
||||
---
|
||||
# 用户协议
|
||||
|
||||
1. 本项目遵循`MIT`协议,你可以自由使用,修改,分发,但是请保留原作者信息
|
||||
2. 你可以选择开启`auto_report`(默认开启),轻雪会收集以下内容
|
||||
- 运行环境的设备信息:CPU,内存,系统信息及Python信息
|
||||
- 插件信息(不含插件数据)
|
||||
- 部分异常信息,
|
||||
- 会话负载信息(不含隐私部分)
|
||||
以上内容仅用于项目的优化,不包含任何隐私信息,且通过安全的方式传输到轻雪的服务器,若你不希望提供这些信息,可以在配置文件中把`auto_report`设定为`false`
|
||||
3. 本项目不会收集用户的任何隐私信息,但请注意甄别第三方插件的安全性
|
||||
4. 使用此项目代表你已经同意以上协议
|
122
docs/zh/usage/basic.md
Normal file
122
docs/zh/usage/basic.md
Normal file
@ -0,0 +1,122 @@
|
||||
---
|
||||
title: 基础插件
|
||||
order: 1
|
||||
---
|
||||
|
||||
# 基础插件
|
||||
---
|
||||
|
||||
> [!tip]
|
||||
> **参数**:`<param>`为必填参数,`[option]`为可选参数。
|
||||
>
|
||||
> **命令别名**:配置了命令别名的命令可以使用别名代替原命令,例如`npm install ~`可以使用`插件 安装 ~`代替。
|
||||
|
||||
## **轻雪命令`liteyuki_command`**
|
||||
|
||||
|
||||
| 命令 | 说明 | 权限 | 举例 | 可用参数 |
|
||||
| :----------------------------------------: | :---------------------------------------------------------------------------------------------: | :----------------------------------------: | :---------------------------------------------------------: | :----------------------------------------------------------------------------------: |
|
||||
| `reload-liteyuki` | 重载轻雪 | 超级用户 | ❌ | ❌ |
|
||||
| `update-liteyuki` | 更新轻雪 | 超级用户 | ❌ | ❌ |
|
||||
| `liteecho` | 查看当前bot 版本 | 超级用户 | ❌ | ❌ |
|
||||
| `status` | 查看统计信息和状态 | 超级用户 | ❌ | ❌ |
|
||||
| `config set <key> value` | 添加配置项,若存在则会覆盖,输入值会被执行以转换为正确的类型,"10"和10是不一样的 | 超级用户 | `config set name 'liteyuki-bot'` | `<key>`: 若存在则覆盖, 若不存在则创建于`config.yml` ; `value`: yml格式的所有合法内容 |
|
||||
| `config get [key] ` | 查询配置项,不带key返回配置项列表,推荐私聊使用 | 超级用户 | `config get name` | `<key>`: 若存在则返回, 若不存在则返回空 |
|
||||
| `switch-image-mode ` | 在普通图片和Markdown大图之间切换,该功能需要commit:505468b及以后的Lagrange.OneBot,默认普通图片 | 超级用户 | `switch-image-mode` | ❌ |
|
||||
| `/api api_name [args] ` | 调用机器人API | 超级用户 | `/api get_group_member_list group_id=1234567` | `<args>`: 参数列表, 格式为onebot v11协议api, 可用%20代替空格 |
|
||||
| `/function function_name [args] [kwargs] ` | 调用机器人函数(`.lyfunction`语法) | 超级用户 | `/function send_group_msg group_id=1234567 message='hello'` | `<args>`和`<kwargs>`: 参数列表, api格式为onebot v11协议api |
|
||||
| group enable/disable [group_id] | 在群聊启用/停用机器人,group_id仅超级用户可用 | 超级用户,群聊仅群主、管理员、超级用户可用 | `group enable 1145141919810` | `<group_id>`: 群号 |
|
||||
| liteyuki-docs | 查看轻雪文档 | 所有人 | ❌ | ❌ |
|
||||
|
||||
|
||||
---
|
||||
### **命令别名**
|
||||
|
||||
|
||||
| 命令 | 别名 |
|
||||
| :---------------: | :----------------------------------: |
|
||||
| status | 状态 |
|
||||
| reload-liteyuki | 重启轻雪 |
|
||||
| update-liteyuki | 更新轻雪 |
|
||||
| reload-resources | 重载资源 |
|
||||
| config | 配置, `set` 设置 / `get` 查询 |
|
||||
| switch-image-mode | 切换图片模式 |
|
||||
| liteyuki-docs | 轻雪文档 |
|
||||
| group | 群聊, `enable` 启用 / `disable` 停用 |
|
||||
|
||||
|
||||
---
|
||||
|
||||
## **插件/包管理器 `liteyuki_pacman`**
|
||||
|
||||
- 插件管理
|
||||
|
||||
| 命令 | 说明 | 权限 |
|
||||
| :-----------------------------------------------------: | :----------------------------------------: | :----------------------------------------------: |
|
||||
| `npm update` | 更新插件商店索引 | 超级用户 |
|
||||
| `npm install <plugin_name>` | 安装插件 | 超级用户 |
|
||||
| `npm uninstall <plugin_name>` | 卸载插件 | 超级用户 |
|
||||
| `npm search <keywords...>` | 通过关键词搜索插件 | 超级用户 |
|
||||
| `npm enable-global/disable-global <plugin_name>` | 全局启用/停用插件 | 超级用户 |
|
||||
| `npm enable/disable <plugin_name> [--group <group_id>]` | 当前会话启用/停用插件 | 群聊仅群主、管理员、超级用户可用,私聊所有人可用 |
|
||||
| `npm list [page] [num]` | 列出所有插件 page为页数,num为每页显示数量 | 群聊仅群主、管理员、超级用户可用,私聊所有人可用 |
|
||||
| `help <plugin_name>` | 查看插件帮助 | 所有人 |
|
||||
|
||||
|
||||
- 资源包管理
|
||||
|
||||
| 命令 | 说明 | 权限 |
|
||||
| :----------------------: | :------------------------------------------: | :------: |
|
||||
| `rpm list [page] [num]` | 列出所有资源包 page为页数,num为每页显示数量 | 超级用户 |
|
||||
| `rpm load <pack_name>` | 加载资源包 | 超级用户 |
|
||||
| `rpm unload <pack_name>` | 卸载资源包 | 超级用户 |
|
||||
| `rpm change <pack_name>` | 修改优先级 | 超级用户 |
|
||||
| `rpm reload` | 重载所有资源包 | 超级用户 |
|
||||
|
||||
|
||||
### 命令别名
|
||||
|
||||
| 命令 | 别名 |
|
||||
| :--------------: | :------: |
|
||||
| `npm` | 插件管理 |
|
||||
| `update` | 更新 |
|
||||
| `install` | 安装 |
|
||||
| `uninstall` | 卸载 |
|
||||
| `search` | 搜索 |
|
||||
| `enable` | 启用 |
|
||||
| `disable` | 停用 |
|
||||
| `enable-global` | 全局启用 |
|
||||
| `disable-global` | 全局停用 |
|
||||
| `rpm` | 资源包 |
|
||||
| `load` | 加载 |
|
||||
| `unload` | 卸载 |
|
||||
| `change` | 更改 |
|
||||
| `reload` | 重载 |
|
||||
| `list` | 列表 |
|
||||
| `help` | 帮助 |
|
||||
|
||||
> [!warning]
|
||||
> 受限于NoneBot2钩子函数的依赖注入参数,插件停用只能阻断传入响应,对于主动推送的插件不生效,请阅读插件主页的说明。
|
||||
>
|
||||
|
||||
---
|
||||
|
||||
|
||||
## **用户管理`liteyuki_user`**
|
||||
|
||||
| 命令 | 说明 | 权限 |
|
||||
| :-------------------------: | :----------------------------: | :----: |
|
||||
| `profile` | 查看用户信息菜单 | 所有人 |
|
||||
| `profile set <key> [value]` | 设置用户信息或打开属性设置菜单 | 所有人 |
|
||||
| `profile get <key>` | 获取用户信息 | 所有人 |
|
||||
|
||||
|
||||
###命令别名
|
||||
|
||||
| 命令 | 别名 |
|
||||
| :-------: | :------: |
|
||||
| `profile` | 个人信息 |
|
||||
| `set` | 设置 |
|
||||
| `get` | 查询 |
|
||||
|
||||
|
68
docs/zh/usage/extra.md
Normal file
68
docs/zh/usage/extra.md
Normal file
@ -0,0 +1,68 @@
|
||||
---
|
||||
title: 功能命令
|
||||
order: 2
|
||||
---
|
||||
|
||||
# 功能插件命令
|
||||
|
||||
### **轻雪天气`liteyuki_weather`**
|
||||
|
||||
查询实时天气,支持绑定城市,支持中英文城市名,支持多个关键词查询。
|
||||
|
||||
配置项
|
||||
|
||||
```yaml
|
||||
weather_key: "" # 和风天气的天气key,会自动判断key版本
|
||||
```
|
||||
|
||||
命令
|
||||
|
||||
```shell
|
||||
weather <keywords...> # Keywords为城市名,支持中英文
|
||||
```
|
||||
查询目标地实时天气,例如:"天气 北京 海淀", "weather Tokyo Shinjuku"
|
||||
|
||||
```shell
|
||||
bind-city <keywords...> # Keywords为城市名,支持中英文
|
||||
```
|
||||
|
||||
绑定查询城市,个人全局生效
|
||||
|
||||
#### 命令别名
|
||||
|
||||
| 命令 | 别名 |
|
||||
| :-------: | :------- |
|
||||
| weather | 天气 |
|
||||
| bind-city | 绑定城市 |
|
||||
|
||||
---
|
||||
|
||||
### **统计信息`liteyuki_statistics`**
|
||||
|
||||
统计信息
|
||||
命令
|
||||
|
||||
```shell
|
||||
statistic message --duration <duration> --period <period> --group [current|group_id] --bot [current|bot_id]
|
||||
```
|
||||
|
||||
功能: 用于统计Bot接收到的消息, 统计周期为`period`, 统计时间范围为`duration`
|
||||
|
||||
| 参数 | 格式 |
|
||||
| :------: | :------------------------------------------------------------: |
|
||||
| duration | 使用通用日期简写: `1d`(天), `1h`(小时), `45m`(分钟), `14s`(秒) |
|
||||
| period | 使用通用日期简写: `1d`(天), `1h`(小时), `45m`(分钟), `14s`(秒) |
|
||||
| group | `current` (当前群聊) 或 `group_id` (QQ群号) |
|
||||
| bot | `current` (当前Bot) 或 `bot_id` |
|
||||
|
||||
#### 命令别名
|
||||
|
||||
| 命令 | 别名 |
|
||||
| :----------: | :---: |
|
||||
| `statistic` | stat |
|
||||
| `message` | m |
|
||||
| `--duration` | -d |
|
||||
| --period` | -p |
|
||||
| `--group` | -g |
|
||||
| `--bot` | -b |
|
||||
| `current` | c |
|
31
liteyuki/EN.LICENSE
Normal file
31
liteyuki/EN.LICENSE
Normal file
@ -0,0 +1,31 @@
|
||||
LSO license
|
||||
LiteyukiStudio Opensource license
|
||||
|
||||
---
|
||||
|
||||
Copyright © 2024 Snowykami
|
||||
|
||||
---
|
||||
|
||||
Free to grant the same license-based rights to any person or organization who obtains a copy
|
||||
|
||||
including but not limited to using, copying, modifying, merging, publishing, distributing, sublicenseing, and/or selling copies of the software
|
||||
|
||||
This software and related documentation files (hereinafter referred to as "this software") are licensed in the same way as the base, and are released in the form of open source on the Internet or other media platforms
|
||||
|
||||
Everyone has the right to obtain a copy and obtain permission to distribute and/or use it in the above manner
|
||||
|
||||
However, when obtaining a copy, it is still necessary to pay attention to the following:
|
||||
|
||||
- The above copyright notice and this permission notice shall be included in a copy of the Software
|
||||
- When using this software and its copies, it is still necessary to maintain the same form as the original
|
||||
|
||||
- When using this software, you still need to disclose the copy of this software under the same license:
|
||||
- Do not profit from copies of this software in a non-original license without the permission of the original author
|
||||
|
||||
---
|
||||
|
||||
The software is provided as a "copy as is" without any warranty of any kind, either express or implied:
|
||||
including but not limited to the warranty of merchantability, non-infringement for specific purposes
|
||||
|
||||
In any case, the author or copyright owner shall not be liable for any claims, damages, or other liabilities arising from the use of the software by the author or copyright owner, whether in contract litigation, infringement litigation, or other litigation. The author and its copyright owner have the right to refuse compensation for any losses caused by the user for personal reasons
|
57
liteyuki/LICENSE
Normal file
57
liteyuki/LICENSE
Normal file
@ -0,0 +1,57 @@
|
||||
# 开源声明
|
||||
|
||||
MIT-LSO-AGC许可(cn)
|
||||
本声明推荐Markdown格式阅读
|
||||
|
||||
## 版权声明
|
||||
|
||||
版权所有(c) 2024 Snowykami
|
||||
|
||||
## 开源许可声明
|
||||
|
||||
现授予任何人以同仓库许可的开源,包含:
|
||||
本软件或本库和相关文档文件(以下简称“软件”)
|
||||
|
||||
在本软件或本库中不受限制,包括但不限于**非侵犯MIT-LSO-AGC许可**的:
|
||||
- 使用
|
||||
- 复制
|
||||
- 修改
|
||||
- 合并
|
||||
- 发布
|
||||
- 分发
|
||||
- 再许可和/或受到许可的销售本软件的副本
|
||||
|
||||
并要求或强烈建议使用本软件或库时:
|
||||
- 将上述版权声明和本许可声明包含在所有本软件或本库的副本或主要部分
|
||||
|
||||
---
|
||||
|
||||
本软件或本库按“原样”提供,不提供任何形式的、明确或隐含的,包括但不限于适销性保证,适合特定用途和非侵权性
|
||||
|
||||
## 责任声明
|
||||
|
||||
在任何情况下都不得在使用本软件或库时,在本协议许可的情况下,以损害或其他责任追责,无论是在合同诉讼、侵权行为或其他情况下,抑或是由以下原因引起:
|
||||
由于或与本软件有关,或是使用,或其他交易
|
||||
都与本软件,库作者无责任关系,非作者使用本软件或本库并造成任何损失的行为与作者无关
|
||||
|
||||
---
|
||||
|
||||
|
||||
## 搬运声明
|
||||
|
||||
在前提开源许可的基础上:
|
||||
|
||||
**未经允许不得**搬运至商业性平台**并且**进行**付费行为或疑似未得到许可的交易行为**
|
||||
|
||||
**不得**在搬运本仓库至**其他私有/公有开源镜像代码托管网站**时未经允许修改**任何**关于**原库的内容**
|
||||
|
||||
### 包括但不限于:
|
||||
|
||||
- 将`github`修改为`gitcode`
|
||||
- 篡改外链网址
|
||||
- 修改贡献者名单
|
||||
- 未经原作者允许私自占有仓库并声明为自己所有
|
||||
- 未经允许修改库原先所有的开源协议许可
|
||||
等
|
||||
|
||||
---
|
54
liteyuki/__init__.py
Normal file
54
liteyuki/__init__.py
Normal file
@ -0,0 +1,54 @@
|
||||
"""
|
||||
---
|
||||
title: liteyuki API
|
||||
order: 100
|
||||
---
|
||||
此模块为liteyuki的框架整体
|
||||
|
||||
This module is the overall framework of liteyuki.
|
||||
"""
|
||||
from liteyuki.bot import (
|
||||
LiteyukiBot,
|
||||
get_bot,
|
||||
get_config,
|
||||
get_config_with_compat
|
||||
)
|
||||
|
||||
from liteyuki.comm import (
|
||||
Channel,
|
||||
Event
|
||||
)
|
||||
|
||||
from liteyuki.plugin import (
|
||||
load_plugin,
|
||||
load_plugins
|
||||
)
|
||||
|
||||
from liteyuki.log import (
|
||||
init_log,
|
||||
logger
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
"LiteyukiBot",
|
||||
"get_bot",
|
||||
"get_config",
|
||||
"get_config_with_compat",
|
||||
"Channel",
|
||||
"Event",
|
||||
"load_plugin",
|
||||
"load_plugins",
|
||||
"init_log",
|
||||
"logger",
|
||||
]
|
||||
|
||||
__version__ = "6.3.10" # 测试版本号
|
||||
# 6.3.10
|
||||
# 新增`on_command`装饰器
|
||||
|
||||
# 6.3.9
|
||||
# 更改了on语法
|
||||
|
||||
# 6.3.8
|
||||
# 1. 初步添加对聊天的支持
|
||||
# 2. 优化了通道的性能
|
294
liteyuki/bot/__init__.py
Normal file
294
liteyuki/bot/__init__.py
Normal file
@ -0,0 +1,294 @@
|
||||
import asyncio
|
||||
import atexit
|
||||
import os
|
||||
import platform
|
||||
import signal
|
||||
import sys
|
||||
import threading
|
||||
import time
|
||||
from typing import Any, Optional
|
||||
|
||||
from liteyuki.bot.lifespan import LIFESPAN_FUNC, Lifespan, PROCESS_LIFESPAN_FUNC
|
||||
from liteyuki.comm.channel import get_channel
|
||||
from liteyuki.core.manager import ProcessManager
|
||||
from liteyuki.log import init_log, logger
|
||||
from liteyuki.plugin import load_plugin
|
||||
from liteyuki.utils import IS_MAIN_PROCESS
|
||||
|
||||
# new version
|
||||
from liteyuki.core.manager import sub_process_manager
|
||||
|
||||
__all__ = [
|
||||
"LiteyukiBot",
|
||||
"get_bot",
|
||||
"get_config",
|
||||
"get_config_with_compat",
|
||||
]
|
||||
|
||||
|
||||
class LiteyukiBot:
|
||||
def __init__(self, **kwargs) -> None:
|
||||
"""
|
||||
初始化轻雪实例
|
||||
Args:
|
||||
**kwargs: 配置
|
||||
"""
|
||||
"""常规操作"""
|
||||
print_logo()
|
||||
global _BOT_INSTANCE
|
||||
_BOT_INSTANCE = self # 引用
|
||||
|
||||
"""配置"""
|
||||
self.config: dict[str, Any] = kwargs
|
||||
|
||||
"""初始化"""
|
||||
self.init(**self.config) # 初始化
|
||||
logger.info("Liteyuki is initializing...")
|
||||
|
||||
"""生命周期管理"""
|
||||
self.lifespan = Lifespan()
|
||||
self.process_manager: ProcessManager = ProcessManager(lifespan=self.lifespan)
|
||||
|
||||
"""事件循环"""
|
||||
self.loop = asyncio.new_event_loop()
|
||||
asyncio.set_event_loop(self.loop)
|
||||
self.stop_event = threading.Event()
|
||||
self.call_restart_count = 0
|
||||
|
||||
"""加载插件加载器"""
|
||||
load_plugin("liteyuki.plugins.plugin_loader") # 加载轻雪插件
|
||||
|
||||
async def _run(self):
|
||||
"""
|
||||
启动逻辑
|
||||
"""
|
||||
await self.lifespan.before_start() # 启动前钩子
|
||||
sub_process_manager.start_all()
|
||||
await self.lifespan.after_start() # 启动后钩子
|
||||
await self.keep_alive()
|
||||
|
||||
def run(self):
|
||||
"""
|
||||
外部启动接口
|
||||
"""
|
||||
self.process_manager.start_all()
|
||||
try:
|
||||
asyncio.run(self._run())
|
||||
except KeyboardInterrupt:
|
||||
logger.opt(colors=True).info("<y>Liteyuki is stopping...</y>")
|
||||
self.stop()
|
||||
logger.opt(colors=True).info("<y>Liteyuki is stopped...</y>")
|
||||
|
||||
async def keep_alive(self):
|
||||
"""
|
||||
保持轻雪运行
|
||||
"""
|
||||
logger.info("Liteyuki is keeping alive...")
|
||||
try:
|
||||
while not self.stop_event.is_set():
|
||||
await asyncio.sleep(0.1)
|
||||
except Exception:
|
||||
logger.info("Liteyuki is exiting...")
|
||||
self.stop()
|
||||
|
||||
def restart(self, delay: int = 0):
|
||||
"""
|
||||
重启轻雪本体
|
||||
Args:
|
||||
delay ([`int`](https%3A//docs.python.org/3/library/functions.html#int), optional): 延迟重启时间. Defaults to 0.
|
||||
"""
|
||||
if self.call_restart_count < 1:
|
||||
executable = sys.executable
|
||||
args = sys.argv
|
||||
logger.info("Restarting LiteyukiBot...")
|
||||
time.sleep(delay)
|
||||
if platform.system() == "Windows":
|
||||
cmd = "start"
|
||||
elif platform.system() == "Linux":
|
||||
cmd = "nohup"
|
||||
elif platform.system() == "Darwin":
|
||||
cmd = "open"
|
||||
else:
|
||||
cmd = "nohup"
|
||||
self.process_manager.terminate_all()
|
||||
# 进程退出后重启
|
||||
threading.Thread(
|
||||
target=os.system,
|
||||
args=(f"{cmd} {executable} {' '.join(args)}",),
|
||||
daemon=True,
|
||||
).start()
|
||||
sys.exit(0)
|
||||
self.call_restart_count += 1
|
||||
|
||||
def restart_process(self, name: Optional[str] = None):
|
||||
"""
|
||||
停止轻雪
|
||||
Args:
|
||||
name ([`Optional`](https%3A//docs.python.org/3/library/typing.html#typing.Optional)[[`str`](https%3A//docs.python.org/3/library/stdtypes.html#str)]): 进程名. Defaults to None.
|
||||
Returns:
|
||||
"""
|
||||
if name is not None:
|
||||
chan_active = get_channel(f"{name}-active")
|
||||
chan_active.send(1)
|
||||
else:
|
||||
for process_name in self.process_manager.processes:
|
||||
chan_active = get_channel(f"{process_name}-active")
|
||||
chan_active.send(1)
|
||||
|
||||
def init(self, *args, **kwargs):
|
||||
"""
|
||||
初始化轻雪, 自动调用
|
||||
Args:
|
||||
*args: 参数
|
||||
**kwargs: 关键字参数
|
||||
"""
|
||||
self.init_logger()
|
||||
|
||||
def init_logger(self):
|
||||
"""
|
||||
初始化日志
|
||||
"""
|
||||
init_log(config=self.config)
|
||||
|
||||
def stop(self):
|
||||
"""
|
||||
停止轻雪
|
||||
"""
|
||||
self.process_manager.terminate_all()
|
||||
self.stop_event.set()
|
||||
|
||||
def on_before_start(self, func: LIFESPAN_FUNC) -> LIFESPAN_FUNC:
|
||||
"""
|
||||
注册启动前的函数
|
||||
Args:
|
||||
func ([`LIFESPAN_FUNC`](./lifespan#var-lifespan-func)): 生命周期函数
|
||||
Returns:
|
||||
[`LIFESPAN_FUNC`](./lifespan#var-lifespan-func): 生命周期函数
|
||||
"""
|
||||
return self.lifespan.on_before_start(func)
|
||||
|
||||
def on_after_start(self, func: LIFESPAN_FUNC):
|
||||
"""
|
||||
注册启动后的函数
|
||||
Args:
|
||||
func ([`LIFESPAN_FUNC`](./lifespan#var-lifespan-func)): 生命周期函数
|
||||
Returns:
|
||||
[`LIFESPAN_FUNC`](./lifespan#var-lifespan-func): 生命周期函数
|
||||
"""
|
||||
return self.lifespan.on_after_start(func)
|
||||
|
||||
def on_after_shutdown(self, func: LIFESPAN_FUNC):
|
||||
"""
|
||||
注册停止后的函数:未实现
|
||||
Args:
|
||||
func ([`LIFESPAN_FUNC`](./lifespan#var-lifespan-func)): 生命周期函数
|
||||
Returns:
|
||||
[`LIFESPAN_FUNC`](./lifespan#var-lifespan-func): 生命周期函数
|
||||
"""
|
||||
return self.lifespan.on_after_shutdown(func)
|
||||
|
||||
def on_before_process_shutdown(self, func: PROCESS_LIFESPAN_FUNC):
|
||||
"""
|
||||
注册进程停止前的函数,为子进程停止时调用
|
||||
Args:
|
||||
func ([`PROCESS_LIFESPAN_FUNC`](./lifespan#var-process-lifespan-func)): 生命周期函数
|
||||
Returns:
|
||||
[`PROCESS_LIFESPAN_FUNC`](./lifespan#var-process-lifespan-func): 生命周期函数
|
||||
"""
|
||||
return self.lifespan.on_before_process_shutdown(func)
|
||||
|
||||
def on_before_process_restart(
|
||||
self, func: PROCESS_LIFESPAN_FUNC
|
||||
) -> PROCESS_LIFESPAN_FUNC:
|
||||
"""
|
||||
注册进程重启前的函数,为子进程重启时调用
|
||||
Args:
|
||||
func ([`PROCESS_LIFESPAN_FUNC`](./lifespan#var-process-lifespan-func)): 生命周期函数
|
||||
Returns:
|
||||
[`PROCESS_LIFESPAN_FUNC`](./lifespan#var-process-lifespan-func): 生命周期函数
|
||||
"""
|
||||
|
||||
return self.lifespan.on_before_process_restart(func)
|
||||
|
||||
def on_after_restart(self, func: LIFESPAN_FUNC):
|
||||
"""
|
||||
注册重启后的函数:未实现
|
||||
Args:
|
||||
func ([`LIFESPAN_FUNC`](./lifespan#var-lifespan-func)): 生命周期函数
|
||||
Returns:
|
||||
[`LIFESPAN_FUNC`](./lifespan#var-lifespan-func): 生命周期函数
|
||||
"""
|
||||
return self.lifespan.on_after_restart(func)
|
||||
|
||||
|
||||
_BOT_INSTANCE: LiteyukiBot | None = None
|
||||
|
||||
|
||||
def get_bot() -> LiteyukiBot:
|
||||
"""
|
||||
获取轻雪实例
|
||||
Returns:
|
||||
[`LiteyukiBot`](#class-liteyukibot): 轻雪实例
|
||||
"""
|
||||
|
||||
if IS_MAIN_PROCESS:
|
||||
if _BOT_INSTANCE is None:
|
||||
raise RuntimeError("Liteyuki instance not initialized.")
|
||||
return _BOT_INSTANCE
|
||||
else:
|
||||
raise RuntimeError("Can't get bot instance in sub process.")
|
||||
|
||||
|
||||
def get_config(key: str, default: Any = None) -> Any:
|
||||
"""
|
||||
获取配置
|
||||
Args:
|
||||
key ([`str`](https%3A//docs.python.org/3/library/stdtypes.html#str)): 配置键
|
||||
default ([`Any`](https%3A//docs.python.org/3/library/functions.html#any), optional): 默认值. Defaults to None.
|
||||
Returns:
|
||||
[`Any`](https%3A//docs.python.org/3/library/functions.html#any): 配置值
|
||||
"""
|
||||
return get_bot().config.get(key, default)
|
||||
|
||||
|
||||
def get_config_with_compat(
|
||||
key: str, compat_keys: tuple[str], default: Any = None
|
||||
) -> Any:
|
||||
"""
|
||||
获取配置,兼容旧版本
|
||||
Args:
|
||||
key ([`str`](https%3A//docs.python.org/3/library/stdtypes.html#str)): 配置键
|
||||
compat_keys ([`tuple`](https%3A//docs.python.org/3/library/stdtypes.html#tuple)[`str`](https%3A//docs.python.org/3/library/stdtypes.html#str)): 兼容键
|
||||
default ([`Any`](https%3A//docs.python.org/3/library/functions.html#any), optional): 默认值. Defaults to None.
|
||||
|
||||
Returns:
|
||||
[`Any`](https%3A//docs.python.org/3/library/functions.html#any): 配置值
|
||||
"""
|
||||
if key in get_bot().config:
|
||||
return get_bot().config[key]
|
||||
for compat_key in compat_keys:
|
||||
if compat_key in get_bot().config:
|
||||
logger.warning(
|
||||
f'Config key "{compat_key}" will be deprecated, use "{key}" instead.'
|
||||
)
|
||||
return get_bot().config[compat_key]
|
||||
return default
|
||||
|
||||
|
||||
def print_logo():
|
||||
"""@litedoc-hide"""
|
||||
print(
|
||||
"\033[34m"
|
||||
+ r"""
|
||||
__ ______ ________ ________ __ __ __ __ __ __ ______
|
||||
/ | / |/ |/ |/ \ / |/ | / |/ | / |/ |
|
||||
$$ | $$$$$$/ $$$$$$$$/ $$$$$$$$/ $$ \ /$$/ $$ | $$ |$$ | /$$/ $$$$$$/
|
||||
$$ | $$ | $$ | $$ |__ $$ \/$$/ $$ | $$ |$$ |/$$/ $$ |
|
||||
$$ | $$ | $$ | $$ | $$ $$/ $$ | $$ |$$ $$< $$ |
|
||||
$$ | $$ | $$ | $$$$$/ $$$$/ $$ | $$ |$$$$$ \ $$ |
|
||||
$$ |_____ _$$ |_ $$ | $$ |_____ $$ | $$ \__$$ |$$ |$$ \ _$$ |_
|
||||
$$ |/ $$ | $$ | $$ | $$ | $$ $$/ $$ | $$ |/ $$ |
|
||||
$$$$$$$$/ $$$$$$/ $$/ $$$$$$$$/ $$/ $$$$$$/ $$/ $$/ $$$$$$/
|
||||
"""
|
||||
+ "\033[0m"
|
||||
)
|
159
liteyuki/bot/lifespan.py
Normal file
159
liteyuki/bot/lifespan.py
Normal file
@ -0,0 +1,159 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Copyright (C) 2020-2024 LiteyukiStudio. All Rights Reserved
|
||||
|
||||
@Time : 2024/7/23 下午8:24
|
||||
@Author : snowykami
|
||||
@Email : snowykami@outlook.com
|
||||
@File : lifespan.py
|
||||
@Software: PyCharm
|
||||
"""
|
||||
import asyncio
|
||||
from typing import Any, Awaitable, Callable, TypeAlias, Sequence
|
||||
|
||||
from liteyuki.log import logger
|
||||
from liteyuki.utils import is_coroutine_callable, async_wrapper
|
||||
|
||||
SYNC_LIFESPAN_FUNC: TypeAlias = Callable[[], Any] # 同步生命周期函数
|
||||
ASYNC_LIFESPAN_FUNC: TypeAlias = Callable[[], Awaitable[Any]] # 异步生命周期函数
|
||||
LIFESPAN_FUNC: TypeAlias = SYNC_LIFESPAN_FUNC | ASYNC_LIFESPAN_FUNC # 生命周期函数
|
||||
|
||||
SYNC_PROCESS_LIFESPAN_FUNC: TypeAlias = Callable[[str], Any] # 同步进程生命周期函数
|
||||
ASYNC_PROCESS_LIFESPAN_FUNC: TypeAlias = Callable[[str], Awaitable[Any]] # 异步进程生命周期函数
|
||||
PROCESS_LIFESPAN_FUNC: TypeAlias = SYNC_PROCESS_LIFESPAN_FUNC | ASYNC_PROCESS_LIFESPAN_FUNC # 进程函数
|
||||
|
||||
|
||||
class Lifespan:
|
||||
def __init__(self) -> None:
|
||||
"""
|
||||
轻雪生命周期管理,启动、停止、重启
|
||||
"""
|
||||
self.life_flag: int = 0
|
||||
|
||||
self._before_start_funcs: list[LIFESPAN_FUNC] = []
|
||||
self._after_start_funcs: list[LIFESPAN_FUNC] = []
|
||||
|
||||
self._before_process_shutdown_funcs: list[PROCESS_LIFESPAN_FUNC] = []
|
||||
self._after_shutdown_funcs: list[LIFESPAN_FUNC] = []
|
||||
|
||||
self._before_process_restart_funcs: list[PROCESS_LIFESPAN_FUNC] = []
|
||||
self._after_restart_funcs: list[LIFESPAN_FUNC] = []
|
||||
|
||||
@staticmethod
|
||||
async def run_funcs(funcs: Sequence[LIFESPAN_FUNC | PROCESS_LIFESPAN_FUNC], *args, **kwargs) -> None:
|
||||
"""
|
||||
并发运行异步函数
|
||||
Args:
|
||||
funcs ([`Sequence`](https%3A//docs.python.org/3/library/typing.html#typing.Sequence)[[`ASYNC_LIFESPAN_FUNC`](#var-lifespan-func) | [`PROCESS_LIFESPAN_FUNC`](#var-process-lifespan-func)]): 函数列表
|
||||
Returns:
|
||||
"""
|
||||
tasks = [func(*args, **kwargs) if is_coroutine_callable(func) else async_wrapper(func)(*args, **kwargs) for func in funcs]
|
||||
await asyncio.gather(*tasks)
|
||||
|
||||
def on_before_start(self, func: LIFESPAN_FUNC) -> LIFESPAN_FUNC:
|
||||
"""
|
||||
注册启动时的函数
|
||||
Args:
|
||||
func ([`LIFESPAN_FUNC`](#var-lifespan-func)): 生命周期函数
|
||||
Returns:
|
||||
[`LIFESPAN_FUNC`](#var-lifespan-func): 生命周期函数
|
||||
"""
|
||||
self._before_start_funcs.append(func)
|
||||
return func
|
||||
|
||||
def on_after_start(self, func: LIFESPAN_FUNC) -> LIFESPAN_FUNC:
|
||||
"""
|
||||
注册启动时的函数
|
||||
Args:
|
||||
func ([`LIFESPAN_FUNC`](#var-lifespan-func)): 生命周期函数
|
||||
Returns:
|
||||
[`LIFESPAN_FUNC`](#var-lifespan-func): 生命周期函数
|
||||
"""
|
||||
self._after_start_funcs.append(func)
|
||||
return func
|
||||
|
||||
def on_before_process_shutdown(self, func: PROCESS_LIFESPAN_FUNC) -> PROCESS_LIFESPAN_FUNC:
|
||||
"""
|
||||
注册进程停止前的函数
|
||||
Args:
|
||||
func ([`PROCESS_LIFESPAN_FUNC`](#var-process-lifespan-func)): 进程生命周期函数
|
||||
Returns:
|
||||
[`PROCESS_LIFESPAN_FUNC`](#var-process-lifespan-func): 进程生命周期函数
|
||||
"""
|
||||
self._before_process_shutdown_funcs.append(func)
|
||||
return func
|
||||
|
||||
def on_after_shutdown(self, func: LIFESPAN_FUNC) -> LIFESPAN_FUNC:
|
||||
"""
|
||||
注册停止后的函数
|
||||
Args:
|
||||
func ([`LIFESPAN_FUNC`](#var-lifespan-func)): 生命周期函数
|
||||
Returns:
|
||||
[`LIFESPAN_FUNC`](#var-lifespan-func): 生命周期函数
|
||||
"""
|
||||
self._after_shutdown_funcs.append(func)
|
||||
return func
|
||||
|
||||
def on_before_process_restart(self, func: PROCESS_LIFESPAN_FUNC) -> PROCESS_LIFESPAN_FUNC:
|
||||
"""
|
||||
注册进程重启前的函数
|
||||
Args:
|
||||
func ([`PROCESS_LIFESPAN_FUNC`](#var-process-lifespan-func)): 进程生命周期函数
|
||||
Returns:
|
||||
[`PROCESS_LIFESPAN_FUNC`](#var-process-lifespan-func): 进程生命周期函数
|
||||
"""
|
||||
self._before_process_restart_funcs.append(func)
|
||||
return func
|
||||
|
||||
def on_after_restart(self, func: LIFESPAN_FUNC) -> LIFESPAN_FUNC:
|
||||
"""
|
||||
注册重启后的函数
|
||||
Args:
|
||||
func ([`LIFESPAN_FUNC`](#var-lifespan-func)): 生命周期函数
|
||||
Returns:
|
||||
[`LIFESPAN_FUNC`](#var-lifespan-func): 生命周期函数
|
||||
"""
|
||||
self._after_restart_funcs.append(func)
|
||||
return func
|
||||
|
||||
async def before_start(self) -> None:
|
||||
"""
|
||||
启动前钩子
|
||||
"""
|
||||
logger.debug("Running before_start functions")
|
||||
await self.run_funcs(self._before_start_funcs)
|
||||
|
||||
async def after_start(self) -> None:
|
||||
"""
|
||||
启动后钩子
|
||||
"""
|
||||
logger.debug("Running after_start functions")
|
||||
await self.run_funcs(self._after_start_funcs)
|
||||
|
||||
async def before_process_shutdown(self, *args, **kwargs) -> None:
|
||||
"""
|
||||
停止前钩子
|
||||
"""
|
||||
logger.debug("Running before_shutdown functions")
|
||||
await self.run_funcs(self._before_process_shutdown_funcs, *args, **kwargs)
|
||||
|
||||
async def after_shutdown(self) -> None:
|
||||
"""
|
||||
停止后钩子 未实现
|
||||
"""
|
||||
logger.debug("Running after_shutdown functions")
|
||||
await self.run_funcs(self._after_shutdown_funcs)
|
||||
|
||||
async def before_process_restart(self, *args, **kwargs) -> None:
|
||||
"""
|
||||
重启前钩子
|
||||
"""
|
||||
logger.debug("Running before_restart functions")
|
||||
await self.run_funcs(self._before_process_restart_funcs, *args, **kwargs)
|
||||
|
||||
async def after_restart(self) -> None:
|
||||
"""
|
||||
重启后钩子 未实现
|
||||
"""
|
||||
logger.debug("Running after_restart functions")
|
||||
await self.run_funcs(self._after_restart_funcs)
|
38
liteyuki/comm/__init__.py
Normal file
38
liteyuki/comm/__init__.py
Normal file
@ -0,0 +1,38 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
该模块用于轻雪主进程和Nonebot子进程之间的通信
|
||||
依赖关系
|
||||
event -> _
|
||||
storage -> channel_
|
||||
rpc -> channel_, storage
|
||||
"""
|
||||
from liteyuki.comm.channel import (
|
||||
Channel,
|
||||
get_channel,
|
||||
set_channel,
|
||||
set_channels,
|
||||
get_channels,
|
||||
active_channel,
|
||||
passive_channel
|
||||
)
|
||||
from liteyuki.comm.event import Event
|
||||
|
||||
__all__ = [
|
||||
"Channel",
|
||||
"Event",
|
||||
"get_channel",
|
||||
"set_channel",
|
||||
"set_channels",
|
||||
"get_channels",
|
||||
"active_channel",
|
||||
"passive_channel"
|
||||
]
|
||||
|
||||
from liteyuki.utils import IS_MAIN_PROCESS
|
||||
|
||||
# 第一次引用必定为赋值
|
||||
_ref_count = 0
|
||||
if not IS_MAIN_PROCESS:
|
||||
if (active_channel is None or passive_channel is None) and _ref_count > 0:
|
||||
raise RuntimeError("Error: Channel not initialized in sub process")
|
||||
_ref_count += 1
|
332
liteyuki/comm/channel.py
Normal file
332
liteyuki/comm/channel.py
Normal file
@ -0,0 +1,332 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
本模块定义了一个通用的通道类,用于进程间通信
|
||||
"""
|
||||
import asyncio
|
||||
from multiprocessing import Pipe
|
||||
from typing import (
|
||||
Any,
|
||||
Callable,
|
||||
Coroutine,
|
||||
Generic,
|
||||
Optional,
|
||||
TypeAlias,
|
||||
TypeVar,
|
||||
get_args,
|
||||
)
|
||||
|
||||
from liteyuki.log import logger
|
||||
from liteyuki.utils import IS_MAIN_PROCESS, is_coroutine_callable
|
||||
|
||||
T = TypeVar("T")
|
||||
|
||||
SYNC_ON_RECEIVE_FUNC: TypeAlias = Callable[[T], Any] # 同步接收函数
|
||||
ASYNC_ON_RECEIVE_FUNC: TypeAlias = Callable[
|
||||
[T], Coroutine[Any, Any, Any]
|
||||
] # 异步接收函数
|
||||
ON_RECEIVE_FUNC: TypeAlias = SYNC_ON_RECEIVE_FUNC | ASYNC_ON_RECEIVE_FUNC # 接收函数
|
||||
|
||||
SYNC_FILTER_FUNC: TypeAlias = Callable[[T], bool] # 同步过滤函数
|
||||
ASYNC_FILTER_FUNC: TypeAlias = Callable[[T], Coroutine[Any, Any, bool]] # 异步过滤函数
|
||||
FILTER_FUNC: TypeAlias = SYNC_FILTER_FUNC | ASYNC_FILTER_FUNC # 过滤函数
|
||||
|
||||
_func_id: int = 0
|
||||
_channel: dict[str, "Channel"] = {}
|
||||
_callback_funcs: dict[int, ON_RECEIVE_FUNC] = {}
|
||||
|
||||
|
||||
class Channel(Generic[T]):
|
||||
"""
|
||||
通道类,可以在进程间和进程内通信,双向但同时只能有一个发送者和一个接收者
|
||||
有两种接收工作方式,但是只能选择一种,主动接收和被动接收,主动接收使用 `receive` 方法,被动接收使用 `on_receive` 装饰器
|
||||
"""
|
||||
|
||||
def __init__(self, name: str, type_check: Optional[bool] = None):
|
||||
"""
|
||||
初始化通道
|
||||
Args:
|
||||
name: 通道ID
|
||||
type_check: 是否开启类型检查, 若为空,则传入泛型默认开启,否则默认关闭
|
||||
"""
|
||||
|
||||
self.conn_send, self.conn_recv = Pipe()
|
||||
self._conn_send_inner, self._conn_recv_inner = (
|
||||
Pipe()
|
||||
) # 内部通道,用于子进程通信
|
||||
self._closed = False
|
||||
self._on_main_receive_func_ids: list[int] = []
|
||||
self._on_sub_receive_func_ids: list[int] = []
|
||||
self.name: str = name
|
||||
|
||||
self.is_receive_loop_running = False
|
||||
|
||||
if type_check is None:
|
||||
# 若传入泛型则默认开启类型检查
|
||||
type_check = self._get_generic_type() is not None
|
||||
|
||||
elif type_check:
|
||||
if self._get_generic_type() is None:
|
||||
raise TypeError("Type hint is required for enforcing type check.")
|
||||
self.type_check = type_check
|
||||
if name in _channel:
|
||||
raise ValueError(f"Channel {name} already exists")
|
||||
|
||||
if IS_MAIN_PROCESS:
|
||||
if name in _channel:
|
||||
raise ValueError(f"Channel {name} already exists")
|
||||
_channel[name] = self
|
||||
logger.debug(f"Channel {name} initialized in main process")
|
||||
else:
|
||||
logger.debug(
|
||||
f"Channel {name} initialized in sub process, should manually set in main process"
|
||||
)
|
||||
|
||||
def _get_generic_type(self) -> Optional[type]:
|
||||
"""
|
||||
获取通道传递泛型类型
|
||||
Returns:
|
||||
Optional[type]: 泛型类型
|
||||
"""
|
||||
if hasattr(self, "__orig_class__"):
|
||||
return get_args(self.__orig_class__)[0]
|
||||
return None
|
||||
|
||||
def _validate_structure(self, data: Any, structure: type) -> bool:
|
||||
"""
|
||||
验证数据结构
|
||||
Args:
|
||||
data: 数据
|
||||
structure: 结构
|
||||
Returns:
|
||||
bool: 是否通过验证
|
||||
"""
|
||||
if isinstance(structure, type):
|
||||
return isinstance(data, structure)
|
||||
elif isinstance(structure, tuple):
|
||||
if not isinstance(data, tuple) or len(data) != len(structure):
|
||||
return False
|
||||
return all(self._validate_structure(d, s) for d, s in zip(data, structure))
|
||||
elif isinstance(structure, list):
|
||||
if not isinstance(data, list):
|
||||
return False
|
||||
return all(self._validate_structure(d, structure[0]) for d in data)
|
||||
elif isinstance(structure, dict):
|
||||
if not isinstance(data, dict):
|
||||
return False
|
||||
return all(
|
||||
k in data and self._validate_structure(data[k], structure[k])
|
||||
for k in structure
|
||||
)
|
||||
return False
|
||||
|
||||
def __str__(self):
|
||||
return f"Channel({self.name})"
|
||||
|
||||
def send(self, data: T):
|
||||
"""
|
||||
发送数据,发送函数为同步函数,没有异步的必要
|
||||
Args:
|
||||
data (T): 数据
|
||||
"""
|
||||
if self.type_check:
|
||||
_type = self._get_generic_type()
|
||||
if _type is not None and not self._validate_structure(data, _type):
|
||||
raise TypeError(
|
||||
f"Data must be an instance of {_type}, {type(data)} found"
|
||||
)
|
||||
|
||||
if self._closed:
|
||||
raise RuntimeError("Cannot send to a closed channel")
|
||||
self.conn_send.send(data)
|
||||
|
||||
def receive(self) -> T:
|
||||
"""
|
||||
同步接收数据,会阻塞线程
|
||||
Returns:
|
||||
T: 数据
|
||||
"""
|
||||
if self._closed:
|
||||
raise RuntimeError("Cannot receive from a closed channel")
|
||||
|
||||
while True:
|
||||
data = self.conn_recv.recv()
|
||||
return data
|
||||
|
||||
async def async_receive(self) -> T:
|
||||
"""
|
||||
异步接收数据,会挂起等待
|
||||
Returns:
|
||||
T: 数据
|
||||
"""
|
||||
loop = asyncio.get_running_loop()
|
||||
data = await loop.run_in_executor(None, self.receive)
|
||||
return data
|
||||
|
||||
def on_receive(
|
||||
self, filter_func: Optional[FILTER_FUNC] = None
|
||||
) -> Callable[[Callable[[T], Any]], Callable[[T], Any]]:
|
||||
"""
|
||||
接收数据并执行函数
|
||||
Args:
|
||||
filter_func ([`Optional`](https%3A//docs.python.org/3/library/typing.html#typing.Optional)[[`FILTER_FUNC`](#var-FILTER_FUNC)], optional): 过滤函数. Defaults to None.
|
||||
Returns:
|
||||
Callable[[Callable[[T], Any]], Callable[[T], Any]]: 装饰器
|
||||
"""
|
||||
if not IS_MAIN_PROCESS:
|
||||
raise RuntimeError("on_receive can only be used in main process")
|
||||
|
||||
def decorator(func: Callable[[T], Any]) -> Callable[[T], Any]:
|
||||
global _func_id
|
||||
|
||||
async def wrapper(data: T) -> Any:
|
||||
if filter_func is not None:
|
||||
if is_coroutine_callable(filter_func):
|
||||
if not (await filter_func(data)): # type: ignore
|
||||
return
|
||||
else:
|
||||
if not filter_func(data):
|
||||
return
|
||||
|
||||
if is_coroutine_callable(func):
|
||||
return await func(data)
|
||||
else:
|
||||
return func(data)
|
||||
|
||||
_callback_funcs[_func_id] = wrapper
|
||||
if IS_MAIN_PROCESS:
|
||||
self._on_main_receive_func_ids.append(_func_id)
|
||||
else:
|
||||
self._on_sub_receive_func_ids.append(_func_id)
|
||||
_func_id += 1
|
||||
return func
|
||||
|
||||
return decorator
|
||||
|
||||
async def _run_on_receive_funcs(self, data: Any):
|
||||
"""
|
||||
运行接收函数
|
||||
Args:
|
||||
data: 数据
|
||||
"""
|
||||
if IS_MAIN_PROCESS:
|
||||
[
|
||||
asyncio.create_task(_callback_funcs[func_id](data))
|
||||
for func_id in self._on_main_receive_func_ids
|
||||
]
|
||||
else:
|
||||
[
|
||||
asyncio.create_task(_callback_funcs[func_id](data))
|
||||
for func_id in self._on_sub_receive_func_ids
|
||||
]
|
||||
|
||||
|
||||
"""子进程可用的主动和被动通道"""
|
||||
active_channel: Channel = Channel(name="active_channel") # 主动通道
|
||||
passive_channel: Channel = Channel(name="passive_channel") # 被动通道
|
||||
publish_channel: Channel[tuple[str, dict[str, Any]]] = Channel(
|
||||
name="publish_channel"
|
||||
) # 发布通道
|
||||
"""通道传递通道,主进程创建单例,子进程初始化时实例化"""
|
||||
channel_deliver_active_channel: Channel[Channel[Any]] # 主动通道传递通道
|
||||
channel_deliver_passive_channel: Channel[tuple[str, dict[str, Any]]] # 被动通道传递通道
|
||||
|
||||
if IS_MAIN_PROCESS:
|
||||
channel_deliver_active_channel = Channel(
|
||||
name="channel_deliver_active_channel"
|
||||
) # 主动通道传递通道
|
||||
channel_deliver_passive_channel = Channel(
|
||||
name="channel_deliver_passive_channel"
|
||||
) # 被动通道传递通道
|
||||
|
||||
@channel_deliver_passive_channel.on_receive(
|
||||
filter_func=lambda data: data[0] == "set_channel"
|
||||
)
|
||||
def on_set_channel(data: tuple[str, dict[str, Any]]):
|
||||
name, channel = data[1]["name"], data[1]["channel_"]
|
||||
set_channel(name, channel)
|
||||
|
||||
@channel_deliver_passive_channel.on_receive(
|
||||
filter_func=lambda data: data[0] == "get_channel"
|
||||
)
|
||||
def on_get_channel(data: tuple[str, dict[str, Any]]):
|
||||
name, recv_chan = data[1]["name"], data[1]["recv_chan"]
|
||||
recv_chan.send(get_channel(name))
|
||||
|
||||
@channel_deliver_passive_channel.on_receive(
|
||||
filter_func=lambda data: data[0] == "get_channels"
|
||||
)
|
||||
def on_get_channels(data: tuple[str, dict[str, Any]]):
|
||||
recv_chan = data[1]["recv_chan"]
|
||||
recv_chan.send(get_channels())
|
||||
|
||||
|
||||
def set_channel(name: str, channel: "Channel"):
|
||||
"""
|
||||
设置通道实例
|
||||
Args:
|
||||
name ([`str`](https%3A//docs.python.org/3/library/stdtypes.html#str)): 通道名称
|
||||
channel ([`Channel`](#class-channel-generic-t)): 通道实例
|
||||
"""
|
||||
if not isinstance(channel, Channel):
|
||||
raise TypeError(
|
||||
f"channel_ must be an instance of Channel, {type(channel)} found"
|
||||
)
|
||||
|
||||
if IS_MAIN_PROCESS:
|
||||
if name in _channel:
|
||||
raise ValueError(f"Channel {name} already exists")
|
||||
_channel[name] = channel
|
||||
else:
|
||||
# 请求主进程设置通道
|
||||
channel_deliver_passive_channel.send(
|
||||
(
|
||||
"set_channel",
|
||||
{
|
||||
"name": name,
|
||||
"channel_": channel,
|
||||
},
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def set_channels(channels: dict[str, "Channel"]):
|
||||
"""
|
||||
设置通道实例
|
||||
Args:
|
||||
channels ([`dict`](https%3A//docs.python.org/3/library/stdtypes.html#dict)[[`str`](https%3A//docs.python.org/3/library/stdtypes.html#str), [`Channel`](#class-channel-generic-t)]): 通道实例
|
||||
"""
|
||||
for name, channel in channels.items():
|
||||
set_channel(name, channel)
|
||||
|
||||
|
||||
def get_channel(name: str) -> "Channel":
|
||||
"""
|
||||
获取通道实例
|
||||
Args:
|
||||
name ([`str`](https%3A//docs.python.org/3/library/stdtypes.html#str)): 通道名称
|
||||
Returns:
|
||||
[`Channel`](#class-channel-generic-t): 通道实例
|
||||
"""
|
||||
if IS_MAIN_PROCESS:
|
||||
return _channel[name]
|
||||
|
||||
else:
|
||||
recv_chan = Channel[Channel[Any]]("recv_chan")
|
||||
channel_deliver_passive_channel.send(
|
||||
("get_channel", {"name": name, "recv_chan": recv_chan})
|
||||
)
|
||||
return recv_chan.receive()
|
||||
|
||||
|
||||
def get_channels() -> dict[str, "Channel"]:
|
||||
"""
|
||||
获取通道实例们
|
||||
Returns:
|
||||
[`dict`](https%3A//docs.python.org/3/library/stdtypes.html#dict)[[`str`](https%3A//docs.python.org/3/library/stdtypes.html#str), [`Channel`](#class-channel-generic-t)]: 通道实例
|
||||
"""
|
||||
if IS_MAIN_PROCESS:
|
||||
return _channel
|
||||
else:
|
||||
recv_chan = Channel[dict[str, Channel[Any]]]("recv_chan")
|
||||
channel_deliver_passive_channel.send(("get_channels", {"recv_chan": recv_chan}))
|
||||
return recv_chan.receive()
|
15
liteyuki/comm/event.py
Normal file
15
liteyuki/comm/event.py
Normal file
@ -0,0 +1,15 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
本模块用于轻雪主进程和子进程之间的通信的事件类
|
||||
"""
|
||||
from typing import Any
|
||||
|
||||
|
||||
class Event:
|
||||
"""
|
||||
事件类
|
||||
"""
|
||||
|
||||
def __init__(self, name: str, data: dict[str, Any]):
|
||||
self.name = name
|
||||
self.data = data
|
48
liteyuki/comm/socks_channel.py
Normal file
48
liteyuki/comm/socks_channel.py
Normal file
@ -0,0 +1,48 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
基于socket的通道
|
||||
"""
|
||||
|
||||
|
||||
class SocksChannel:
|
||||
"""
|
||||
通道类,可以在进程间和进程内通信,双向但同时只能有一个发送者和一个接收者
|
||||
有两种接收工作方式,但是只能选择一种,主动接收和被动接收,主动接收使用 `receive` 方法,被动接收使用 `on_receive` 装饰器
|
||||
"""
|
||||
|
||||
def __init__(self, name: str):
|
||||
"""
|
||||
初始化通道
|
||||
Args:
|
||||
name: 通道ID
|
||||
"""
|
||||
|
||||
self._name = name
|
||||
self._conn_send = None
|
||||
self._conn_recv = None
|
||||
self._closed = False
|
||||
|
||||
def send(self, data):
|
||||
"""
|
||||
发送数据
|
||||
Args:
|
||||
data: 数据
|
||||
"""
|
||||
|
||||
pass
|
||||
|
||||
def receive(self):
|
||||
"""
|
||||
接收数据
|
||||
Returns:
|
||||
data: 数据
|
||||
"""
|
||||
|
||||
pass
|
||||
|
||||
def close(self):
|
||||
"""
|
||||
关闭通道
|
||||
"""
|
||||
|
||||
pass
|
271
liteyuki/comm/storage.py
Normal file
271
liteyuki/comm/storage.py
Normal file
@ -0,0 +1,271 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
共享内存模块。类似于redis,但是更加轻量级并且线程安全
|
||||
"""
|
||||
import asyncio
|
||||
import threading
|
||||
from typing import Any, Callable, Optional
|
||||
|
||||
from liteyuki.comm import channel
|
||||
from liteyuki.comm.channel import ASYNC_ON_RECEIVE_FUNC, Channel, ON_RECEIVE_FUNC
|
||||
from liteyuki.utils import IS_MAIN_PROCESS, is_coroutine_callable, run_coroutine_in_thread
|
||||
|
||||
if IS_MAIN_PROCESS:
|
||||
_locks = {}
|
||||
|
||||
_on_main_subscriber_receive_funcs: dict[str, list[ASYNC_ON_RECEIVE_FUNC]] = {} # type: ignore
|
||||
"""主进程订阅者接收函数"""
|
||||
_on_sub_subscriber_receive_funcs: dict[str, list[ASYNC_ON_RECEIVE_FUNC]] = {} # type: ignore
|
||||
"""子进程订阅者接收函数"""
|
||||
|
||||
|
||||
def _get_lock(key) -> threading.Lock:
|
||||
"""
|
||||
获取锁
|
||||
"""
|
||||
if IS_MAIN_PROCESS:
|
||||
if key not in _locks:
|
||||
_locks[key] = threading.Lock()
|
||||
return _locks[key]
|
||||
else:
|
||||
raise RuntimeError("Cannot get lock in sub process.")
|
||||
|
||||
|
||||
class KeyValueStore:
|
||||
def __init__(self):
|
||||
self._store = {}
|
||||
self.active_chan = Channel[tuple[str, Optional[dict[str, Any]]]](name="shared_memory-active")
|
||||
self.passive_chan = Channel[tuple[str, Optional[dict[str, Any]]]](name="shared_memory-passive")
|
||||
|
||||
self.publish_channel = Channel[tuple[str, Any]](name="shared_memory-publish")
|
||||
|
||||
self.is_main_receive_loop_running = False
|
||||
self.is_sub_receive_loop_running = False
|
||||
|
||||
def set(self, key: str, value: Any) -> None:
|
||||
"""
|
||||
设置键值对
|
||||
Args:
|
||||
key: 键
|
||||
value: 值
|
||||
|
||||
"""
|
||||
if IS_MAIN_PROCESS:
|
||||
lock = _get_lock(key)
|
||||
with lock:
|
||||
self._store[key] = value
|
||||
else:
|
||||
# 向主进程发送请求拿取
|
||||
self.passive_chan.send(
|
||||
(
|
||||
"set",
|
||||
{
|
||||
"key" : key,
|
||||
"value": value
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
def get(self, key: str, default: Optional[Any] = None) -> Optional[Any]:
|
||||
"""
|
||||
获取键值对
|
||||
Args:
|
||||
key: 键
|
||||
default: 默认值
|
||||
|
||||
Returns:
|
||||
Any: 值
|
||||
"""
|
||||
if IS_MAIN_PROCESS:
|
||||
lock = _get_lock(key)
|
||||
with lock:
|
||||
return self._store.get(key, default)
|
||||
else:
|
||||
recv_chan = Channel[Optional[Any]]("recv_chan")
|
||||
self.passive_chan.send(
|
||||
(
|
||||
"get",
|
||||
{
|
||||
"key" : key,
|
||||
"default" : default,
|
||||
"recv_chan": recv_chan
|
||||
}
|
||||
|
||||
)
|
||||
)
|
||||
return recv_chan.receive()
|
||||
|
||||
def delete(self, key: str, ignore_key_error: bool = True) -> None:
|
||||
"""
|
||||
删除键值对
|
||||
Args:
|
||||
key: 键
|
||||
ignore_key_error: 是否忽略键不存在的错误
|
||||
|
||||
Returns:
|
||||
"""
|
||||
if IS_MAIN_PROCESS:
|
||||
lock = _get_lock(key)
|
||||
with lock:
|
||||
if key in self._store:
|
||||
try:
|
||||
del self._store[key]
|
||||
del _locks[key]
|
||||
except KeyError as e:
|
||||
if not ignore_key_error:
|
||||
raise e
|
||||
else:
|
||||
# 向主进程发送请求删除
|
||||
self.passive_chan.send(
|
||||
(
|
||||
"delete",
|
||||
{
|
||||
"key": key
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
def get_all(self) -> dict[str, Any]:
|
||||
"""
|
||||
获取所有键值对
|
||||
Returns:
|
||||
dict[str, Any]: 键值对
|
||||
"""
|
||||
if IS_MAIN_PROCESS:
|
||||
return self._store
|
||||
else:
|
||||
recv_chan = Channel[dict[str, Any]]("recv_chan")
|
||||
self.passive_chan.send(
|
||||
(
|
||||
"get_all",
|
||||
{
|
||||
"recv_chan": recv_chan
|
||||
}
|
||||
)
|
||||
)
|
||||
return recv_chan.receive()
|
||||
|
||||
def publish(self, channel_: str, data: Any) -> None:
|
||||
"""
|
||||
发布消息
|
||||
Args:
|
||||
channel_: 频道
|
||||
data: 数据
|
||||
|
||||
Returns:
|
||||
"""
|
||||
self.active_chan.send(
|
||||
(
|
||||
"publish",
|
||||
{
|
||||
"channel": channel_,
|
||||
"data" : data
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
def on_subscriber_receive(self, channel_: str) -> Callable[[ON_RECEIVE_FUNC], ON_RECEIVE_FUNC]:
|
||||
"""
|
||||
订阅者接收消息时的回调
|
||||
Args:
|
||||
channel_: 频道
|
||||
|
||||
Returns:
|
||||
装饰器
|
||||
"""
|
||||
if not IS_MAIN_PROCESS:
|
||||
raise RuntimeError("Cannot subscribe in sub process.")
|
||||
|
||||
def decorator(func: ON_RECEIVE_FUNC) -> ON_RECEIVE_FUNC:
|
||||
async def wrapper(data: Any):
|
||||
if is_coroutine_callable(func):
|
||||
await func(data)
|
||||
else:
|
||||
func(data)
|
||||
|
||||
if IS_MAIN_PROCESS:
|
||||
if channel_ not in _on_main_subscriber_receive_funcs:
|
||||
_on_main_subscriber_receive_funcs[channel_] = []
|
||||
_on_main_subscriber_receive_funcs[channel_].append(wrapper)
|
||||
else:
|
||||
if channel_ not in _on_sub_subscriber_receive_funcs:
|
||||
_on_sub_subscriber_receive_funcs[channel_] = []
|
||||
_on_sub_subscriber_receive_funcs[channel_].append(wrapper)
|
||||
return wrapper
|
||||
|
||||
return decorator
|
||||
|
||||
@staticmethod
|
||||
async def run_subscriber_receive_funcs(channel_: str, data: Any):
|
||||
"""
|
||||
运行订阅者接收函数
|
||||
Args:
|
||||
channel_: 频道
|
||||
data: 数据
|
||||
"""
|
||||
[asyncio.create_task(func(data)) for func in _on_main_subscriber_receive_funcs[channel_]]
|
||||
|
||||
async def start_receive_loop(self):
|
||||
"""
|
||||
启动发布订阅接收器循环,在主进程中运行,若有子进程订阅则推送给子进程
|
||||
"""
|
||||
|
||||
if not IS_MAIN_PROCESS:
|
||||
raise RuntimeError("Cannot start receive loop in sub process.")
|
||||
while True:
|
||||
data = await self.active_chan.async_receive()
|
||||
if data[0] == "publish":
|
||||
# 运行主进程订阅函数
|
||||
await self.run_subscriber_receive_funcs(data[1]["channel"], data[1]["data"])
|
||||
# 推送给子进程
|
||||
self.publish_channel.send(data)
|
||||
|
||||
|
||||
class GlobalKeyValueStore:
|
||||
_instance = None
|
||||
_lock = threading.Lock()
|
||||
|
||||
@classmethod
|
||||
def get_instance(cls):
|
||||
if cls._instance is None:
|
||||
with cls._lock:
|
||||
if cls._instance is None:
|
||||
cls._instance = KeyValueStore()
|
||||
return cls._instance
|
||||
|
||||
|
||||
shared_memory: KeyValueStore = GlobalKeyValueStore.get_instance() # 共享内存对象
|
||||
|
||||
# 全局单例访问点
|
||||
if IS_MAIN_PROCESS:
|
||||
@shared_memory.passive_chan.on_receive(lambda d: d[0] == "get")
|
||||
def on_get(data: tuple[str, dict[str, Any]]):
|
||||
key = data[1]["key"]
|
||||
default = data[1]["default"]
|
||||
recv_chan = data[1]["recv_chan"]
|
||||
recv_chan.send(shared_memory.get(key, default))
|
||||
|
||||
|
||||
@shared_memory.passive_chan.on_receive(lambda d: d[0] == "set")
|
||||
def on_set(data: tuple[str, dict[str, Any]]):
|
||||
key = data[1]["key"]
|
||||
value = data[1]["value"]
|
||||
shared_memory.set(key, value)
|
||||
|
||||
|
||||
@shared_memory.passive_chan.on_receive(lambda d: d[0] == "delete")
|
||||
def on_delete(data: tuple[str, dict[str, Any]]):
|
||||
key = data[1]["key"]
|
||||
shared_memory.delete(key)
|
||||
|
||||
|
||||
@shared_memory.passive_chan.on_receive(lambda d: d[0] == "get_all")
|
||||
def on_get_all(data: tuple[str, dict[str, Any]]):
|
||||
recv_chan = data[1]["recv_chan"]
|
||||
recv_chan.send(shared_memory.get_all())
|
||||
|
||||
_ref_count = 0 # import 引用计数, 防止获取空指针
|
||||
if not IS_MAIN_PROCESS:
|
||||
if (shared_memory is None) and _ref_count > 1:
|
||||
raise RuntimeError("Shared memory not initialized.")
|
||||
_ref_count += 1
|
132
liteyuki/config.py
Normal file
132
liteyuki/config.py
Normal file
@ -0,0 +1,132 @@
|
||||
"""
|
||||
该模块用于常用配置文件的加载
|
||||
多配置文件编写原则:
|
||||
1. 尽量不要冲突: 一个键不要多次出现
|
||||
2. 分工明确: 每个配置文件给一个或一类服务提供配置
|
||||
3. 扁平化编写: 配置文件尽量扁平化,不要出现过多的嵌套
|
||||
4. 注意冲突时的优先级: 项目目录下的配置文件优先级高于config目录下的配置文件
|
||||
5. 请不要将需要动态加载的内容写入配置文件,你应该使用其他储存方式
|
||||
"""
|
||||
|
||||
import os
|
||||
import json
|
||||
import copy
|
||||
import toml
|
||||
import yaml
|
||||
|
||||
from typing import Any
|
||||
|
||||
from liteyuki.log import logger
|
||||
|
||||
_SUPPORTED_CONFIG_FORMATS = (".yaml", ".yml", ".json", ".toml")
|
||||
|
||||
|
||||
def flat_config(config: dict[str, Any]) -> dict[str, Any]:
|
||||
"""
|
||||
扁平化配置文件
|
||||
|
||||
{a:{b:{c:1}}} -> {"a.b.c": 1}
|
||||
Args:
|
||||
config: 配置项目
|
||||
|
||||
Returns:
|
||||
扁平化后的配置文件,但也包含原有的键值对
|
||||
"""
|
||||
new_config = copy.deepcopy(config)
|
||||
for key, value in config.items():
|
||||
if isinstance(value, dict):
|
||||
for k, v in flat_config(value).items():
|
||||
new_config[f"{key}.{k}"] = v
|
||||
return new_config
|
||||
|
||||
|
||||
def load_from_yaml(file_: str) -> dict[str, Any]:
|
||||
"""
|
||||
Load config from yaml file
|
||||
|
||||
"""
|
||||
logger.debug(f"Loading YAML config from {file_}")
|
||||
config = yaml.safe_load(open(file_, "r", encoding="utf-8"))
|
||||
return flat_config(config if config is not None else {})
|
||||
|
||||
|
||||
def load_from_json(file_: str) -> dict[str, Any]:
|
||||
"""
|
||||
Load config from json file
|
||||
"""
|
||||
logger.debug(f"Loading JSON config from {file_}")
|
||||
config = json.load(open(file_, "r", encoding="utf-8"))
|
||||
return flat_config(config if config is not None else {})
|
||||
|
||||
|
||||
def load_from_toml(file_: str) -> dict[str, Any]:
|
||||
"""
|
||||
Load config from toml file
|
||||
"""
|
||||
logger.debug(f"Loading TOML config from {file_}")
|
||||
config = toml.load(open(file_, "r", encoding="utf-8"))
|
||||
return flat_config(config if config is not None else {})
|
||||
|
||||
|
||||
def load_from_files(*files: str, no_warning: bool = False) -> dict[str, Any]:
|
||||
"""
|
||||
从指定文件加载配置项,会自动识别文件格式
|
||||
默认执行扁平化选项
|
||||
"""
|
||||
config = {}
|
||||
for file in files:
|
||||
if os.path.exists(file):
|
||||
if file.endswith((".yaml", "yml")):
|
||||
config.update(load_from_yaml(file))
|
||||
elif file.endswith(".json"):
|
||||
config.update(load_from_json(file))
|
||||
elif file.endswith(".toml"):
|
||||
config.update(load_from_toml(file))
|
||||
else:
|
||||
if not no_warning:
|
||||
logger.warning(f"Unsupported config file format: {file}")
|
||||
else:
|
||||
if not no_warning:
|
||||
logger.warning(f"Config file not found: {file}")
|
||||
return config
|
||||
|
||||
|
||||
def load_configs_from_dirs(
|
||||
*directories: str, no_waring: bool = False
|
||||
) -> dict[str, Any]:
|
||||
"""
|
||||
从目录下加载配置文件,不递归
|
||||
按照读取文件的优先级反向覆盖
|
||||
默认执行扁平化选项
|
||||
"""
|
||||
config = {}
|
||||
for directory in directories:
|
||||
if not os.path.exists(directory):
|
||||
if not no_waring:
|
||||
logger.warning(f"Directory not found: {directory}")
|
||||
continue
|
||||
for file in os.listdir(directory):
|
||||
if file.endswith(_SUPPORTED_CONFIG_FORMATS):
|
||||
config.update(
|
||||
load_from_files(os.path.join(directory, file), no_warning=no_waring)
|
||||
)
|
||||
return config
|
||||
|
||||
|
||||
def load_config_in_default(no_waring: bool = False) -> dict[str, Any]:
|
||||
"""
|
||||
从一个标准的轻雪项目加载配置文件
|
||||
项目目录下的config.*和config目录下的所有配置文件
|
||||
项目目录下的配置文件优先
|
||||
"""
|
||||
config = load_configs_from_dirs("config", no_waring=no_waring)
|
||||
config.update(
|
||||
load_from_files(
|
||||
"config.yaml",
|
||||
"config.toml",
|
||||
"config.json",
|
||||
"config.yml",
|
||||
no_warning=no_waring,
|
||||
)
|
||||
)
|
||||
return config
|
2
liteyuki/core/__init__.py
Normal file
2
liteyuki/core/__init__.py
Normal file
@ -0,0 +1,2 @@
|
||||
from .manager import *
|
||||
|
272
liteyuki/core/manager.py
Normal file
272
liteyuki/core/manager.py
Normal file
@ -0,0 +1,272 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Copyright (C) 2020-2024 LiteyukiStudio. All Rights Reserved
|
||||
|
||||
@Time : 2024/7/27 上午11:12
|
||||
@Author : snowykami
|
||||
@Email : snowykami@outlook.com
|
||||
@File : manager.py
|
||||
@Software: PyCharm
|
||||
"""
|
||||
import asyncio
|
||||
import multiprocessing
|
||||
import threading
|
||||
from multiprocessing import Process
|
||||
from typing import Any, Callable, TYPE_CHECKING, TypeAlias
|
||||
|
||||
from croterline.context import Context
|
||||
from croterline.process import SubProcess, ProcessFuncType
|
||||
|
||||
from liteyuki.log import logger
|
||||
from liteyuki.utils import IS_MAIN_PROCESS
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from liteyuki.bot.lifespan import Lifespan
|
||||
from liteyuki.comm.storage import KeyValueStore
|
||||
|
||||
from liteyuki.comm import Channel
|
||||
|
||||
if IS_MAIN_PROCESS:
|
||||
from liteyuki.comm.channel import get_channel, publish_channel, get_channels
|
||||
from liteyuki.comm.storage import shared_memory
|
||||
from liteyuki.comm.channel import (
|
||||
channel_deliver_active_channel,
|
||||
channel_deliver_passive_channel,
|
||||
)
|
||||
else:
|
||||
from liteyuki.comm import channel
|
||||
from liteyuki.comm import storage
|
||||
|
||||
TARGET_FUNC: TypeAlias = Callable[..., Any]
|
||||
TIMEOUT = 10
|
||||
|
||||
__all__ = ["ProcessManager", "sub_process_manager"]
|
||||
multiprocessing.set_start_method("spawn", force=True)
|
||||
|
||||
|
||||
class ChannelDeliver:
|
||||
def __init__(
|
||||
self,
|
||||
active: Channel[Any],
|
||||
passive: Channel[Any],
|
||||
channel_deliver_active: Channel[Channel[Any]],
|
||||
channel_deliver_passive: Channel[tuple[str, dict]],
|
||||
publish: Channel[tuple[str, Any]],
|
||||
):
|
||||
self.active = active
|
||||
self.passive = passive
|
||||
self.channel_deliver_active = channel_deliver_active
|
||||
self.channel_deliver_passive = channel_deliver_passive
|
||||
self.publish = publish
|
||||
|
||||
|
||||
# 函数处理一些跨进程通道的
|
||||
def _delivery_channel_wrapper(
|
||||
func: TARGET_FUNC, cd: ChannelDeliver, sm: "KeyValueStore", *args, **kwargs
|
||||
):
|
||||
"""
|
||||
子进程入口函数
|
||||
处理一些操作
|
||||
"""
|
||||
# 给子进程设置通道
|
||||
if IS_MAIN_PROCESS:
|
||||
raise RuntimeError("Function should only be called in a sub process.")
|
||||
|
||||
channel.active_channel = cd.active # 子进程主动通道
|
||||
channel.passive_channel = cd.passive # 子进程被动通道
|
||||
channel.channel_deliver_active_channel = (
|
||||
cd.channel_deliver_active
|
||||
) # 子进程通道传递主动通道
|
||||
channel.channel_deliver_passive_channel = (
|
||||
cd.channel_deliver_passive
|
||||
) # 子进程通道传递被动通道
|
||||
channel.publish_channel = cd.publish # 子进程发布通道
|
||||
|
||||
# 给子进程创建共享内存实例
|
||||
|
||||
storage.shared_memory = sm
|
||||
|
||||
func(*args, **kwargs)
|
||||
|
||||
|
||||
class ProcessManager:
|
||||
"""
|
||||
进程管理器
|
||||
"""
|
||||
|
||||
def __init__(self, lifespan: "Lifespan"):
|
||||
self.lifespan = lifespan
|
||||
self.targets: dict[str, tuple[Callable, tuple, dict]] = {}
|
||||
self.processes: dict[str, Process] = {}
|
||||
|
||||
def _run_process(self, name: str):
|
||||
"""
|
||||
开启后自动监控进程,并添加到进程字典中,会阻塞,请创建task
|
||||
Args:
|
||||
name:
|
||||
Returns:
|
||||
"""
|
||||
if name not in self.targets:
|
||||
raise KeyError(f"Process {name} not found.")
|
||||
|
||||
chan_active = get_channel(f"{name}-active")
|
||||
|
||||
def _start_process():
|
||||
process = Process(
|
||||
target=self.targets[name][0],
|
||||
args=self.targets[name][1],
|
||||
kwargs=self.targets[name][2],
|
||||
daemon=True,
|
||||
)
|
||||
self.processes[name] = process
|
||||
process.start()
|
||||
|
||||
# 启动进程并监听信号
|
||||
_start_process()
|
||||
while True:
|
||||
data = chan_active.receive()
|
||||
if data == 0:
|
||||
# 停止
|
||||
logger.info(f"Stopping process {name}")
|
||||
self.terminate(name)
|
||||
break
|
||||
elif data == 1:
|
||||
# 重启
|
||||
logger.info(f"Restarting process {name}")
|
||||
self.terminate(name)
|
||||
_start_process()
|
||||
continue
|
||||
else:
|
||||
logger.warning("Unknown data received, ignored.")
|
||||
|
||||
def start_all(self):
|
||||
"""
|
||||
对外启动方法,启动所有进程,创建asyncio task
|
||||
"""
|
||||
# [asyncio.create_task(self._run_process(name)) for name in self.targets]
|
||||
|
||||
for name in self.targets:
|
||||
logger.debug(f"Starting process {name}")
|
||||
threading.Thread(
|
||||
target=self._run_process, args=(name,), daemon=True
|
||||
).start()
|
||||
|
||||
def add_target(self, name: str, target: TARGET_FUNC, args: tuple = (), kwargs=None):
|
||||
"""
|
||||
添加进程
|
||||
Args:
|
||||
name: 进程名,用于获取和唯一标识
|
||||
target: 进程函数
|
||||
args: 进程函数参数
|
||||
kwargs: 进程函数关键字参数,通常会默认传入chan_active和chan_passive
|
||||
"""
|
||||
if kwargs is None:
|
||||
kwargs = {}
|
||||
chan_active: Channel = Channel(name=f"{name}-active")
|
||||
chan_passive: Channel = Channel(name=f"{name}-passive")
|
||||
|
||||
channel_deliver = ChannelDeliver(
|
||||
active=chan_active,
|
||||
passive=chan_passive,
|
||||
channel_deliver_active=channel_deliver_active_channel,
|
||||
channel_deliver_passive=channel_deliver_passive_channel,
|
||||
publish=publish_channel,
|
||||
)
|
||||
|
||||
self.targets[name] = (
|
||||
_delivery_channel_wrapper,
|
||||
(target, channel_deliver, shared_memory, *args),
|
||||
kwargs,
|
||||
)
|
||||
# 主进程通道
|
||||
|
||||
def join_all(self):
|
||||
for name, process in self.targets:
|
||||
process.join()
|
||||
|
||||
def terminate(self, name: str):
|
||||
"""
|
||||
终止进程并从进程字典中删除
|
||||
Args:
|
||||
name:
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
if name not in self.processes:
|
||||
logger.warning(f"Process {name} not found.")
|
||||
return
|
||||
process = self.processes[name]
|
||||
process.terminate()
|
||||
process.join(TIMEOUT)
|
||||
if process.is_alive():
|
||||
process.kill()
|
||||
logger.success(f"Process {name} terminated.")
|
||||
|
||||
def terminate_all(self):
|
||||
for name in self.targets:
|
||||
self.terminate(name)
|
||||
|
||||
def is_process_alive(self, name: str) -> bool:
|
||||
"""
|
||||
检查进程是否存活
|
||||
Args:
|
||||
name:
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
if name not in self.targets:
|
||||
logger.warning(f"Process {name} not found.")
|
||||
return self.processes[name].is_alive()
|
||||
|
||||
|
||||
# new version
|
||||
|
||||
|
||||
class _SubProcessManager:
|
||||
|
||||
def __init__(self):
|
||||
self.processes: dict[str, SubProcess] = {}
|
||||
|
||||
def new_process(
|
||||
self, name: str, *args, **kwargs
|
||||
) -> Callable[[ProcessFuncType], None]:
|
||||
def decorator(func: ProcessFuncType):
|
||||
self.processes[name] = SubProcess(name, func, *args, **kwargs)
|
||||
|
||||
return decorator
|
||||
|
||||
def add(self, name: str, func: ProcessFuncType, *args, **kwargs):
|
||||
"""
|
||||
添加子进程
|
||||
Args:
|
||||
func: 子进程函数
|
||||
name: 子进程名称
|
||||
args: 子进程函数参数
|
||||
kwargs: 子进程函数关键字参数
|
||||
Returns:
|
||||
"""
|
||||
self.processes[name] = SubProcess(name, func, *args, **kwargs)
|
||||
|
||||
def start(self, name: str):
|
||||
"""
|
||||
启动指定子进程
|
||||
Args:
|
||||
name: 子进程名称
|
||||
Returns:
|
||||
"""
|
||||
if name not in self.processes:
|
||||
raise KeyError(f"Process {name} not found.")
|
||||
self.processes[name].start()
|
||||
|
||||
def start_all(self):
|
||||
"""
|
||||
启动所有子进程
|
||||
"""
|
||||
for name, process in self.processes.items():
|
||||
process.start()
|
||||
logger.debug(f"Starting process {name}")
|
||||
|
||||
|
||||
sub_process_manager = _SubProcessManager()
|
4
liteyuki/dev/__init__.py
Normal file
4
liteyuki/dev/__init__.py
Normal file
@ -0,0 +1,4 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
该模块用于存放一些开发工具
|
||||
"""
|
90
liteyuki/dev/observer.py
Normal file
90
liteyuki/dev/observer.py
Normal file
@ -0,0 +1,90 @@
|
||||
"""
|
||||
此模块用于注册观察者函数,使用watchdog监控文件变化并重启bot
|
||||
启用该模块需要在配置文件中设置`dev_mode`为True
|
||||
"""
|
||||
import time
|
||||
from typing import Callable, TypeAlias
|
||||
|
||||
from watchdog.events import FileSystemEvent, FileSystemEventHandler
|
||||
from watchdog.observers import Observer
|
||||
|
||||
from liteyuki import get_bot, get_config_with_compat, logger
|
||||
|
||||
liteyuki_bot = get_bot()
|
||||
|
||||
CALLBACK_FUNC: TypeAlias = Callable[[FileSystemEvent], None] # 位置1为FileSystemEvent
|
||||
FILTER_FUNC: TypeAlias = Callable[[FileSystemEvent], bool] # 位置1为FileSystemEvent
|
||||
observer = Observer()
|
||||
|
||||
|
||||
def debounce(wait):
|
||||
"""
|
||||
防抖函数
|
||||
"""
|
||||
def decorator(func):
|
||||
def wrapper(*args, **kwargs):
|
||||
nonlocal last_call_time
|
||||
current_time = time.time()
|
||||
if (current_time - last_call_time) > wait:
|
||||
last_call_time = current_time
|
||||
return func(*args, **kwargs)
|
||||
|
||||
last_call_time = None
|
||||
return wrapper
|
||||
|
||||
return decorator
|
||||
|
||||
|
||||
if get_config_with_compat("liteyuki.reload", ("dev_mode",), False):
|
||||
logger.debug("Liteyuki Reload enabled, watching for file changes...")
|
||||
observer.start()
|
||||
|
||||
|
||||
class CodeModifiedHandler(FileSystemEventHandler):
|
||||
"""
|
||||
Handler for code file changes
|
||||
"""
|
||||
|
||||
@debounce(1)
|
||||
def on_modified(self, event):
|
||||
raise NotImplementedError("on_modified must be implemented")
|
||||
|
||||
def on_created(self, event):
|
||||
self.on_modified(event)
|
||||
|
||||
def on_deleted(self, event):
|
||||
self.on_modified(event)
|
||||
|
||||
def on_moved(self, event):
|
||||
self.on_modified(event)
|
||||
|
||||
def on_any_event(self, event):
|
||||
self.on_modified(event)
|
||||
|
||||
|
||||
def on_file_system_event(directories: tuple[str], recursive: bool = True, event_filter: FILTER_FUNC = None) -> Callable[[CALLBACK_FUNC], CALLBACK_FUNC]:
|
||||
"""
|
||||
注册文件系统变化监听器
|
||||
Args:
|
||||
directories: 监听目录们
|
||||
recursive: 是否递归监听子目录
|
||||
event_filter: 事件过滤器, 返回True则执行回调函数
|
||||
Returns:
|
||||
装饰器,装饰一个函数在接收到数据后执行
|
||||
"""
|
||||
|
||||
def decorator(func: CALLBACK_FUNC) -> CALLBACK_FUNC:
|
||||
def wrapper(event: FileSystemEvent):
|
||||
|
||||
if event_filter is not None and not event_filter(event):
|
||||
return
|
||||
func(event)
|
||||
|
||||
code_modified_handler = CodeModifiedHandler()
|
||||
code_modified_handler.on_modified = wrapper
|
||||
for directory in directories:
|
||||
observer.schedule(code_modified_handler, directory, recursive=recursive)
|
||||
|
||||
return func
|
||||
|
||||
return decorator
|
28
liteyuki/dev/plugin.py
Normal file
28
liteyuki/dev/plugin.py
Normal file
@ -0,0 +1,28 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Copyright (C) 2020-2024 LiteyukiStudio. All Rights Reserved
|
||||
|
||||
@Time : 2024/8/18 上午5:04
|
||||
@Author : snowykami
|
||||
@Email : snowykami@outlook.com
|
||||
@File : plugin.py
|
||||
@Software: PyCharm
|
||||
"""
|
||||
from pathlib import Path
|
||||
|
||||
from liteyuki.bot import LiteyukiBot
|
||||
from liteyuki.config import load_config_in_default
|
||||
|
||||
|
||||
def run_plugins(*module_path: str | Path):
|
||||
"""
|
||||
运行插件,无需手动初始化bot
|
||||
Args:
|
||||
module_path: 插件路径,参考`liteyuki.load_plugin`的函数签名
|
||||
"""
|
||||
cfg = load_config_in_default()
|
||||
plugins = cfg.get("liteyuki.plugins", [])
|
||||
plugins.extend(module_path)
|
||||
cfg["liteyuki.plugins"] = plugins
|
||||
bot = LiteyukiBot(**cfg)
|
||||
bot.run()
|
10
liteyuki/exception.py
Normal file
10
liteyuki/exception.py
Normal file
@ -0,0 +1,10 @@
|
||||
"""exception模块包含了liteyuki运行中的所有错误
|
||||
"""
|
||||
|
||||
from typing import Any, Optional
|
||||
|
||||
|
||||
class LiteyukiException(BaseException):
|
||||
"""Liteyuki的异常基类。"""
|
||||
def __str__(self) -> str:
|
||||
return self.__repr__()
|
63
liteyuki/log.py
Normal file
63
liteyuki/log.py
Normal file
@ -0,0 +1,63 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Copyright (C) 2020-2024 LiteyukiStudio. All Rights Reserved
|
||||
|
||||
@Time : 2024/7/27 上午9:12
|
||||
@Author : snowykami
|
||||
@Email : snowykami@outlook.com
|
||||
@File : log.py
|
||||
@Software: PyCharm
|
||||
"""
|
||||
import sys
|
||||
|
||||
import loguru
|
||||
|
||||
logger = loguru.logger
|
||||
|
||||
# DEBUG日志格式
|
||||
debug_format: str = (
|
||||
"<c>{time:YYYY-MM-DD HH:mm:ss}</c> "
|
||||
"<lvl>[{level.icon}]</lvl> "
|
||||
"<c><{name}.{module}.{function}:{line}></c> "
|
||||
"{message}"
|
||||
)
|
||||
|
||||
# 默认日志格式
|
||||
default_format: str = (
|
||||
"<c>{time:MM-DD HH:mm:ss}</c> "
|
||||
"<lvl>[{level.icon}]</lvl> "
|
||||
"<c><{name}></c> "
|
||||
"{message}"
|
||||
)
|
||||
|
||||
|
||||
def get_format(level: str) -> str:
|
||||
if level == "DEBUG":
|
||||
return debug_format
|
||||
else:
|
||||
return default_format
|
||||
|
||||
|
||||
def init_log(config: dict):
|
||||
"""
|
||||
在语言加载完成后执行
|
||||
Returns:
|
||||
|
||||
"""
|
||||
|
||||
logger.remove()
|
||||
logger.add(
|
||||
sys.stdout,
|
||||
level=0,
|
||||
diagnose=False,
|
||||
format=get_format(config.get("log_level", "INFO")),
|
||||
)
|
||||
show_icon = config.get("log_icon", True)
|
||||
logger.level("DEBUG", color="<blue>", icon=f"{'🐛' if show_icon else ''}DEBUG")
|
||||
logger.level("INFO", color="<normal>", icon=f"{'ℹ️' if show_icon else ''}INFO")
|
||||
logger.level("SUCCESS", color="<green>", icon=f"{'✅' if show_icon else ''}SUCCESS")
|
||||
logger.level("WARNING", color="<yellow>", icon=f"{'⚠️' if show_icon else ''}WARNING")
|
||||
logger.level("ERROR", color="<red>", icon=f"{'⭕' if show_icon else ''}ERROR")
|
||||
|
||||
|
||||
init_log(config={})
|
10
liteyuki/message/__init__.py
Normal file
10
liteyuki/message/__init__.py
Normal file
@ -0,0 +1,10 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Copyright (C) 2020-2024 LiteyukiStudio. All Rights Reserved
|
||||
|
||||
@Time : 2024/8/19 下午10:44
|
||||
@Author : snowykami
|
||||
@Email : snowykami@outlook.com
|
||||
@File : __init__.py.py
|
||||
@Software: PyCharm
|
||||
"""
|
88
liteyuki/message/event.py
Normal file
88
liteyuki/message/event.py
Normal file
@ -0,0 +1,88 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Copyright (C) 2020-2024 LiteyukiStudio. All Rights Reserved
|
||||
|
||||
@Time : 2024/8/19 下午10:47
|
||||
@Author : snowykami
|
||||
@Email : snowykami@outlook.com
|
||||
@File : event.py
|
||||
@Software: PyCharm
|
||||
"""
|
||||
from typing import Any, Optional
|
||||
|
||||
from liteyuki import Channel
|
||||
from liteyuki.comm.storage import shared_memory
|
||||
|
||||
|
||||
class MessageEvent:
|
||||
def __init__(
|
||||
self,
|
||||
|
||||
bot_id: str,
|
||||
message: list[dict[str, Any]] | str,
|
||||
message_type: str,
|
||||
raw_message: str,
|
||||
session_id: str,
|
||||
user_id: str,
|
||||
session_type: str,
|
||||
receive_channel: Optional[Channel["MessageEvent"]] = None,
|
||||
data: Optional[dict[str, Any]] = None,
|
||||
):
|
||||
"""
|
||||
轻雪抽象消息事件
|
||||
Args:
|
||||
|
||||
bot_id: 机器人ID
|
||||
message: 消息,消息段数组[{type: str, data: dict[str, Any]}]
|
||||
raw_message: 原始消息(通常为纯文本的格式)
|
||||
message_type: 消息类型(private, group, other)
|
||||
|
||||
session_id: 会话ID(私聊通常为用户ID,群聊通常为群ID)
|
||||
session_type: 会话类型(private, group)
|
||||
receive_channel: 接收频道(用于回复消息)
|
||||
|
||||
data: 附加数据
|
||||
"""
|
||||
|
||||
if data is None:
|
||||
data = {}
|
||||
self.message_type = message_type
|
||||
self.data = data
|
||||
self.bot_id = bot_id
|
||||
|
||||
self.message = message
|
||||
self.raw_message = raw_message
|
||||
|
||||
self.session_id = session_id
|
||||
self.session_type = session_type
|
||||
self.user_id = user_id
|
||||
|
||||
self.receive_channel = receive_channel
|
||||
|
||||
def __str__(self):
|
||||
return (f"Event(message_type={self.message_type}, data={self.data}, bot_id={self.bot_id}, "
|
||||
f"session_id={self.session_id}, session_type={self.session_type})")
|
||||
|
||||
def reply(self, message: str | dict[str, Any]):
|
||||
"""
|
||||
回复消息
|
||||
Args:
|
||||
message:
|
||||
Returns:
|
||||
"""
|
||||
reply_event = MessageEvent(
|
||||
message_type=self.session_type,
|
||||
message=message,
|
||||
raw_message="",
|
||||
data={
|
||||
"message": message
|
||||
},
|
||||
bot_id=self.bot_id,
|
||||
session_id=self.session_id,
|
||||
user_id=self.user_id,
|
||||
session_type=self.session_type,
|
||||
receive_channel=None
|
||||
)
|
||||
# shared_memory.publish(self.receive_channel, reply_event)
|
||||
if self.receive_channel:
|
||||
self.receive_channel.send(reply_event)
|
63
liteyuki/message/matcher.py
Normal file
63
liteyuki/message/matcher.py
Normal file
@ -0,0 +1,63 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Copyright (C) 2020-2024 LiteyukiStudio. All Rights Reserved
|
||||
|
||||
@Time : 2024/8/19 下午10:51
|
||||
@Author : snowykami
|
||||
@Email : snowykami@outlook.com
|
||||
@File : matcher.py
|
||||
@Software: PyCharm
|
||||
"""
|
||||
import traceback
|
||||
from typing import Any, TypeAlias, Callable, Coroutine
|
||||
|
||||
from liteyuki.message.event import MessageEvent
|
||||
from liteyuki.message.rule import Rule
|
||||
|
||||
EventHandler: TypeAlias = Callable[[MessageEvent], Coroutine[None, None, Any]]
|
||||
|
||||
|
||||
class Matcher:
|
||||
def __init__(self, rule: Rule, priority: int, block: bool):
|
||||
"""
|
||||
匹配器
|
||||
Args:
|
||||
rule: 规则
|
||||
priority: 优先级 >= 0
|
||||
block: 是否阻断后续优先级更低的匹配器
|
||||
"""
|
||||
self.rule = rule
|
||||
self.priority = priority
|
||||
self.block = block
|
||||
self.handlers: list[EventHandler] = []
|
||||
|
||||
def __str__(self):
|
||||
return f"Matcher(rule={self.rule}, priority={self.priority}, block={self.block})"
|
||||
|
||||
def handle(self) -> Callable[[EventHandler], EventHandler]:
|
||||
"""
|
||||
添加处理函数,装饰器
|
||||
Returns:
|
||||
装饰器 handler
|
||||
"""
|
||||
def decorator(handler: EventHandler) -> EventHandler:
|
||||
self.handlers.append(handler)
|
||||
return handler
|
||||
|
||||
return decorator
|
||||
|
||||
async def run(self, event: MessageEvent) -> None:
|
||||
"""
|
||||
运行处理函数
|
||||
Args:
|
||||
event:
|
||||
Returns:
|
||||
"""
|
||||
if not await self.rule(event):
|
||||
return
|
||||
|
||||
for handler in self.handlers:
|
||||
try:
|
||||
await handler(event)
|
||||
except Exception:
|
||||
traceback.print_exc()
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user