mirror of
https://github.com/zhenxun-org/zhenxun_bot.git
synced 2026-09-28 16:20:56 +08:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
30bcef32e4 | ||
|
|
33d5cd6b5c | ||
|
|
c574459485 | ||
|
|
ca9933a489 | ||
|
|
429b7e38b5 | ||
|
|
8c7110ae85 | ||
|
|
a43a7b5f75 | ||
|
|
5b9218251e | ||
|
|
461ca0af32 | ||
|
|
97443a11e2 | ||
|
|
5aff21aab2 | ||
|
|
1cbe807b05 | ||
|
|
d8ea0c6baf | ||
|
|
d6ef64c70a | ||
|
|
b31fbf5810 | ||
|
|
f2ab119397 | ||
|
|
33e42be32d | ||
|
|
8c92756493 | ||
|
|
c2ca09867a | ||
|
|
a1eb981fd4 | ||
|
|
e32f086b8a | ||
|
|
bdfdbbcaeb | ||
|
|
3cdabb7d81 | ||
|
|
422ed2ff51 | ||
|
|
6e76ffb60f | ||
|
|
eaa809931c | ||
|
|
4163435c84 | ||
|
|
99263ab36a | ||
|
|
0d76fa8560 | ||
|
|
c3644576b8 | ||
|
|
325e345b4e | ||
|
|
3b7972454d | ||
|
|
6af5e42bf0 | ||
|
|
edc68921c9 | ||
|
|
83c3f44ae7 | ||
|
|
6d7e79e9de | ||
|
|
401029bd27 | ||
|
|
33a0477fab | ||
|
|
6826c10a09 | ||
|
|
8133b61ebd | ||
|
|
898d7b40b3 | ||
|
|
5a9beb2e5c | ||
|
|
a63fda8a0d | ||
|
|
0baf043f74 | ||
|
|
cbc32a79d2 | ||
|
|
df5a68bcec | ||
|
|
1ca6a67a24 | ||
|
|
f27a537f70 | ||
|
|
aa76fae386 | ||
|
|
d35d53825c | ||
|
|
7f743141fb | ||
|
|
fa88a08d8e | ||
|
|
06d0bb1ab2 | ||
|
|
3c0f35b3b7 | ||
|
|
396d9c1a65 | ||
|
|
e41f3a4ce7 | ||
|
|
8f9e4d6e4f | ||
|
|
d0764f0865 | ||
|
|
ab32eedbb9 | ||
|
|
73f858e071 | ||
|
|
73b4a1254c | ||
|
|
fbf82d999f | ||
|
|
1f68481256 | ||
|
|
876cfb421a | ||
|
|
c97f1bd750 | ||
|
|
e4dec9f0de | ||
|
|
282b4c102f | ||
|
|
37e0003810 | ||
|
|
42683e4f09 | ||
|
|
6352ea7c6f | ||
|
|
40c10e672c | ||
|
|
fef85190c5 | ||
|
|
eea1be0fc1 | ||
|
|
633862d121 | ||
|
|
86ac770971 | ||
|
|
7685b95031 | ||
|
|
a6105e8a1b | ||
|
|
9feea3ba1d | ||
|
|
22f415abfa | ||
|
|
c99292d327 | ||
|
|
362ef519b7 | ||
|
|
a053c120c4 | ||
|
|
4d133ed2df | ||
|
|
d2cf18e11d | ||
|
|
5ddfd2b5b9 | ||
|
|
71264bffa1 | ||
|
|
f39005f5e1 | ||
|
|
9454308ba7 | ||
|
|
51dd622e20 | ||
|
|
05de55cc6b | ||
|
|
db441c8406 | ||
|
|
295c535002 | ||
|
|
b0606feff3 | ||
|
|
2070014021 | ||
|
|
def53aa22e | ||
|
|
a3d10385a0 | ||
|
|
cb7527a44b | ||
|
|
32e420efbb | ||
|
|
6db9bef115 | ||
|
|
a0b440efcd | ||
|
|
d2bb7ae493 | ||
|
|
b4e213518b | ||
|
|
e3b0da4717 | ||
|
|
155de5a032 | ||
|
|
57e166940d | ||
|
|
721efb79fd | ||
|
|
5338342208 | ||
|
|
c49ef3052f | ||
|
|
802f4df2ce | ||
|
|
1edcee9ce3 | ||
|
|
3acd7d7d41 | ||
|
|
83dc29a562 | ||
|
|
b25b9ca928 | ||
|
|
0dc11b7edb | ||
|
|
9b477e2eb8 | ||
|
|
272f6726ea | ||
|
|
181717001a | ||
|
|
43b654285f | ||
|
|
50e7d5036e | ||
|
|
b6b2f2e2a6 | ||
|
|
b9d08855d0 | ||
|
|
93deb2b1be | ||
|
|
b3871d550c | ||
|
|
ab8e87899c | ||
|
|
9d6f44849a | ||
|
|
380588c11d | ||
|
|
5810c28294 | ||
|
|
c039015e8f | ||
|
|
0dda0a228a | ||
|
|
940514b16b | ||
|
|
8adff43113 | ||
|
|
b59699dabe | ||
|
|
f496ce4861 | ||
|
|
5fb4eccd0f | ||
|
|
31e4cadecb | ||
|
|
994484f3b0 | ||
|
|
7b3a3451f6 | ||
|
|
14d075bc17 | ||
|
|
75369411b3 | ||
|
|
d97542a3fd | ||
|
|
2f70fcd92a | ||
|
|
182f93d618 | ||
|
|
34e28384dd | ||
|
|
f6f095f949 | ||
|
|
08609bdf53 | ||
|
|
5de5667f80 | ||
|
|
69b3ba47b3 | ||
|
|
11c1d0b827 | ||
|
|
8f97880754 | ||
|
|
ff435bea95 | ||
|
|
cd66b7f5b8 | ||
|
|
a60286c328 | ||
|
|
49e38862af | ||
|
|
75b8cc0766 | ||
|
|
a8e2f00e16 | ||
|
|
d099a85c59 | ||
|
|
55b2a86397 | ||
|
|
d39dfbe62f | ||
|
|
a41e62cefe | ||
|
|
c981562f90 | ||
|
|
e6cd57cc26 | ||
|
|
074152ae14 | ||
|
|
91ca7b40b8 | ||
|
|
5321c8d9c0 | ||
|
|
b7f753ebb6 | ||
|
|
c5e8ba3cdf | ||
|
|
658ef5309b | ||
|
|
823f642225 | ||
|
|
57ea6bad07 | ||
|
|
109c645e68 | ||
|
|
5ef0ce5aa8 | ||
|
|
f6d9e083b8 | ||
|
|
499517e882 | ||
|
|
648db532bb | ||
|
|
1ce4537605 | ||
|
|
701812ed9d | ||
|
|
8d9e81827b | ||
|
|
f51a2816b4 | ||
|
|
3d4a7950c5 | ||
|
|
ba6668f144 | ||
|
|
e1f7a77d15 | ||
|
|
64e2c512d3 | ||
|
|
6082e68a1f | ||
|
|
f135d48e55 | ||
|
|
751fc8fdb4 | ||
|
|
df73e79ee4 | ||
|
|
fa6cfdb609 | ||
|
|
b057a05520 | ||
|
|
cb0938721c | ||
|
|
4f3f8de734 | ||
|
|
5ba303aada | ||
|
|
bd42fefe4e | ||
|
|
d24c081973 | ||
|
|
1b9eddd905 | ||
|
|
0bfe398dc4 | ||
|
|
e9216a472f | ||
|
|
8b93ec0df6 | ||
|
|
7497706a0d | ||
|
|
6e41cb0630 | ||
|
|
62d99e7e90 | ||
|
|
43cddb3580 | ||
|
|
c9c1beda75 | ||
|
|
3ee9ae5fb8 | ||
|
|
ea6344fae1 | ||
|
|
6f54e91f5c | ||
|
|
27fb3aaa19 | ||
|
|
86d736745d | ||
|
|
821fe21c2b | ||
|
|
7f4aa518dc | ||
|
|
8cf589e433 | ||
|
|
105a29cd70 | ||
|
|
cb6a5e81e7 | ||
|
|
9c073383ad | ||
|
|
5e0756825d | ||
|
|
933523ae6d | ||
|
|
ed6b05605c | ||
|
|
71a9c34620 | ||
|
|
e29f2cb920 | ||
|
|
d47dd88d6c | ||
|
|
fb34bbce7a | ||
|
|
100236da83 | ||
|
|
a45a3002d5 | ||
|
|
9214a4c978 | ||
|
|
70761dc222 | ||
|
|
32e8136f71 | ||
|
|
8b20d99b2c | ||
|
|
1174b5cc7f | ||
|
|
ec448cfdfb | ||
|
|
f3e44b1baf | ||
|
|
0ea740aa7c | ||
|
|
5f118471cb | ||
|
|
504f78dd49 | ||
|
|
72723c7d1f | ||
|
|
6a74053853 | ||
|
|
af0b4987a4 | ||
|
|
775140cf79 | ||
|
|
6a8a796add | ||
|
|
8e4bda3760 | ||
|
|
a36e8145a8 | ||
|
|
3cbd899daa | ||
|
|
30f356a2c7 | ||
|
|
1138a115ce | ||
|
|
c397da75e7 | ||
|
|
1d141c9313 | ||
|
|
a4cc365af8 | ||
|
|
4e551bec0d | ||
|
|
e03086062d | ||
|
|
270a5a5e20 | ||
|
|
bda9133ed6 | ||
|
|
749f475f2c | ||
|
|
e38394948c | ||
|
|
51fc4cbb6a | ||
|
|
9a976cda37 | ||
|
|
e42521f0a3 | ||
|
|
0756d39ac5 | ||
|
|
4595bdcee4 | ||
|
|
38160065cc | ||
|
|
3d04f013cd | ||
|
|
8e1a416be3 | ||
|
|
a98bc42f1a | ||
|
|
6c93769838 | ||
|
|
d2dccfebc5 | ||
|
|
82fe59e469 | ||
|
|
afc05e1731 | ||
|
|
0206b0a522 | ||
|
|
0e1e5a5ebe | ||
|
|
9ad088ed6a | ||
|
|
34da04bba5 | ||
|
|
09ce812faf | ||
|
|
1dc8cb2a98 | ||
|
|
e73c509823 | ||
|
|
e70a85400f | ||
|
|
f7aecf8eed | ||
|
|
cc2645aa9d | ||
|
|
52d23c4a8d | ||
|
|
1e57ddb9ee | ||
|
|
7ae883d921 | ||
|
|
27e97214bd | ||
|
|
6321a9e7e0 | ||
|
|
a9eadca46c | ||
|
|
76bef8a725 | ||
|
|
308b76dc7d | ||
|
|
31d4d29f3e | ||
|
|
016a7e87ea | ||
|
|
4ce88c7bfc | ||
|
|
f570bffcf7 | ||
|
|
3bb9aaf637 | ||
|
|
cf7a64c934 | ||
|
|
c466603193 | ||
|
|
bc53f329a9 | ||
|
|
4a5cc5153f | ||
|
|
8697670fe1 | ||
|
|
64d8543af9 | ||
|
|
482cc242cb | ||
|
|
2051e8183c | ||
|
|
d183903917 | ||
|
|
5c695899a1 | ||
|
|
708e912069 | ||
|
|
db1f4b2b52 | ||
|
|
1508782f49 | ||
|
|
445b830683 | ||
|
|
1a17e88142 | ||
|
|
c0ec2e7856 | ||
|
|
9e962bd0d1 | ||
|
|
7efcff13bb | ||
|
|
e70d0df949 | ||
|
|
fcaa469edd | ||
|
|
51508182ef | ||
|
|
27e78c1626 | ||
|
|
0b4d37a0b6 | ||
|
|
ddbebba6f1 | ||
|
|
7f296348e4 | ||
|
|
ca78e32d97 | ||
|
|
4efc561f4d | ||
|
|
40fed798a1 | ||
|
|
0876afc08c | ||
|
|
ba4c4d06ff | ||
|
|
f64c30af48 | ||
|
|
bb0cea2dd3 | ||
|
|
3c120ce602 | ||
|
|
6151dfb161 | ||
|
|
f05d4ec931 | ||
|
|
e16afc3fc2 | ||
|
|
1b8753fead | ||
|
|
ba960d1ced | ||
|
|
41b60b6152 | ||
|
|
70ddce713c | ||
|
|
e15622748b | ||
|
|
96687021ff | ||
|
|
3c0a7ef510 | ||
|
|
4d84c9fb3f | ||
|
|
aa35a16610 | ||
|
|
a380fcb680 | ||
|
|
e544afe475 | ||
|
|
c089388323 | ||
|
|
e97a043031 | ||
|
|
d1b6dfe577 | ||
|
|
9189fd411e | ||
|
|
432eba80a9 | ||
|
|
db39612c54 | ||
|
|
5794c82a2d | ||
|
|
acf875a886 | ||
|
|
79185bd90f | ||
|
|
d695c30988 | ||
|
|
a1308fbdc9 | ||
|
|
e4f2b761d6 | ||
|
|
6ff6009a70 | ||
|
|
dadeb4006e | ||
|
|
ee0ff762e5 | ||
|
|
1f01ba1d10 | ||
|
|
62965fe1df | ||
|
|
e29eb4cce5 | ||
|
|
699791e318 | ||
|
|
946fbbec3c | ||
|
|
d2aae0103d | ||
|
|
ab06e38b7c | ||
|
|
bd531c85b1 | ||
|
|
8a0ca3c2e5 | ||
|
|
97317ed2c6 | ||
|
|
cda9aae8a3 | ||
|
|
a33e785043 | ||
|
|
833e266a1f | ||
|
|
1bb9b2cb3f | ||
|
|
7ba129b3bf | ||
|
|
1e2c9ca28a | ||
|
|
c6f37fd05f | ||
|
|
b6b569fff9 | ||
|
|
ac57c016a6 | ||
|
|
28fdd9de95 | ||
|
|
1ec946aaa9 | ||
|
|
d6110e3c6f | ||
|
|
4c5302bd4c | ||
|
|
21ee3d5c3e | ||
|
|
e5c036cee8 | ||
|
|
6f82488af7 | ||
|
|
57a7903c63 | ||
|
|
27b89b333a | ||
|
|
ce2c4e7492 | ||
|
|
cad98a2a33 | ||
|
|
40da48e685 | ||
|
|
1c393e5c3e | ||
|
|
667c143c74 | ||
|
|
bc089c18ce | ||
|
|
24b730c0e2 | ||
|
|
9ed9e9b973 | ||
|
|
5b0027aa3d | ||
|
|
810c5e4e8b | ||
|
|
907534958f | ||
|
|
f08a18ab07 | ||
|
|
40549d5ca7 | ||
|
|
3e67edc506 | ||
|
|
9c56a81f65 | ||
|
|
a9a636c9ae | ||
|
|
98374dcd74 | ||
|
|
10ca6eec91 | ||
|
|
216d32cf21 | ||
|
|
248fae4fe6 | ||
|
|
b479b6839d | ||
|
|
1a4e1e9515 | ||
|
|
e5160a4ee8 | ||
|
|
56ea3964a7 | ||
|
|
ad73778286 | ||
|
|
3e548f48c8 | ||
|
|
ae466931fa | ||
|
|
68052b526f | ||
|
|
903b5b88a7 | ||
|
|
007823d6b2 | ||
|
|
db5c4fb349 | ||
|
|
c37901290a | ||
|
|
da67f1f6f8 | ||
|
|
5172fa69e5 | ||
|
|
dd7280cd0d | ||
|
|
0d0f522c88 | ||
|
|
5429e6ed5c | ||
|
|
9db69fa05b | ||
|
|
3b8d52bcb5 | ||
|
|
807f39a336 | ||
|
|
fea2f30461 | ||
|
|
5d97e3f95f | ||
|
|
63e6ca3e99 | ||
|
|
b11590ec3c | ||
|
|
3a43ed6919 | ||
|
|
15ce9c91fb | ||
|
|
0e8d7537d4 | ||
|
|
1fca0787be | ||
|
|
af30285c00 | ||
|
|
a84cab7afd | ||
|
|
593573ddfb | ||
|
|
e9e9f81bdb | ||
|
|
61f624841b | ||
|
|
4bb38a2730 | ||
|
|
d4ec0df00c | ||
|
|
c8a7d855f8 | ||
|
|
73a126dcd5 | ||
|
|
0736533389 | ||
|
|
2695d658ab | ||
|
|
947166e793 | ||
|
|
80c49a7cf5 | ||
|
|
55bfa8f669 | ||
|
|
74ade14f5e | ||
|
|
c9b5218ebc | ||
|
|
b324b2bd65 | ||
|
|
35c038efa2 | ||
|
|
caa404d58d | ||
|
|
830103b9d5 | ||
|
|
6e18379597 | ||
|
|
6effbacb28 | ||
|
|
a20a1a0234 | ||
|
|
3667724061 | ||
|
|
785179e4a7 | ||
|
|
715338f821 | ||
|
|
7d5a82ab6d | ||
|
|
e764cfcc1c | ||
|
|
363acf37bc | ||
|
|
5fd687c1e4 | ||
|
|
dbedcbb93d | ||
|
|
9e9611f11f | ||
|
|
44002838da | ||
|
|
4daab8459f | ||
|
|
5a002f75cc | ||
|
|
a2180a1365 | ||
|
|
7b836acf36 | ||
|
|
a112a561cc | ||
|
|
6dcc2a69f4 | ||
|
|
74e9c55a09 | ||
|
|
d4d6e736a4 | ||
|
|
5cf9938fa2 | ||
|
|
72ad0ad983 | ||
|
|
690750d91e | ||
|
|
9f1dfc4b9c | ||
|
|
05eab5307f | ||
|
|
b0fe70c960 | ||
|
|
09d7992bc4 | ||
|
|
c832a432c1 | ||
|
|
41c22f4314 | ||
|
|
252d45c79b | ||
|
|
ae8a214a3a | ||
|
|
86ce6d7664 | ||
|
|
67e96d6106 | ||
|
|
24d9ec2b75 | ||
|
|
501e47c6ec | ||
|
|
5534a5a5c2 | ||
|
|
7246aabf40 | ||
|
|
604ffae87e | ||
|
|
2d5c16456b | ||
|
|
d44f7633dc | ||
|
|
469ac4035c | ||
|
|
dab218a6d7 | ||
|
|
d6fc675d57 | ||
|
|
04148f7365 | ||
|
|
4c34ac570f | ||
|
|
fbdb8758a3 | ||
|
|
2d08747d70 | ||
|
|
0b4bacbbd1 | ||
|
|
e0412c7edb | ||
|
|
38501cebf1 | ||
|
|
ee45d0217e | ||
|
|
558f362919 | ||
|
|
fb9db2f50c | ||
|
|
c1862e9c68 | ||
|
|
731bde8e28 | ||
|
|
47c87f8694 | ||
|
|
5558cdacc3 | ||
|
|
4650d244a5 | ||
|
|
c65b8aae07 | ||
|
|
7ba2fb9584 | ||
|
|
9a2cfd0c9b | ||
|
|
a7d61b1f2b | ||
|
|
7ab185f7d7 | ||
|
|
724541d691 | ||
|
|
baa6cda9c2 | ||
|
|
9c987e9aad | ||
|
|
134974ea9c | ||
|
|
2d946958e3 | ||
|
|
ffe010e9ce | ||
|
|
1c67252b0c | ||
|
|
60c1be25d4 | ||
|
|
18323a0916 | ||
|
|
9d98abded9 | ||
|
|
b06a742b8c | ||
|
|
87fe44f188 | ||
|
|
96f6ef1693 | ||
|
|
374b7b7261 | ||
|
|
23c4141e55 | ||
|
|
c21200c2a9 | ||
|
|
4a8b94259d | ||
|
|
28a58df27b | ||
|
|
caaf486bbe | ||
|
|
0a972a399e | ||
|
|
245dad1239 | ||
|
|
a6b6a2fbff | ||
|
|
53000a949b | ||
|
|
eae1dd59a7 | ||
|
|
e6347a76a8 | ||
|
|
13e0fb3df7 | ||
|
|
3f1b23ef8a | ||
|
|
acae7f0658 | ||
|
|
a9456b1bd6 | ||
|
|
e4d5a230d2 | ||
|
|
fedf44b9fa | ||
|
|
451db104e8 | ||
|
|
9ac4c7f4e3 | ||
|
|
7cf1c6f016 | ||
|
|
6dd87ee301 | ||
|
|
96837fa8db | ||
|
|
d816e76962 | ||
|
|
310945d9cd | ||
|
|
1aba480495 | ||
|
|
07407749fe | ||
|
|
dfab70dd90 | ||
|
|
3b15469c98 | ||
|
|
e4590800c2 | ||
|
|
c63d4b8ad0 | ||
|
|
1fb913e2ed | ||
|
|
72ace2dab7 | ||
|
|
3bcef18629 | ||
|
|
c91758519c | ||
|
|
9c8f2e7aa0 | ||
|
|
5f377af6d5 | ||
|
|
570b5b1766 | ||
|
|
c4d53d58eb | ||
|
|
ed65033cf1 | ||
|
|
41836bde26 | ||
|
|
8381858742 | ||
|
|
1040bd0d58 | ||
|
|
404e47332e | ||
|
|
3db128785e | ||
|
|
2189ea11df | ||
|
|
49b9bd65b0 | ||
|
|
b3fdb1ff58 | ||
|
|
c79e3fdcf0 | ||
|
|
d7509f7030 | ||
|
|
5c99b55502 | ||
|
|
fd255f022b | ||
|
|
41642589ae | ||
|
|
619fef3bff | ||
|
|
38c0da0cf8 | ||
|
|
b241a58e84 | ||
|
|
35c9585b22 | ||
|
|
d2fe95cc05 | ||
|
|
92e60ba141 | ||
|
|
2356527791 | ||
|
|
cab0f7b522 | ||
|
|
8d3f101a68 | ||
|
|
c8ac2b6281 | ||
|
|
d7d8efd305 | ||
|
|
a4bc04c958 | ||
|
|
5fc208c95a | ||
|
|
02743d5955 | ||
|
|
a7ffebea35 | ||
|
|
c511ef50a2 | ||
|
|
82902e0cde | ||
|
|
1de5ae09bc | ||
|
|
8b735f1cb5 | ||
|
|
33d1334174 | ||
|
|
324b58c21b | ||
|
|
b7b1651709 | ||
|
|
b5a4a6a9ff | ||
|
|
fe2c82b03c | ||
|
|
81e4bca9ef | ||
|
|
c0d055b825 | ||
|
|
188ae38525 | ||
|
|
86a8fe7096 | ||
|
|
0bb2b45f5c | ||
|
|
a17d8731c8 | ||
|
|
9cdd2326c8 | ||
|
|
d75653631d | ||
|
|
e234c8906e | ||
|
|
57baea18b5 | ||
|
|
0f3efe29c4 | ||
|
|
89b87a38b3 | ||
|
|
c52b7ca2e9 | ||
|
|
371b55dc9e | ||
|
|
53e572353c | ||
|
|
3ae71c74e9 | ||
|
|
cb643c04f7 | ||
|
|
faf9ee159b | ||
|
|
ffb3b2d73e | ||
|
|
ffcc327089 | ||
|
|
dfd5a6a86a | ||
|
|
d43f89b067 | ||
|
|
f15c3ab6a2 | ||
|
|
e27809298e | ||
|
|
4b3e28e14b | ||
|
|
71ce9153ed | ||
|
|
25d63966de | ||
|
|
98afbb54b6 | ||
|
|
03a51ca5dd | ||
|
|
428481b2d5 | ||
|
|
f87c60e6db | ||
|
|
6d824e38bc | ||
|
|
f0fd5bfb9e | ||
|
|
33374df671 | ||
|
|
c035b1b19c | ||
|
|
b3b09ed666 | ||
|
|
8029c00a02 | ||
|
|
a2c0921013 | ||
|
|
31cdabc229 | ||
|
|
1059a0aae0 | ||
|
|
e6576e7f40 | ||
|
|
5837144095 | ||
|
|
d30a5c4207 | ||
|
|
7d169b0e5e | ||
|
|
1f54226673 | ||
|
|
901a90ff13 | ||
|
|
56573d1d34 |
@@ -0,0 +1,23 @@
|
||||
---
|
||||
name: Issue template
|
||||
about: " issue template's purpose here."
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
### 系统版本:Ubuntu 20.04
|
||||
### 真寻版本:0.1.5.3
|
||||
|
||||
### 错误截图
|
||||
|
||||
[img]
|
||||
|
||||
### 日志截图
|
||||
|
||||
[img]
|
||||
|
||||
### 错误说明
|
||||
|
||||
发生了xx错误...
|
||||
@@ -0,0 +1,9 @@
|
||||
### 系统版本:Ubuntu 20.04
|
||||
### 真寻版本:0.1.5.3
|
||||
|
||||
### 错误截图
|
||||
|
||||
[img]
|
||||
|
||||
### 错误说明
|
||||
发生了xx错误...
|
||||
+23
-1
@@ -138,10 +138,32 @@ dmypy.json
|
||||
# Cython debug symbols
|
||||
cython_debug/
|
||||
|
||||
demo.py
|
||||
test.py
|
||||
server_ip.py
|
||||
member_activity_handle.py
|
||||
Yu-Gi-Oh/
|
||||
black_word/
|
||||
csgo/
|
||||
fantasy_card/
|
||||
data/
|
||||
log/
|
||||
backup/
|
||||
extensive_plugin/
|
||||
test/
|
||||
bot.py
|
||||
data/
|
||||
.env
|
||||
.env.dev
|
||||
/resources/text/
|
||||
/resources/image/
|
||||
/resources/temp/
|
||||
!/resources/template/
|
||||
/configs/config.py
|
||||
configs/config.yaml
|
||||
./.env
|
||||
./.env.dev
|
||||
plugins/csgo_server/
|
||||
plugins/activity/
|
||||
!/resources/image/genshin/alc/back.png
|
||||
!/data/genshin_alc/
|
||||
.vscode/launch.json
|
||||
@@ -5,37 +5,53 @@
|
||||

|
||||
|
||||
# 绪山真寻Bot
|
||||
|
||||
****
|
||||
此项目基于 Nonebot2 和 go-cqhttp 开发,以 postgresql 作为数据库的QQ群娱乐机器人
|
||||
|
||||
## 关于
|
||||
|
||||
用爱发电,某些功能学习借鉴了大佬们的代码,因为绪山真寻实在太可爱了因此开发了
|
||||
绪山真寻bot,实现了一些对群友的娱乐功能和实用功能(大概)。
|
||||
|
||||
如果该项目的图片等等侵犯猫豆腐老师权益请联系我删除!
|
||||
|
||||
如果希望有个地方讨论绪山真寻Bot,或者有问题或建议,可以发送issues或加入[ <strong>[是真寻酱哒](https://jq.qq.com/?_wv=1027&k=u8PgBkMZ) </strong>]
|
||||
是新手!希望有个地方讨论绪山真寻Bot,或者有问题或建议,可以发送issues或加入[ <strong>[是真寻酱哒(萌新版)](https://jq.qq.com/?_wv=1027&k=u8PgBkMZ) </strong>]
|
||||
|
||||
[//]: # (是老手!讨论插件开发,nonebot2开发,可以加入[ <strong>[真寻酱的技术群](https://jq.qq.com/?_wv=1027&k=u8PgBkMZ) </strong>])
|
||||
|
||||
## 声明
|
||||
|
||||
此项目仅用于学习交流,请勿用于非法用途
|
||||
|
||||
## Gitee
|
||||
# Nonebot2
|
||||
|
||||
# [Gitee](https://gitee.com/two_Dimension/zhenxun_bot)
|
||||
<img style="height: 200px;width: 200px;" src="https://camo.githubusercontent.com/0ef71e86056da694c540790aa4a4e314396884d6c4fdb95362a7538b27a1b034/68747470733a2f2f76322e6e6f6e65626f742e6465762f6c6f676f2e706e67">
|
||||
|
||||
非常 [ **[NICE](https://github.com/nonebot/nonebot2)** ] 的OneBot框架
|
||||
|
||||
## 未完成的文档
|
||||
|
||||
# [传送门](https://hibikier.github.io/zhenxun_bot/)
|
||||
|
||||
## 真寻的帮助
|
||||
|
||||
请对真寻说: '真寻帮助' or '管理员帮助' or '超级用户帮助' or '真寻帮助 指令'
|
||||
|
||||
## 普通帮助图片
|
||||

|
||||
|
||||

|
||||
|
||||
## HTML版帮助图片
|
||||
|
||||

|
||||
|
||||
## Web UI
|
||||
|
||||
[zhenxun_bot_webui](https://github.com/HibiKier/zhenxun_bot_webui)
|
||||
|
||||
## 一键安装脚本
|
||||
|
||||
[zhenxun_bot-deploy](https://github.com/AkashiCoin/zhenxun_bot-deploy)
|
||||
|
||||
## 提供符合真寻标准的插件仓库
|
||||
@@ -44,152 +60,164 @@
|
||||
|
||||
## 来点优点?
|
||||
|
||||
* 实现了许多功能,且提供了大量功能管理命令
|
||||
* 通过Config配置项将所有插件配置统计保存至config.yaml,利于统一用户修改
|
||||
* 方便增删插件,原生nonebot2 matcher,不需要额外修改,仅仅通过简单的配置属性就可以生成`帮助图片`和`帮助信息`
|
||||
* 提供了cd,阻塞,每日次数等限制,仅仅通过简单的属性就可以生成一个限制,例如:`__plugin_cd_limit__`
|
||||
* __..... 更多详细请通过`传送门`查看文档!__
|
||||
|
||||
* 实现了许多功能,且提供了大量功能管理命令
|
||||
* 通过Config配置项将所有插件配置统计保存至config.yaml,利于统一用户修改
|
||||
* 方便增删插件,原生nonebot2 matcher,不需要额外修改,仅仅通过简单的配置属性就可以生成`帮助图片`和`帮助信息`
|
||||
* 提供了cd,阻塞,每日次数等限制,仅仅通过简单的属性就可以生成一个限制,例如:`__plugin_cd_limit__`
|
||||
* __..... 更多详细请通过`传送门`查看文档!__
|
||||
|
||||
## 功能列表
|
||||
|
||||
<details>
|
||||
<summary>已实现的功能</summary>
|
||||
|
||||
### 已实现的常用功能
|
||||
- [x] 昵称系统(群与群与私聊分开.)
|
||||
- [x] 图灵AI(会把'你'等关键字替换为你的昵称),且带有 [AnimeThesaurus](https://github.com/Kyomotoi/AnimeThesaurus),够味
|
||||
- [x] 签到/我的签到/好感度排行/好感度总排行(影响色图概率和开箱次数,支持配置)
|
||||
- [x] 发送某文件夹下的随机图片(支持自定义,默认:美图,萝莉,壁纸)
|
||||
- [x] 色图(这不是基础功能嘛喂)
|
||||
- [x] coser
|
||||
- [x] 黑白草图生成器
|
||||
- [x] 鸡汤/语录
|
||||
- [x] 骂我(钉宫语音)
|
||||
- [x] 戳一戳(概率发送美图,钉宫语音或者戳回去)
|
||||
- [x] 模拟开箱/我的开箱/群开箱统计/我的金色/设置cookie(csgo,内置爬虫脚本,需要提前抓取数据和图片,需要session,可能需要代理,阿里云服务器等ip也许已经被ban了(我无代理访问失败),如果访问太多账号API调用可能被禁止访问api!)
|
||||
- [x] 鲁迅说过
|
||||
- [x] 构造假消息(自定义的分享链接)
|
||||
- [x] 商店/我的金币/购买道具/使用道具
|
||||
- [x] 8种手游抽卡 (查看 [nonebot_plugin_gamedraw](https://github.com/HibiKier/nonebot_plugin_gamedraw))
|
||||
- [x] 我有一个朋友想问问..(借鉴pcrbot插件)
|
||||
- [x] 原神黄历
|
||||
- [x] 原神今日素材
|
||||
- [x] 原神资源查询 (借鉴[Genshin_Impact_bot](https://github.com/H-K-Y/Genshin_Impact_bot)插件)
|
||||
- [x] 原神便笺查询
|
||||
- [x] 原神玩家查询
|
||||
- [x] 原神树脂提醒
|
||||
- [x] 原神签到/自动签到
|
||||
- [x] 金币红包
|
||||
- [x] 微博热搜
|
||||
- [x] B站主播/UP/番剧订阅
|
||||
|
||||
- [x] pil对图片的一些操作
|
||||
- [x] BUFF饰品底价查询(需要session)
|
||||
- [x] 天气查询
|
||||
- [x] 疫情查询
|
||||
- [x] bt磁力搜索(咳咳,这功能我想dddd)
|
||||
- [x] reimu搜索(上车) (使用[XUN_Langskip](https://github.com/Angel-Hair/XUN_Bot)的插件)
|
||||
- [x] 靠图识番 (使用[XUN_Langskip](https://github.com/Angel-Hair/XUN_Bot)的插件)
|
||||
- [x] 以图搜图 (使用[nonebot_plugin_picsearcher](https://github.com/synodriver/nonebot_plugin_picsearcher)插件)
|
||||
- [x] 搜番
|
||||
- [x] 点歌 [nonebot_plugin_songpicker2](https://github.com/maxesisn/nonebot_plugin_songpicker2)插件(删除了选歌和评论)
|
||||
- [x] epic免费游戏
|
||||
- [x] p站排行榜
|
||||
- [x] p站搜图
|
||||
- [x] 翻译(日英韩)
|
||||
- [x] pix图库(一个自己的图库,含有增删查改,黑名单等命令)
|
||||
* [x] 昵称系统(群与群与私聊分开.)
|
||||
|
||||
- [x] 查看当前群欢迎消息
|
||||
- [x] 查看该群自己的权限
|
||||
- [x] 我的信息(只是为了看看什么时候入群)
|
||||
- [x] 更新信息(如果继续更新的话)
|
||||
- [x] go-cqhttp最新版下载和上传(不需要请删除)
|
||||
- [x] 撤回
|
||||
- [x] 滴滴滴-(用户对超级用户发送消息)
|
||||
- [x] 金币红包/金币排行
|
||||
- [x] 俄罗斯轮盘/胜场排行/败场排行/欧洲人排行/慈善家排行
|
||||
- [x] 网易云热评
|
||||
- [x] 念首古诗
|
||||
- [x] 获取b站视频封面
|
||||
- [x] 通过PID获取图片
|
||||
- [x] 功能统计可视化
|
||||
* [x] 图灵AI(会把'你'等关键字替换为你的昵称),且带有 [AnimeThesaurus](https://github.com/Kyomotoi/AnimeThesaurus),够味
|
||||
* [x] 签到/我的签到/好感度排行/好感度总排行(影响色图概率和开箱次数,支持配置)
|
||||
* [x] 发送某文件夹下的随机图片(支持自定义,默认:美图,萝莉,壁纸)
|
||||
* [x] 色图(这不是基础功能嘛喂)
|
||||
* [x] coser
|
||||
* [x] 黑白草图生成器
|
||||
* [x] 鸡汤/语录
|
||||
* [x] 骂我(钉宫语音)
|
||||
* [x] 戳一戳(概率发送美图,钉宫语音或者戳回去)
|
||||
* [x] 模拟开箱/我的开箱/群开箱统计/我的金色/设置cookie(csgo,内置爬虫脚本,需要提前抓取数据和图片,需要session,可能需要代理,阿里云服务器等ip也许已经被ban了(我无代理访问失败),如果访问太多账号API调用可能被禁止访问api!)
|
||||
* [x] 鲁迅说过
|
||||
* [x] 构造假消息(自定义的分享链接)
|
||||
* [x] 商店/我的金币/购买道具/使用道具
|
||||
* [x] 8种手游抽卡 (查看 [nonebot_plugin_gamedraw](https://github.com/HibiKier/nonebot_plugin_gamedraw))
|
||||
* [x] 我有一个朋友想问问..(借鉴pcrbot插件)
|
||||
* [x] 原神黄历
|
||||
* [x] 原神今日素材
|
||||
* [x] 原神资源查询 (借鉴[Genshin_Impact_bot](https://github.com/H-K-Y/Genshin_Impact_bot)插件)
|
||||
* [x] 原神便笺查询
|
||||
* [x] 原神玩家查询
|
||||
* [x] 原神树脂提醒
|
||||
* [x] 原神签到/自动签到
|
||||
* [x] 金币红包
|
||||
* [x] 微博热搜
|
||||
* [x] B站主播/UP/番剧订阅
|
||||
|
||||
- [x] 关于
|
||||
* [x] pil对图片的一些操作
|
||||
* [x] BUFF饰品底价查询(需要session)
|
||||
* [x] 天气查询
|
||||
* [x] 疫情查询
|
||||
* [x] bt磁力搜索(咳咳,这功能我想dddd)
|
||||
* [x] reimu搜索(上车) (使用[XUN_Langskip](https://github.com/Angel-Hair/XUN_Bot)的插件)
|
||||
* [x] 靠图识番 (使用[XUN_Langskip](https://github.com/Angel-Hair/XUN_Bot)的插件)
|
||||
* [x] 以图搜图 (使用[nonebot_plugin_picsearcher](https://github.com/synodriver/nonebot_plugin_picsearcher)插件)
|
||||
* [x] 搜番
|
||||
* [x] 点歌 [nonebot_plugin_songpicker2](https://github.com/maxesisn/nonebot_plugin_songpicker2)插件(删除了选歌和评论)
|
||||
* [x] epic免费游戏
|
||||
* [x] p站排行榜
|
||||
* [x] p站搜图
|
||||
* [x] 翻译(日英韩)
|
||||
* [x] pix图库(一个自己的图库,含有增删查改,黑名单等命令)
|
||||
|
||||
* [x] 查看当前群欢迎消息
|
||||
* [x] 查看该群自己的权限
|
||||
* [x] 我的信息(只是为了看看什么时候入群)
|
||||
* [x] 更新信息(如果继续更新的话)
|
||||
* [x] go-cqhttp最新版下载和上传(不需要请删除)
|
||||
* [x] 撤回
|
||||
* [x] 滴滴滴-(用户对超级用户发送消息)
|
||||
* [x] 金币红包/金币排行
|
||||
* [x] 俄罗斯轮盘/胜场排行/败场排行/欧洲人排行/慈善家排行
|
||||
* [x] 网易云热评
|
||||
* [x] 念首古诗
|
||||
* [x] 获取b站视频封面
|
||||
* [x] 通过PID获取图片
|
||||
* [x] 功能统计可视化
|
||||
* [x] 词云
|
||||
* [x] 关于
|
||||
|
||||
### 已实现的管理员功能
|
||||
- [x] 更新群组成员信息
|
||||
- [x] 95%的群功能开关
|
||||
- [x] 查看群内被动技能状态
|
||||
- [x] 自定义群欢迎消息(是真寻的不是管家的!)
|
||||
- [x] .ban/.unban(支持设置ban时长)= 黑白名单
|
||||
- [x] 刷屏禁言相关:刷屏检测设置/设置禁言时长/设置检测次数
|
||||
- [x] 上传图片/连续上传图片 (上传图片至指定图库)
|
||||
- [x] 移动图片 (同上)
|
||||
- [x] 删除图片 (同上)
|
||||
- [x] 群内B站订阅
|
||||
- [x] 群词条
|
||||
- [x] 休息吧/醒来
|
||||
|
||||
* [x] 更新群组成员信息
|
||||
|
||||
* [x] 95%的群功能开关
|
||||
* [x] 查看群内被动技能状态
|
||||
* [x] 自定义群欢迎消息(是真寻的不是管家的!)
|
||||
* [x] .ban/.unban(支持设置ban时长)= 黑白名单
|
||||
* [x] 刷屏禁言相关:刷屏检测设置/设置禁言时长/设置检测次数
|
||||
* [x] 上传图片/连续上传图片 (上传图片至指定图库)
|
||||
* [x] 移动图片 (同上)
|
||||
* [x] 删除图片 (同上)
|
||||
* [x] 群内B站订阅
|
||||
* [x] 词条设置
|
||||
* [x] 休息吧/醒来
|
||||
|
||||
### 已实现的超级用户功能
|
||||
- [x] 添加/删除权限(是真寻的管理员权限,不是群管理员)
|
||||
- [x] 开启/关闭指定群的广播通知
|
||||
- [x] 广播
|
||||
- [x] 自检(检查系统状态)
|
||||
- [x] 所有群组/所有好友
|
||||
- [x] 退出指定群
|
||||
- [x] 更新好友信息/更新群信息
|
||||
- [x] /t(对用户进行回复或发送消息)
|
||||
- [x] 上传/删除/修改商品(需要编写对应的商品功能)
|
||||
- [x] 节日红包发送
|
||||
- [x] 修改群权限
|
||||
- [x] ban
|
||||
- [x] 更新色图
|
||||
- [x] 更新价格/更加图片(csgo开箱)
|
||||
- [x] 重载原神/方舟/赛马娘/坎公骑冠剑卡池
|
||||
- [x] 更新原神今日素材/更新原神资源信息
|
||||
- [x] PIX相关操作
|
||||
- [x] 检查更新真寻
|
||||
- [x] 重启
|
||||
- [x] 添加/删除/查看群白名单
|
||||
- [x] 功能开关(更多设置)
|
||||
- [x] 功能状态
|
||||
- [x] b了
|
||||
- [x] 执行sql
|
||||
- [x] 重载配置
|
||||
- [x] 清理临时数据
|
||||
- [x] 增删群认证
|
||||
- [x] 同意/拒绝好友/群聊请求
|
||||
- [x] 配置重载
|
||||
|
||||
* [x] 添加/删除权限(是真寻的管理员权限,不是群管理员)
|
||||
|
||||
* [x] 开启/关闭指定群的广播通知
|
||||
* [x] 广播
|
||||
* [x] 自检(检查系统状态)
|
||||
* [x] 所有群组/所有好友
|
||||
* [x] 退出指定群
|
||||
* [x] 更新好友信息/更新群信息
|
||||
* [x] /t(对用户进行回复或发送消息)
|
||||
* [x] 上传/删除/修改商品(需要编写对应的商品功能)
|
||||
* [x] 节日红包发送
|
||||
* [x] 修改群权限
|
||||
* [x] ban
|
||||
* [x] 更新色图
|
||||
* [x] 更新价格/更加图片(csgo开箱)
|
||||
* [x] 重载原神/方舟/赛马娘/坎公骑冠剑卡池
|
||||
* [x] 更新原神今日素材/更新原神资源信息
|
||||
* [x] PIX相关操作
|
||||
* [x] 检查更新真寻
|
||||
* [x] 重启
|
||||
* [x] 添加/删除/查看群白名单
|
||||
* [x] 功能开关(更多设置)
|
||||
* [x] 功能状态
|
||||
* [x] b了
|
||||
* [x] 执行sql
|
||||
* [x] 重载配置
|
||||
* [x] 清理临时数据
|
||||
* [x] 增删群认证
|
||||
* [x] 同意/拒绝好友/群聊请求
|
||||
* [x] 配置重载
|
||||
|
||||
#### 超级用户的被动技能
|
||||
- [x] 邀请入群提醒(别人邀请真寻入群)
|
||||
- [x] 添加好友提醒(别人添加真寻好友)
|
||||
|
||||
* [x] 邀请入群提醒(别人邀请真寻入群)
|
||||
|
||||
* [x] 添加好友提醒(别人添加真寻好友)
|
||||
|
||||
### 已实现的被动技能
|
||||
- [x] 进群欢迎消息
|
||||
- [x] 群早晚安
|
||||
- [x] 每日开箱重置提醒
|
||||
- [x] b站转发解析(解析b站分享信息,支持bv,bilibili链接,b站手机端转发卡片,cv,b23.tv),且5分钟内不解析相同url
|
||||
- [x] 丢人爬(爬表情包)
|
||||
- [x] epic通知(每日发送epic免费游戏链接)
|
||||
- [x] 原神黄历提醒
|
||||
- [x] 复读
|
||||
|
||||
### 已实现的看不见的技能!
|
||||
- [x] 刷屏禁言检测
|
||||
- [x] 功能调用统计
|
||||
- [x] 检测恶意触发命令(将被最高权限ban掉30分钟,只有最高权限(9级)可以进行unban)
|
||||
- [x] 自动同意好友请求,加群请求将会提醒管理员,退群提示,加群欢迎等等
|
||||
- [x] 群聊时间检测(当群聊最后一人发言时间大于当前36小时后将关闭该群所有通知(即被动技能))
|
||||
- [x] 群管理员监控,自动为新晋管理员增加权限,为失去群管理员的用户删除权限
|
||||
- [x] 群权限系统
|
||||
- [x] 定时更新权限
|
||||
- [x] 自动配置重载
|
||||
* [x] 进群欢迎消息
|
||||
|
||||
* [x] 群早晚安
|
||||
* [x] 每日开箱重置提醒
|
||||
* [x] b站转发解析(解析b站分享信息,支持bv,bilibili链接,b站手机端转发卡片,cv,b23.tv),且5分钟内不解析相同url
|
||||
* [x] 丢人爬(爬表情包)
|
||||
* [x] epic通知(每日发送epic免费游戏链接)
|
||||
* [x] 原神黄历提醒
|
||||
* [x] 复读
|
||||
|
||||
### 已实现的看不见的技能
|
||||
|
||||
* [x] 刷屏禁言检测
|
||||
|
||||
* [x] 功能调用统计
|
||||
* [x] 检测恶意触发命令(将被最高权限ban掉30分钟,只有最高权限(9级)可以进行unban)
|
||||
* [x] 自动同意好友请求,加群请求将会提醒管理员,退群提示,加群欢迎等等
|
||||
* [x] 群聊时间检测(当群聊最后一人发言时间大于当前36小时后将关闭该群所有通知(即被动技能))
|
||||
* [x] 群管理员监控,自动为新晋管理员增加权限,为失去群管理员的用户删除权限
|
||||
* [x] 群权限系统
|
||||
* [x] 定时更新权限
|
||||
* [x] 自动配置重载
|
||||
|
||||
</details>
|
||||
|
||||
## 详细配置请前往文档,以下为最简部署和配置,如果你有基础并学习过nonebot2的话
|
||||
|
||||
|
||||
## 简单部署
|
||||
|
||||
```
|
||||
@@ -232,239 +260,418 @@ python bot.py
|
||||
|
||||
```
|
||||
|
||||
|
||||
## 使用Docker
|
||||
__Docker 最新版本由 [Sakuracio](https://github.com/Sakuracio) 提供__
|
||||
#### GitHub:[Sakuracio/zxenv](https://github.com/Sakuracio/zxenv)
|
||||
#### DockerHub:[hibikier/zhenxun_bot](https://hub.docker.com/r/hibikier/zhenxun_bot)
|
||||
|
||||
**Docker 单机版(仅真寻Bot)**
|
||||
**点击下方的 GitHub 徽标查看教程**
|
||||
[](https://github.com/Sakuracio/zhenxun_bot_docker)
|
||||
[](https://hub.docker.com/r/hibikier/zhenxun_bot)
|
||||
**Docker 全量版(包含 真寻Bot PostgreSQL数据库 go-cqhttp webui等)**
|
||||
[](https://github.com/SinKy-Yan/zhenxunbot-docker)
|
||||
[](https://hub.docker.com/r/jyishit/zhenxun_bot)
|
||||
**点击上方的 GitHub 徽标查看教程**
|
||||
PS: **ARM平台** 请使用全量版 同时 **如果你的机器 RAM < 1G 可能无法正常启动全量版容器**
|
||||
|
||||
## [爱发电](https://afdian.net/@HibiKier)
|
||||
|
||||
<details>
|
||||
<summary>爱发电 以及 感谢投喂 </summary>
|
||||
<img width="365px" height="450px" src="https://user-images.githubusercontent.com/45528451/175059389-cfeb8174-fa07-4939-80ab-a039087a50f6.png">
|
||||
|
||||
### 感谢名单
|
||||
|
||||
(可以告诉我你的 **github** 地址,我偷偷换掉0v|)
|
||||
|
||||
[shenqi](https://afdian.net/u/fa923a8cfe3d11eba61752540025c377)
|
||||
[A_Kyuu](https://afdian.net/u/b83954fc2c1211eba9eb52540025c377)
|
||||
[疯狂混沌](https://afdian.net/u/789a2f9200cd11edb38352540025c377)
|
||||
[投冥](https://afdian.net/a/144514mm)
|
||||
[茶喵](https://afdian.net/u/fd22382eac4d11ecbfc652540025c377)
|
||||
[AemokpaTNR](https://afdian.net/u/1169bb8c8a9611edb0c152540025c377)
|
||||
[爱发电用户_wrxn](https://afdian.net/u/4aa03d20db4311ecb1e752540025c377)
|
||||
[qqw](https://afdian.net/u/b71db4e2cc3e11ebb76652540025c377)
|
||||
[溫一壺月光下酒](https://afdian.net/u/ad667a5c650c11ed89bf52540025c377)
|
||||
[伝木](https://afdian.net/u/246b80683f9511edba7552540025c377)
|
||||
[阿奎](https://afdian.net/u/da41f72845d511ed930d52540025c377)
|
||||
[醉梦尘逸](https://afdian.net/u/bc11d2683cd011ed99b552540025c377)
|
||||
[Abc](https://afdian.net/u/870dc10a3cd311ed828852540025c377)
|
||||
[本喵无敌哒](https://afdian.net/u/dffaa9005bc911ebb69b52540025c377)
|
||||
[椎名冬羽](https://afdian.net/u/ca1ebd64395e11ed81b452540025c377)
|
||||
[kaito](https://afdian.net/u/a055e20a498811eab1f052540025c377)
|
||||
[笑柒XIAO_Q7](https://afdian.net/u/4696db5c529111ec84ea52540025c377)
|
||||
[请问一份爱多少钱](https://afdian.net/u/f57ef6602dbd11ed977f52540025c377)
|
||||
[咸鱼鱼鱼鱼](https://afdian.net/u/8e39b9a400e011ed9f4a52540025c377)
|
||||
[Kafka](https://afdian.net/u/41d66798ef6911ecbc5952540025c377)
|
||||
[墨然](https://afdian.net/u/8aa5874a644d11eb8a6752540025c377)
|
||||
[爱发电用户_T9e4](https://afdian.net/u/2ad1bb82f3a711eca22852540025c377)
|
||||
[笑柒XIAO_Q7](https://afdian.net/u/4696db5c529111ec84ea52540025c377)
|
||||
[noahzark](https://afdian.net/a/noahzark)
|
||||
[腊条](https://afdian.net/u/f739c4d69eca11eba94b52540025c377)
|
||||
[ze roller](https://afdian.net/u/0e599e96257211ed805152540025c377)
|
||||
[爱发电用户_4jrf](https://afdian.net/u/6b2cdcc817c611ed949152540025c377)
|
||||
[爱发电用户_TBsd](https://afdian.net/u/db638b60217911ed9efd52540025c377)
|
||||
[烟寒若雨](https://afdian.net/u/067bd2161eec11eda62b52540025c377)
|
||||
[ln](https://afdian.net/u/b51914ba1c6611ed8a4e52540025c377)
|
||||
[爱发电用户_b9S4](https://afdian.net/u/3d8f30581a2911edba6d52540025c377)
|
||||
[爱发电用户_c58s](https://afdian.net/u/a6ad8dda195e11ed9a4152540025c377)
|
||||
[爱发电用户_eNr9](https://afdian.net/u/05fdb41c0c9a11ed814952540025c377)
|
||||
[MangataAkihi](https://github.com/Sakuracio)
|
||||
[炀](https://afdian.net/u/69b76e9ec77b11ec874f52540025c377)
|
||||
[爱发电用户_Bc6j](https://afdian.net/u/8546be24f44111eca64052540025c377)
|
||||
[大魔王](https://github.com/xipesoy)
|
||||
[CopilotLaLaLa](https://github.com/CopilotLaLaLa)
|
||||
[嘿小欧](https://afdian.net/u/daa4bec4f24911ec82e552540025c377)
|
||||
[回忆的秋千](https://afdian.net/u/e315d9c6f14f11ecbeef52540025c377)
|
||||
[十年くん](https://github.com/shinianj)
|
||||
[哇](https://afdian.net/u/9b266244f23911eca19052540025c377)
|
||||
[yajiwa](https://github.com/yajiwa)
|
||||
[爆金币](https://afdian.net/u/0d78879ef23711ecb22452540025c377)
|
||||
|
||||
</details>
|
||||
|
||||
## 更新
|
||||
|
||||
### 2022/4/26
|
||||
### 2023/3/12 \[v0.1.6.7]
|
||||
|
||||
* 修复了群白名单无法正确添加
|
||||
* 优化了管理员帮助图片,背景图层将位于最下层
|
||||
* 修复了树脂140时不断提醒(未测试
|
||||
* 新增了消息记录的消息排行
|
||||
* WebUI新增CPU,内存,磁盘监控
|
||||
* WebUI新增资源文件夹统计可视化
|
||||
* 新增`更新武器箱ALL`命令来更新所有武器箱
|
||||
* 新增`查看武器箱`命令
|
||||
* 色图bug修复、增加指令 [@pull/1301](https://github.com/HibiKier/zhenxun_bot/pull/1301)
|
||||
|
||||
### 2022/4/12
|
||||
### 2023/3/9
|
||||
|
||||
* 修复b了命令私聊出错
|
||||
* 更正sql语句 [@pull/1302](https://github.com/HibiKier/zhenxun_bot/pull/1302)
|
||||
* 修改签到卡片中签到增加好感度显示错误 [@pull/1299](https://github.com/HibiKier/zhenxun_bot/pull/1299)
|
||||
|
||||
### 2022/4/10 \[v0.1.4.7]
|
||||
### 2023/3/5
|
||||
|
||||
* 新增消息记录模块
|
||||
* 丰富处理请求操作提示
|
||||
* web ui新增配置项修改
|
||||
* 更新开箱会记录箱子数据以及开箱时箱子价格加入花费
|
||||
* 修复开箱BUG
|
||||
|
||||
### 2022/4/9
|
||||
### 2023/3/4
|
||||
|
||||
* fix: 更新问题,戳一戳图片路径问题 [@pull/144](https://github.com/HibiKier/zhenxun_bot/pull/144)
|
||||
* 重写翻译,使用百度翻译API
|
||||
* 新增开箱日志以及自动更新武器箱
|
||||
|
||||
### 2022/4/8
|
||||
### 2023/3/2
|
||||
|
||||
* 修复原神玩家查询
|
||||
* 修复config.yaml中把False也当成None的问题 [@pull/1288](https://github.com/HibiKier/zhenxun_bot/pull/1288)
|
||||
* 删除道具表无用字段(props) [@pull/1287](https://github.com/HibiKier/zhenxun_bot/pull/1287)
|
||||
* 修复词云
|
||||
* 修复我的签到签到图片
|
||||
* 更正BuffSkin添加语句
|
||||
* 修复词条单图片/表情/at无法添加
|
||||
|
||||
### 2022/4/6
|
||||
### 2022/3/1
|
||||
|
||||
* update search_type [@pull/143](https://github.com/HibiKier/zhenxun_bot/pull/143)
|
||||
* 重写开箱更新箱子,允许更新目前所有箱子的皮肤
|
||||
* 修复消息统计
|
||||
|
||||
### 2022/4/5 \[v0.1.4.6]
|
||||
### 2023/2/28
|
||||
|
||||
* 修复web修改插件后帮助图片生成错误
|
||||
* 把Config的type字段默认类型由str改为None [@pull/1283](https://github.com/HibiKier/zhenxun_bot/pull/1283)
|
||||
* 修复同意群聊请求以及添加群认证 更新变成查询的问题 [@pull/1282](https://github.com/HibiKier/zhenxun_bot/pull/1282)
|
||||
|
||||
### 2022/4/4 \[v0.1.4.5]
|
||||
### 2023/2/26
|
||||
|
||||
* 替换了bt搜索URL
|
||||
* 优化使用playwright的相关代码
|
||||
* 原神玩家查询新增层岩巨渊探索
|
||||
* 修复原神便笺角色头像黑框
|
||||
* 修复同意群聊请求错误
|
||||
* 提供webui方面的api
|
||||
* 新增web-ui(前端简易管理页面插件)插件
|
||||
* Config提供`type`字段确定配置项类型
|
||||
* 重写开箱功能
|
||||
|
||||
### 2022/3/21
|
||||
### 2023/2/25
|
||||
|
||||
* 修复statistics_handle.py乱码
|
||||
* 修复ys查询,尘歌壶背景尺寸与内容不匹配的问题 [@pull/1270](https://github.com/HibiKier/zhenxun_bot/pull/1275)
|
||||
* 更换cos url [@pull/1270](https://github.com/HibiKier/zhenxun_bot/pull/1274)
|
||||
|
||||
### 2022/3/18 \[v0.1.4.4]
|
||||
### 2023/2/20
|
||||
|
||||
* 修复戳一戳无法功能关闭与ban禁用
|
||||
* 新增图片搜索 search_image
|
||||
* chat_history部分字段调整为可null [@pull/1270](https://github.com/HibiKier/zhenxun_bot/pull/1270)
|
||||
|
||||
### 2022/3/7
|
||||
### 2023/2/19
|
||||
|
||||
* 优化增删权限插件
|
||||
* 修正了`重载插件`的帮助提示
|
||||
* 修改BUG
|
||||
|
||||
### 2022/3/6
|
||||
### 2023/2/18
|
||||
|
||||
* 修复树脂提醒无法开启
|
||||
* 修复p搜图片路径错误
|
||||
* 数据库舍弃`gino`使用`tortoise`
|
||||
* 昵称提供命令`全局昵称设置`
|
||||
* `manager_group`群管理操作中`退群`,`修改群权限`,`添加/删除群白名单`,`添加/删除群认证`在群聊中使用命令时且未指定群聊时,默认指定当前群聊
|
||||
* 修复插件帮助命令不生效的问题 [@pull/1263](https://github.com/HibiKier/zhenxun_bot/pull/1263)
|
||||
* 解决开红包经常误触的问题,有红包和未领取的时候才会触发“开”命令 [@pull/1257](https://github.com/HibiKier/zhenxun_bot/pull/1257)
|
||||
* 细节优化,原神今日素材重写 [@pull/1258](https://github.com/HibiKier/zhenxun_bot/pull/1258)
|
||||
|
||||
### 2022/3/3 \[v0.1.4.3]
|
||||
### 2023/1/31
|
||||
|
||||
* 修复手动同意群聊请求依旧退出
|
||||
* 修复B站转发卡片BUG [@pull/1249](https://github.com/HibiKier/zhenxun_bot/pull/1249)
|
||||
|
||||
### 2022/3/1 \[v0.1.4.2]
|
||||
### 2023/1/27
|
||||
|
||||
* 0.1.4内容
|
||||
* 替换pixiv反向代理地址 [@pull/1244](https://github.com/HibiKier/zhenxun_bot/pull/1244)
|
||||
|
||||
### 2022/2/27 \[v0.1.4.1]
|
||||
### 2022/12/31
|
||||
|
||||
* 优化抽卡
|
||||
* 修复epic报错,优化简介 [@pull/1226](https://github.com/HibiKier/zhenxun_bot/pull/1226)
|
||||
* 修复词条在某些回答下出错
|
||||
* 原神黄历改为PIL
|
||||
* 允许真寻自身触发命令,提供配置项 `self_message:STATUS`
|
||||
|
||||
### 2022/2/25 \[v0.1.4]
|
||||
### 2022/12/27 \[v0.1.6.6]
|
||||
|
||||
* PIX提供配置MAX_ONCE_NUM2FORWARD:当单次发送图片超过指定张数且在群聊时,将转为合并消息
|
||||
* 修复点歌无法正确发送
|
||||
* 修复我有一个朋友有时文本会包含CQ码
|
||||
* 修复群欢消息被动控制文本未删除 [@pull/124](https://github.com/HibiKier/zhenxun_bot/pull/124)
|
||||
* message_builder.image不再提供参数:abspath
|
||||
* 添加权限检查依赖注入
|
||||
|
||||
### 2022/2/23
|
||||
### 2022/12/26
|
||||
|
||||
* 插件状态将从已加载插件模块中读取
|
||||
* 修复b站订阅插件订阅失败问题
|
||||
* 修复重启命令无法使用
|
||||
* 优化`gamedraw`插件
|
||||
* 提供全局被动控制
|
||||
* 群被动状态改为图片
|
||||
* 修复epic获取到的简介不是中文的bug [@pull/1221](https://github.com/HibiKier/zhenxun_bot/pull/1221)
|
||||
|
||||
### 2022/2/21 \[v0.1.3.2]
|
||||
## 2022/12/24
|
||||
|
||||
* 群权限为-1时,超级用户发送的命令依旧生效
|
||||
* 当群权限为-1时,被动技能也将不会发送
|
||||
* 修复功能开关,b站转发解析,复读 ignore无法使用
|
||||
* 修复色图下载文件名与路径错误
|
||||
* 修复被动技能提醒有时无法删除控制文本
|
||||
* 修复群管理员权限检测会阻挡超级用户权限
|
||||
|
||||
### 2022/12/23
|
||||
|
||||
### 2022/2/20 \[v0.1.3.1]
|
||||
* 优化`管理员帮助`,`超级用户帮助`图片
|
||||
* 重新移植`gamedraw`
|
||||
* 修复pil帮助私聊时无法生成
|
||||
|
||||
* 修复pix下载临时文件目录错误
|
||||
* 修复AI,天气,发送图片ignore导致无法使用
|
||||
* 修复纯文本被动技能提醒有时无法删除控制文本
|
||||
### 2022/12/17
|
||||
|
||||
### 2022/2/19 \[v0.1.3] (nonebot beta2!)
|
||||
* 修复查看插件仓库当已安装插件版本不一致时出错
|
||||
|
||||
* 由于nonebot升级版本,提供更新建议(__该次升级将会导致nonebot.beta1以下的插件无法使用__
|
||||
* 保证services,utils,configs,plugins,basic_plugins,文件夹均为最新
|
||||
* 根目录有pyproject.toml与poetry.lock
|
||||
* 执行命令:
|
||||
* pip3 install poetry
|
||||
* poetry install
|
||||
* poetry shell
|
||||
* playwright install chromium
|
||||
* python3 bot.py
|
||||
### 2022/12/15
|
||||
|
||||
* 修复自定义群欢迎消息无法使用
|
||||
|
||||
* 适配nonebot.beta2
|
||||
* 删除图片搜索 nonebot_plugin_picsearcher
|
||||
* 替换cos api
|
||||
* 原神签到树脂提醒新增绑定群里,在某群绑定uid就会在某群发送提醒信息(有好友则私聊,需要重新绑定uid
|
||||
* 修改update_info.json
|
||||
* 修复原神资源查询下载数据失败时导致报错
|
||||
* 优化BuildImage.circle()锯齿问题 [@pull/109](https://github.com/HibiKier/zhenxun_bot/pull/109)
|
||||
* epic restful 替换 [@pull/119](https://github.com/HibiKier/zhenxun_bot/pull/119)
|
||||
* fix: 修复远古时期残留的epic推送问题 [@pull/122](https://github.com/HibiKier/zhenxun_bot/pull/122)
|
||||
### 2022/12/13
|
||||
|
||||
### 2022/2/11
|
||||
* 修复.unban
|
||||
|
||||
* 修复pix不使用反代无法下载图片
|
||||
### 2022/12/12
|
||||
|
||||
### 2022/2/10 \[v0.1.1]
|
||||
* 修改HTML帮助禁用提示文本错误
|
||||
* 修复HTML帮助私聊无法生成
|
||||
|
||||
* 修复购买道具出错
|
||||
### 2022/12/11
|
||||
|
||||
### 2022/2/9 \[v0.1]
|
||||
* 词条问题支持真寻的昵称开头与at真寻开头并优化回复
|
||||
* 帮助新增HTML生成(新布局),添加配置`TYPE`切换
|
||||
* 更正私聊时功能管理回复错误
|
||||
* 修复加入新群聊时初始化功能开关错误
|
||||
* 添加单例注解
|
||||
* 添加统计表
|
||||
|
||||
* 新增原神自动签到和手动签到
|
||||
* 新增原神树脂提醒
|
||||
* 新增手动重载Config.yaml命令以及重载配置定时任务(极少部分帮助或配置可能需要重启
|
||||
* 修改了发送本地图库的matcher,改为on_message
|
||||
* register_use可以通过返回值发送消息
|
||||
* 修复修改商品时限制时间出错
|
||||
* 修复超时商品依旧可以被购买
|
||||
### 2022/12/10
|
||||
|
||||
### 2022/1/16 \[v0.0.9.0]
|
||||
* 重写帮助,删除 `详细帮助` 命令
|
||||
|
||||
* Ai提供文本敏感词过滤器
|
||||
* 疫情插件适配新版腾讯API
|
||||
* 修复/t回复带空格切分
|
||||
* 修复原神玩家查询缺少渊下宫和稻妻家园以及角色不完全
|
||||
* 修复方法 text2image 中 padding 和 font 无法对纯文本生效
|
||||
* 修复签到图片中信息并未使用配置文件中的色图概率
|
||||
* 修改原神大地图合成方式,改为先压缩再合成
|
||||
* bag_user弃用字段props(该字段会在下次更新删除),使用新字段property
|
||||
* 数据库中所有belonging_group统一修改为group_id
|
||||
* 商店将registered_use和register_goods更名为register_use何register_goods
|
||||
* 商品注册提供了kwargs参数提供:
|
||||
* bot
|
||||
* event
|
||||
* 特殊字段
|
||||
* “send_success_msg”(发送成功的交互信息->即:使用道具 {name} {num} 次成功)
|
||||
* “_max_num_limit”(该道具单次使用的最多个数,默认1)
|
||||
### 2022/12/4
|
||||
|
||||
### 2022/1/5 \[v0.0.8.2]
|
||||
* 优化管理代码
|
||||
|
||||
* 提供金币消费hook,可在plugins2settings.yaml中配置该功能需要消费的金币
|
||||
* 商店插件将作为内置插件移动至basic_plugins
|
||||
* 商店插件通过export提供了方法,不需要修改商店插件代码添加商品数据和生效方法
|
||||
* 修改了hook插件顺序,主要以auth_hook为主
|
||||
* 修改商店图片样式
|
||||
* 取消每次启动更新城市列表(首次除外),采用定时更新,加快bot启动速度
|
||||
* 取消每次启动时截取今日素材,采用调用时截取保存,加快bot启动速度
|
||||
* 更新色图时当图片404时会删除并替换
|
||||
* 疫情消息回复改为图片
|
||||
* 修复商店折扣和限时时间无法生效
|
||||
* 修复原神玩家查询尘歌壶缺少图片
|
||||
### 2022/11/28
|
||||
|
||||
### 2021/12/26
|
||||
* 修复web_ui群组无法获取
|
||||
* 修复web_ui修改插件数据时cmd格式错误
|
||||
|
||||
* 修复群词条问题 空格 会被录入导致不断回复
|
||||
* 修复米游社app替换api导致无法正常查询
|
||||
### 2022/11/28
|
||||
|
||||
### 2021/12/24
|
||||
* :bug: Fix a bug in open_cases to get vanilla knives' prices [@pull/1188](https://github.com/HibiKier/zhenxun_bot/pull/1188)
|
||||
|
||||
* 支持国际疫情数据查询 [@pull/99](https://github.com/HibiKier/zhenxun_bot/pull/99)
|
||||
### 2022/11/24
|
||||
|
||||
### 2021/12/20
|
||||
* 修复管理员插件加载路径错误
|
||||
|
||||
* 只有发布小于存储时间的新动态/视频的时候才获取并推送 [@pull/96](https://github.com/HibiKier/zhenxun_bot/pull/96)
|
||||
### 2022/11/23
|
||||
|
||||
### 2021/12/16 \[v0.0.7.0]
|
||||
* 修复webui插件无法获取修改
|
||||
|
||||
* 提供了真寻群聊功能总开关和对应默认配置项,命令:休息吧 醒来
|
||||
* 新增原神玩家查询,原神便笺查询
|
||||
* 群功能管理提供全部开启/关闭命令:开启/关闭全部功能
|
||||
* 提供主要数据自动备份,且提供自定义配置项
|
||||
* 提供命令:关于,用于介绍Bot之类的
|
||||
* 新增命令exec,用于执行sql语句
|
||||
* 签到提供参数 "all",用于签到所有群聊
|
||||
* Ban提醒提供cd
|
||||
* 本地图库提供配置项SHOW_ID,用于设置发送图片时是否显示id
|
||||
* 色图和PIX提供配置项SHOW_INFO,用于设置发送图片时是否显示图片信息
|
||||
* 所有被动技能提供了进群默认状态配置项
|
||||
* 修复添加权限第二种添加形式无法正确添加正确的权限
|
||||
* 修复签到获取好感度卡时金币不会增加
|
||||
* 修复当红包数量不合法时依旧扣除金币
|
||||
* 修复金币红包再次使用塞红包时无法正确退回上次未开完的金币
|
||||
* 修复 滴滴滴- 只包含图片时不会发送至管理员
|
||||
* 修复添加权限等级错误
|
||||
* 修复群词条以bot名称为开头时无法正确触发
|
||||
* 修改了权限插件加载顺序防止小概率优先加载权限插件引起报错
|
||||
* 本地图库新图库会统一建立在resource/img/image_management文件夹下,如果该文件夹内未找到图库,会从上级目录查找(即:resource/img/)
|
||||
### 2022/11/22
|
||||
|
||||
* fix switch_rule [@pull/1185](https://github.com/HibiKier/zhenxun_bot/pull/1185)
|
||||
|
||||
### 2022/11/21 \[v0.1.6.5]
|
||||
|
||||
* 优化manager, hook代码
|
||||
* 修复pid搜图 [@pull/1180](https://github.com/HibiKier/zhenxun_bot/pull/1180)
|
||||
|
||||
### 2022/11/19
|
||||
|
||||
* 修改优化帮助图片生成逻辑
|
||||
|
||||
### 2022/11/18
|
||||
|
||||
* poetry添加适配器依赖,更新支持py3.10 [@pull/1176](https://github.com/HibiKier/zhenxun_bot/pull/1176)
|
||||
|
||||
### 2022/11/13
|
||||
|
||||
* 更新天气api
|
||||
* 使用道具可以附带额外信息供函数使用
|
||||
* 限制帮助图片最小宽度
|
||||
|
||||
### 2022/11/12
|
||||
|
||||
* 更新yiqing插件数据显示 [@pull/1168](https://github.com/HibiKier/zhenxun_bot/pull/1168)
|
||||
|
||||
### 2022/11/11
|
||||
|
||||
* fix: B站直播订阅的相关问题 [@pull/1158](https://github.com/HibiKier/zhenxun_bot/pull/1158)
|
||||
|
||||
### 2022/10/30
|
||||
|
||||
* 商店简介动态行数,根据文字长度自动换行
|
||||
|
||||
### 2022/10/28
|
||||
|
||||
* 为exec指令进行了SELECT语句适配,添加了查看所有表指令 [@pull/1155](https://github.com/HibiKier/zhenxun_bot/pull/1155)
|
||||
* 修复复读 [@pull/1154](https://github.com/HibiKier/zhenxun_bot/pull/1154)
|
||||
|
||||
### 2022/10/23
|
||||
|
||||
* 复读修改回图片下载
|
||||
|
||||
### 2022/10/22
|
||||
|
||||
* 更新依赖注入
|
||||
|
||||
### 2022/10/16 \[v0.1.6.4]
|
||||
|
||||
* 修改商店道具icon可以为空
|
||||
|
||||
### 2022/10/15
|
||||
|
||||
* nonebot2版本更新为rc1
|
||||
* 我的道具改为图片形式
|
||||
* 商品添加图标与是否为被动道具(被动道具无法被主动使用)
|
||||
* 商品添加使用前方法和使用后方法(类似hook),使用方法具体查看文档或签到商品文件中注册的例子
|
||||
* 新增用户使用道具,花费金币(包括插件)及用途记录
|
||||
* 更细致的金币使用依赖注入
|
||||
* 更多的依赖注入(包含图片获取等等..
|
||||
* 修复我的道具仅有被动或主动道具时图片显示错误
|
||||
* 色图插件p站反向代理失效 [@pull/1139](https://github.com/HibiKier/zhenxun_bot/pull/1139)
|
||||
|
||||
### 2022/10/9
|
||||
|
||||
* 修复碧蓝档案角色获取问题,换源 [@pull/1124](https://github.com/HibiKier/zhenxun_bot/pull/1124)
|
||||
|
||||
### 2022/10/7
|
||||
|
||||
* 修复 B 站请求返回 -401 错误 [@pull/1119](https://github.com/HibiKier/zhenxun_bot/pull/1119)
|
||||
* 关闭功能与被动时不再区分大小写,同名时仅被动关闭操作生效
|
||||
|
||||
### 2022/9/30
|
||||
|
||||
* 修改重置开箱的使用权限 [@pull/1118](https://github.com/HibiKier/zhenxun_bot/pull/1118)
|
||||
|
||||
### 2022/9/27
|
||||
|
||||
* 更新b站转发解析 [@pull/1117](https://github.com/HibiKier/zhenxun_bot/pull/1117)
|
||||
|
||||
### 2022/9/24
|
||||
|
||||
* 修复b站订阅 [@pull/1112](https://github.com/HibiKier/zhenxun_bot/pull/1112)
|
||||
* fix: 重载赛马娘卡池失败 [@pull/1114](https://github.com/HibiKier/zhenxun_bot/pull/1114)
|
||||
|
||||
### 2022/9/19
|
||||
|
||||
* 更换bilibili_sub获取用户昵称用的API&尝试修了一下get_video() [@pull/1097](https://github.com/HibiKier/zhenxun_bot/pull/1097)
|
||||
* 修复csgo每日开箱可以多开一次
|
||||
|
||||
### 2022/9/18
|
||||
|
||||
* 修复 bilireq 版本过低导致 B 站视频解析错误 [@pull/1090](https://github.com/HibiKier/zhenxun_bot/pull/1096)
|
||||
|
||||
### 2022/9/16
|
||||
|
||||
* fix: bilibili_sub, azur_draw_card [@pull/1090](https://github.com/HibiKier/zhenxun_bot/pull/1090)
|
||||
* 修复原神资源查询查询完毕后图片存储错误
|
||||
* b站订阅发送 与 b站订阅 使用相同开关,即:关闭b站订阅
|
||||
|
||||
### 2022/9/10
|
||||
|
||||
* 自定义群欢迎消息参数不完全时提示报错
|
||||
* 修改bt插件的url地址 [@pull/1067](https://github.com/HibiKier/zhenxun_bot/pull/1067)
|
||||
|
||||
### 2022/9/8
|
||||
|
||||
* 添加插件数据初始化判断
|
||||
|
||||
### 2022/9/4
|
||||
|
||||
* 旧词条提供图片迁移(需要重新获取old_model文件,并将数据库中user_qq为0的数据删除)
|
||||
|
||||
### 2022/9/3
|
||||
|
||||
* 原神玩家查询增加须弥地区 [@pull/1053](https://github.com/HibiKier/zhenxun_bot/pull/1053)
|
||||
* av号覆盖全面,且修复av号链接 [@pull/1033](https://github.com/HibiKier/zhenxun_bot/pull/1033)
|
||||
* 修复词条含有CQ回答的模糊匹配无法被解析
|
||||
* 禁言检测图片在内存中获取图片hash
|
||||
* B站订阅在群里中任意群管理员可以统一管理(原来为管理员1无法删除管理员2的订阅)
|
||||
* 修复原神资源查询地图api数据变更导致更新的地图不完全
|
||||
|
||||
### 2022/8/27
|
||||
|
||||
* 修复签到积分双倍后,日志记录获得积分变4倍问题 [@pull/1044](https://github.com/HibiKier/zhenxun_bot/pull/1044)
|
||||
|
||||
### 2022/8/26
|
||||
|
||||
* 修复群管理员无法添加词条
|
||||
* 修复词条关键词"问"前空格问题
|
||||
|
||||
### 2022/8/23
|
||||
|
||||
* 修了下模糊匹配 issue#1026 [@pull/1026](https://github.com/HibiKier/zhenxun_bot/pull/1026)
|
||||
|
||||
### 2022/8/22
|
||||
|
||||
* 修复首次安装时词条旧表出错(因为根本就没有这张表!)
|
||||
* 取消配置替换定时任务,统一存储
|
||||
* 对米游社cookie进行判断,整合米游社签到信息 [@pull/1014](https://github.com/HibiKier/zhenxun_bot/pull/1014)
|
||||
* 修正尘歌壶和质变仪图片获取地址 [@pull/1010](https://github.com/HibiKier/zhenxun_bot/pull/1010)
|
||||
* 修复词库问答 **很多** 问题[@pull/1012](https://github.com/HibiKier/zhenxun_bot/pull/1012)
|
||||
|
||||
### 2022/8/21 \[v0.1.6.3]
|
||||
|
||||
* 重构群词条,改为词库Plus,增加 精准|模糊|正则 问题匹配,问题与回答均支持at,image,face,超级用户额外提供 全局|私聊 词库设置,数据迁移目前只提供了问题和回答都是纯文本的词条
|
||||
* 修复b站转发解析av号无法解析
|
||||
* B站订阅直播订阅支持短号
|
||||
* 开箱提供重置开箱命令,重置今日所有开箱数据(重置次数,并不会删除今日已开箱记录)
|
||||
* 提供全局字典GDict,通过from utils.utils import GDict导入
|
||||
* 适配omega 13w张图的数据结构表(建议删表重导)
|
||||
* 除首次启动外将配置替换加入单次定时任务,加快启动速度
|
||||
* fix: WordBank.check() [@pull/1008](https://github.com/HibiKier/zhenxun_bot/pull/1008)
|
||||
* 改进插件 `我有一个朋友`,避免触发过于频繁 [@pull/1001](https://github.com/HibiKier/zhenxun_bot/pull/1001)
|
||||
* 原神便笺新增洞天宝钱和参量质变仪提示 [@pull/1005](https://github.com/HibiKier/zhenxun_bot/pull/1005)
|
||||
* 新增米游社签到功能,自动领取(白嫖)米游币 [@pull/991](https://github.com/HibiKier/zhenxun_bot/pull/991)
|
||||
|
||||
### 2022/8/14
|
||||
|
||||
* 修复epic未获取到时间时出错
|
||||
* 修复订阅主播时动态获取的id是直播间id
|
||||
|
||||
### 2022/8/8
|
||||
|
||||
* 修复赛马娘重载卡池失败的问题 [@pull/969](https://github.com/HibiKier/zhenxun_bot/pull/969)
|
||||
|
||||
### 2022/8/3
|
||||
|
||||
* 修复 bili动态链接在投稿视频时URL和分割线连在一起 [@pull/951](https://github.com/HibiKier/zhenxun_bot/pull/961)
|
||||
* 更新 Epic 免费游戏商城链接拼接规则 [@pull/957](https://github.com/HibiKier/zhenxun_bot/pull/957)
|
||||
|
||||
### 2022/8/6
|
||||
|
||||
* 修复了原神自动签到返回invalid request的问题,新增查看我的cookie命令 [@pull/971](https://github.com/HibiKier/zhenxun_bot/pull/971)
|
||||
|
||||
<br>
|
||||
|
||||
__..... 更多更新信息请查看文档__
|
||||
|
||||
**..... 更多更新信息请查看文档**
|
||||
|
||||
## Todo
|
||||
- [ ] web管理
|
||||
|
||||
* [x] web管理
|
||||
|
||||
## 感谢
|
||||
|
||||
[botuniverse / onebot](https://github.com/botuniverse/onebot) :超棒的机器人协议
|
||||
[Mrs4s / go-cqhttp](https://github.com/Mrs4s/go-cqhttp) :cqhttp的golang实现,轻量、原生跨平台.
|
||||
[nonebot / nonebot2](https://github.com/nonebot/nonebot2) :跨平台Python异步机器人框架
|
||||
@@ -477,4 +684,4 @@ __..... 更多更新信息请查看文档__
|
||||
[NothAmor / nonebot2_luxun_says](https://github.com/NothAmor/nonebot2_luxun_says) :基于nonebot2机器人框架的鲁迅说插件
|
||||
[Kyomotoi / AnimeThesaurus](https://github.com/Kyomotoi/AnimeThesaurus) :一个~~特二刺螈~~(文爱)的适用于任何bot的词库
|
||||
[Ailitonia / omega-miya](https://github.com/Ailitonia/omega-miya) :基于nonebot2的qq机器人
|
||||
[KimigaiiWuyi / GenshinUID]("https://github.com/KimigaiiWuyi/GenshinUID") :一个基于HoshinoBot/NoneBot2的原神UID查询插件
|
||||
[KimigaiiWuyi / GenshinUID](https://github.com/KimigaiiWuyi/GenshinUID) :一个基于HoshinoBot/NoneBot2的原神UID查询插件
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
__version__: v0.1.5.0
|
||||
__version__: v0.1.6.7
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
from configs.config import Config
|
||||
from pathlib import Path
|
||||
|
||||
import nonebot
|
||||
|
||||
from configs.config import Config
|
||||
|
||||
Config.add_plugin_config(
|
||||
"admin_bot_manage:custom_welcome_message",
|
||||
@@ -9,6 +11,7 @@ Config.add_plugin_config(
|
||||
name="群管理员操作",
|
||||
help_="设置群欢迎消息权限",
|
||||
default_value=2,
|
||||
type=int,
|
||||
)
|
||||
|
||||
Config.add_plugin_config(
|
||||
@@ -17,6 +20,7 @@ Config.add_plugin_config(
|
||||
2,
|
||||
help_="开关群功能权限",
|
||||
default_value=2,
|
||||
type=int,
|
||||
)
|
||||
|
||||
Config.add_plugin_config(
|
||||
@@ -24,7 +28,8 @@ Config.add_plugin_config(
|
||||
"ADMIN_DEFAULT_AUTH",
|
||||
5,
|
||||
help_="默认群管理员权限",
|
||||
default_value=5
|
||||
default_value=5,
|
||||
type=int,
|
||||
)
|
||||
|
||||
nonebot.load_plugins("basic_plugins/admin_bot_manage")
|
||||
nonebot.load_plugins(str(Path(__file__).parent.resolve()))
|
||||
|
||||
@@ -1,68 +1,95 @@
|
||||
import asyncio
|
||||
import os
|
||||
import time
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
from typing import List
|
||||
|
||||
import ujson as json
|
||||
from nonebot.adapters.onebot.v11.message import MessageSegment
|
||||
|
||||
from configs.config import Config
|
||||
from configs.path_config import DATA_PATH, IMAGE_PATH
|
||||
from models.group_member_info import GroupInfoUser
|
||||
from models.level_user import LevelUser
|
||||
from services.log import logger
|
||||
from configs.path_config import DATA_PATH
|
||||
from utils.http_utils import AsyncHttpx
|
||||
from utils.image_utils import BuildImage
|
||||
from utils.manager import group_manager, plugins2settings_manager, plugins_manager
|
||||
from utils.message_builder import image
|
||||
from utils.utils import get_bot, get_matchers
|
||||
from pathlib import Path
|
||||
from models.group_member_info import GroupInfoUser
|
||||
from datetime import datetime
|
||||
from services.db_context import db
|
||||
from models.level_user import LevelUser
|
||||
from configs.config import Config
|
||||
from utils.manager import group_manager, plugins2settings_manager, plugins_manager
|
||||
from utils.image_utils import BuildImage
|
||||
from utils.http_utils import AsyncHttpx
|
||||
import asyncio
|
||||
import time
|
||||
import os
|
||||
|
||||
try:
|
||||
import ujson as json
|
||||
except ModuleNotFoundError:
|
||||
import json
|
||||
|
||||
|
||||
async def group_current_status(group_id: int) -> str:
|
||||
"""
|
||||
获取当前所有通知的开关
|
||||
:param group_id: 群号
|
||||
"""
|
||||
rst = "[被动技能 状态]\n"
|
||||
_data = group_manager.get_task_data()
|
||||
for task in _data.keys():
|
||||
rst += f'{_data[task]}: {"√" if await group_manager.check_group_task_status(group_id, task) else "×"}\n'
|
||||
return rst.strip()
|
||||
|
||||
|
||||
custom_welcome_msg_json = (
|
||||
Path() / "data" / "custom_welcome_msg" / "custom_welcome_msg.json"
|
||||
)
|
||||
|
||||
ICON_PATH = IMAGE_PATH / "other"
|
||||
|
||||
|
||||
async def group_current_status(group_id: int) -> str:
|
||||
"""
|
||||
说明:
|
||||
获取当前群聊所有通知的开关
|
||||
参数:
|
||||
:param group_id: 群号
|
||||
"""
|
||||
_data = group_manager.get_task_data()
|
||||
image_list = []
|
||||
for i, task in enumerate(_data):
|
||||
name = _data[task]
|
||||
name_image = BuildImage(0, 0, plain_text=f"{i+1}.{name}", font_size=20)
|
||||
bk = BuildImage(
|
||||
name_image.w + 200, name_image.h + 20, color=(103, 177, 109), font_size=15
|
||||
)
|
||||
await bk.apaste(name_image, (10, 0), True, "by_height")
|
||||
a_icon = BuildImage(40, 40, background=ICON_PATH / "btn_false.png")
|
||||
if group_manager.check_group_task_status(group_id, task):
|
||||
a_icon = BuildImage(40, 40, background=ICON_PATH / "btn_true.png")
|
||||
b_icon = BuildImage(40, 40, background=ICON_PATH / "btn_false.png")
|
||||
if group_manager.check_task_super_status(task):
|
||||
b_icon = BuildImage(40, 40, background=ICON_PATH / "btn_true.png")
|
||||
await bk.atext((name_image.w + 20, 10), "状态")
|
||||
await bk.apaste(a_icon, (name_image.w + 50, 0), True)
|
||||
await bk.atext((name_image.w + 100, 10), "全局")
|
||||
await bk.apaste(b_icon, (name_image.w + 130, 0), True)
|
||||
image_list.append(bk)
|
||||
w = max([x.w for x in image_list])
|
||||
h = sum([x.h + 10 for x in image_list])
|
||||
A = BuildImage(w + 20, h + 70, font_size=30, color=(119, 97, 177))
|
||||
await A.atext((15, 20), "群被动状态")
|
||||
curr_h = 75
|
||||
for img in image_list:
|
||||
# await img.acircle_corner()
|
||||
await A.apaste(img, (0, curr_h), True)
|
||||
curr_h += img.h + 10
|
||||
return A.pic2bs4()
|
||||
|
||||
|
||||
async def custom_group_welcome(
|
||||
msg: str, imgs: List[str], user_id: int, group_id: int
|
||||
msg: str, img_list: List[str], user_id: int, group_id: int
|
||||
) -> str:
|
||||
"""
|
||||
替换群欢迎消息
|
||||
:param msg: 欢迎消息文本
|
||||
:param imgs: 欢迎消息图片,只取第一张
|
||||
:param user_id: 用户id,用于log记录
|
||||
:param group_id: 群号
|
||||
说明:
|
||||
替换群欢迎消息
|
||||
参数:
|
||||
:param msg: 欢迎消息文本
|
||||
:param img_list: 欢迎消息图片,只取第一张
|
||||
:param user_id: 用户id,用于log记录
|
||||
:param group_id: 群号
|
||||
"""
|
||||
img_result = ""
|
||||
img = imgs[0] if imgs else ""
|
||||
result = ""
|
||||
img = img_list[0] if img_list else ""
|
||||
if (DATA_PATH / f"custom_welcome_msg/{group_id}.jpg").exists():
|
||||
(DATA_PATH / f"custom_welcome_msg/{group_id}.jpg").unlink()
|
||||
data = {}
|
||||
if not custom_welcome_msg_json.exists():
|
||||
custom_welcome_msg_json.parent.mkdir(parents=True, exist_ok=True)
|
||||
data = {}
|
||||
else:
|
||||
try:
|
||||
data = json.load(open(custom_welcome_msg_json, "r"))
|
||||
except FileNotFoundError:
|
||||
data = {}
|
||||
pass
|
||||
try:
|
||||
if msg:
|
||||
data[str(group_id)] = str(msg)
|
||||
@@ -86,14 +113,48 @@ async def custom_group_welcome(
|
||||
task_data = None
|
||||
|
||||
|
||||
async def change_group_switch(cmd: str, group_id: int, is_super: bool = False):
|
||||
def change_global_task_status(cmd: str) -> str:
|
||||
"""
|
||||
说明:
|
||||
修改全局被动任务状态
|
||||
参数:
|
||||
:param cmd: 功能名称
|
||||
"""
|
||||
global task_data
|
||||
if not task_data:
|
||||
task_data = group_manager.get_task_data()
|
||||
status = cmd[:2]
|
||||
_cmd = cmd[4:]
|
||||
if "全部被动" in cmd:
|
||||
for task in task_data:
|
||||
if status == "开启":
|
||||
group_manager.open_global_task(task)
|
||||
else:
|
||||
group_manager.close_global_task(task)
|
||||
group_manager.save()
|
||||
return f"已 {status} 全局全部被动技能!"
|
||||
else:
|
||||
modules = [x for x in task_data if task_data[x].lower() == _cmd.lower()]
|
||||
if not modules:
|
||||
return "未查询到该被动任务"
|
||||
if status == "开启":
|
||||
group_manager.open_global_task(modules[0])
|
||||
else:
|
||||
group_manager.close_global_task(modules[0])
|
||||
group_manager.save()
|
||||
return f"已 {status} 全局{_cmd}"
|
||||
|
||||
|
||||
async def change_group_switch(cmd: str, group_id: int, is_super: bool = False) -> str:
|
||||
"""
|
||||
修改群功能状态
|
||||
:param cmd: 功能名称
|
||||
:param group_id: 群号
|
||||
:param is_super: 是否位超级用户,超级用户用于私聊开关功能状态
|
||||
说明:
|
||||
修改群功能状态
|
||||
参数:
|
||||
:param cmd: 功能名称
|
||||
:param group_id: 群号
|
||||
:param is_super: 是否位超级用户,超级用户用于私聊开关功能状态
|
||||
"""
|
||||
global task_data
|
||||
if not task_data:
|
||||
task_data = group_manager.get_task_data()
|
||||
group_help_file = DATA_PATH / "group_help" / f"{group_id}.png"
|
||||
@@ -104,41 +165,44 @@ async def change_group_switch(cmd: str, group_id: int, is_super: bool = False):
|
||||
if cmd == "全部被动":
|
||||
for task in task_data:
|
||||
if status == "开启":
|
||||
if not await group_manager.check_group_task_status(group_id, task):
|
||||
await group_manager.open_group_task(group_id, task)
|
||||
if not group_manager.check_group_task_status(group_id, task):
|
||||
group_manager.open_group_task(group_id, task)
|
||||
else:
|
||||
if await group_manager.check_group_task_status(group_id, task):
|
||||
await group_manager.close_group_task(group_id, task)
|
||||
if group_manager.check_group_task_status(group_id, task):
|
||||
group_manager.close_group_task(group_id, task)
|
||||
if group_help_file.exists():
|
||||
group_help_file.unlink()
|
||||
return f"已 {status} 全部被动技能!"
|
||||
if cmd == "全部功能":
|
||||
for f in plugins2settings_manager.get_data():
|
||||
if status == "开启":
|
||||
group_manager.unblock_plugin(f, group_id)
|
||||
group_manager.unblock_plugin(f, group_id, False)
|
||||
else:
|
||||
group_manager.block_plugin(f, group_id)
|
||||
group_manager.block_plugin(f, group_id, False)
|
||||
group_manager.save()
|
||||
if group_help_file.exists():
|
||||
group_help_file.unlink()
|
||||
return f"已 {status} 全部功能!"
|
||||
if cmd in [task_data[x] for x in task_data.keys()]:
|
||||
if cmd.lower() in [task_data[x].lower() for x in task_data.keys()]:
|
||||
type_ = "task"
|
||||
modules = [x for x in task_data.keys() if task_data[x] == cmd]
|
||||
modules = [x for x in task_data.keys() if task_data[x].lower() == cmd.lower()]
|
||||
for module in modules:
|
||||
if is_super:
|
||||
module = f"{module}:super"
|
||||
if status == "开启":
|
||||
if type_ == "task":
|
||||
if await group_manager.check_group_task_status(group_id, module):
|
||||
if group_manager.check_group_task_status(group_id, module):
|
||||
return f"被动 {task_data[module]} 正处于开启状态!不要重复开启."
|
||||
await group_manager.open_group_task(group_id, module)
|
||||
group_manager.open_group_task(group_id, module)
|
||||
else:
|
||||
if group_manager.get_plugin_status(module, group_id):
|
||||
return f"功能 {cmd} 正处于开启状态!不要重复开启."
|
||||
group_manager.unblock_plugin(module, group_id)
|
||||
else:
|
||||
if type_ == "task":
|
||||
if not await group_manager.check_group_task_status(group_id, module):
|
||||
if not group_manager.check_group_task_status(group_id, module):
|
||||
return f"被动 {task_data[module]} 正处于关闭状态!不要重复关闭."
|
||||
await group_manager.close_group_task(group_id, module)
|
||||
group_manager.close_group_task(group_id, module)
|
||||
else:
|
||||
if not group_manager.get_plugin_status(module, group_id):
|
||||
return f"功能 {cmd} 正处于关闭状态!不要重复关闭."
|
||||
@@ -158,9 +222,11 @@ async def change_group_switch(cmd: str, group_id: int, is_super: bool = False):
|
||||
|
||||
def set_plugin_status(cmd: str, block_type: str = "all"):
|
||||
"""
|
||||
设置插件功能状态(超级用户使用)
|
||||
:param cmd: 功能名称
|
||||
:param block_type: 限制类型, 'all': 私聊+群里, 'private': 私聊, 'group': 群聊
|
||||
说明:
|
||||
设置插件功能状态(超级用户使用)
|
||||
参数:
|
||||
:param cmd: 功能名称
|
||||
:param block_type: 限制类型, 'all': 私聊+群里, 'private': 私聊, 'group': 群聊
|
||||
"""
|
||||
status = cmd[:2]
|
||||
cmd = cmd[2:]
|
||||
@@ -176,14 +242,16 @@ def set_plugin_status(cmd: str, block_type: str = "all"):
|
||||
|
||||
async def get_plugin_status():
|
||||
"""
|
||||
获取功能状态
|
||||
说明:
|
||||
获取功能状态
|
||||
"""
|
||||
return await asyncio.get_event_loop().run_in_executor(None, _get_plugin_status)
|
||||
|
||||
|
||||
def _get_plugin_status() -> MessageSegment:
|
||||
"""
|
||||
合成功能状态图片
|
||||
说明:
|
||||
合成功能状态图片
|
||||
"""
|
||||
rst = "\t功能\n"
|
||||
flag_str = "状态".rjust(4) + "\n"
|
||||
@@ -195,7 +263,7 @@ def _get_plugin_status() -> MessageSegment:
|
||||
flag = plugins_manager.get_plugin_block_type(module)
|
||||
flag = flag.upper() + " CLOSE" if flag else "OPEN"
|
||||
try:
|
||||
plugin_name = plugins_manager.get(module)["plugin_name"]
|
||||
plugin_name = plugins_manager.get(module).plugin_name
|
||||
if (
|
||||
"[Hidden]" in plugin_name
|
||||
or "[Admin]" in plugin_name
|
||||
@@ -205,7 +273,7 @@ def _get_plugin_status() -> MessageSegment:
|
||||
rst += f"{plugin_name}"
|
||||
except KeyError:
|
||||
rst += f"{module}"
|
||||
if plugins_manager.get(module)["error"]:
|
||||
if plugins_manager.get(module).error:
|
||||
rst += "[ERROR]"
|
||||
rst += "\n"
|
||||
flag_str += f"{flag}\n"
|
||||
@@ -222,9 +290,11 @@ def _get_plugin_status() -> MessageSegment:
|
||||
|
||||
async def update_member_info(group_id: int, remind_superuser: bool = False) -> bool:
|
||||
"""
|
||||
更新群成员信息
|
||||
:param group_id: 群号
|
||||
:param remind_superuser: 失败信息提醒超级用户
|
||||
说明:
|
||||
更新群成员信息
|
||||
参数:
|
||||
:param group_id: 群号
|
||||
:param remind_superuser: 失败信息提醒超级用户
|
||||
"""
|
||||
bot = get_bot()
|
||||
_group_user_list = await bot.get_group_member_list(group_id=group_id)
|
||||
@@ -232,58 +302,54 @@ async def update_member_info(group_id: int, remind_superuser: bool = False) -> b
|
||||
_exist_member_list = []
|
||||
# try:
|
||||
for user_info in _group_user_list:
|
||||
if user_info["card"] == "":
|
||||
nickname = user_info["nickname"]
|
||||
else:
|
||||
nickname = user_info["card"]
|
||||
async with db.transaction():
|
||||
# 更新权限
|
||||
if (
|
||||
user_info["role"]
|
||||
in [
|
||||
"owner",
|
||||
"admin",
|
||||
]
|
||||
and not await LevelUser.is_group_flag(user_info["user_id"], group_id)
|
||||
):
|
||||
await LevelUser.set_level(
|
||||
user_info["user_id"],
|
||||
user_info["group_id"],
|
||||
Config.get_config("admin_bot_manage", "ADMIN_DEFAULT_AUTH"),
|
||||
)
|
||||
if str(user_info["user_id"]) in bot.config.superusers:
|
||||
await LevelUser.set_level(
|
||||
user_info["user_id"], user_info["group_id"], 9
|
||||
)
|
||||
user = await GroupInfoUser.get_member_info(
|
||||
user_info["user_id"], user_info["group_id"]
|
||||
)
|
||||
if user:
|
||||
if user.user_name != nickname:
|
||||
await user.update(user_name=nickname).apply()
|
||||
logger.info(
|
||||
f"用户{user_info['user_id']} 所属{user_info['group_id']} 更新群昵称成功"
|
||||
)
|
||||
_exist_member_list.append(int(user_info["user_id"]))
|
||||
continue
|
||||
join_time = datetime.strptime(
|
||||
time.strftime(
|
||||
"%Y-%m-%d %H:%M:%S", time.localtime(user_info["join_time"])
|
||||
),
|
||||
"%Y-%m-%d %H:%M:%S",
|
||||
)
|
||||
if await GroupInfoUser.add_member_info(
|
||||
nickname = user_info["card"] or user_info["nickname"]
|
||||
# 更新权限
|
||||
if user_info["role"] in [
|
||||
"owner",
|
||||
"admin",
|
||||
] and not await LevelUser.is_group_flag(user_info["user_id"], group_id):
|
||||
await LevelUser.set_level(
|
||||
user_info["user_id"],
|
||||
user_info["group_id"],
|
||||
nickname,
|
||||
join_time,
|
||||
):
|
||||
_exist_member_list.append(int(user_info["user_id"]))
|
||||
logger.info(f"用户{user_info['user_id']} 所属{user_info['group_id']} 更新成功")
|
||||
else:
|
||||
_error_member_list.append(
|
||||
f"用户{user_info['user_id']} 所属{user_info['group_id']} 更新失败\n"
|
||||
Config.get_config("admin_bot_manage", "ADMIN_DEFAULT_AUTH"),
|
||||
)
|
||||
if str(user_info["user_id"]) in bot.config.superusers:
|
||||
await LevelUser.set_level(user_info["user_id"], user_info["group_id"], 9)
|
||||
user = await GroupInfoUser.get_or_none(
|
||||
user_qq=user_info["user_id"], group_id=user_info["group_id"]
|
||||
)
|
||||
if user:
|
||||
if user.user_name != nickname:
|
||||
user.user_name = nickname
|
||||
await user.save(update_fields=["user_name"])
|
||||
logger.info(
|
||||
f"用户{user_info['user_id']} 所属{user_info['group_id']} 更新群昵称成功"
|
||||
)
|
||||
_exist_member_list.append(int(user_info["user_id"]))
|
||||
continue
|
||||
join_time = datetime.strptime(
|
||||
time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(user_info["join_time"])),
|
||||
"%Y-%m-%d %H:%M:%S",
|
||||
)
|
||||
user, _ = await GroupInfoUser.get_or_create(
|
||||
user_qq=user_info["user_id"],
|
||||
group_id=user_info["group_id"],
|
||||
defaults={
|
||||
"user_name": nickname,
|
||||
},
|
||||
)
|
||||
# await GroupInfoUser.update_or_create(
|
||||
# user_qq=user_info["user_id"],
|
||||
# group_id=user_info["group_id"],
|
||||
# defaults={
|
||||
# "user_name": nickname,
|
||||
# # "user_join_time": datetime.now().replace(tzinfo=None),
|
||||
# },
|
||||
# )
|
||||
user.user_join_time = join_time
|
||||
await user.save()
|
||||
_exist_member_list.append(int(user_info["user_id"]))
|
||||
logger.info("更新成功", "更新成员信息", user_info["user_id"], user_info["group_id"])
|
||||
_del_member_list = list(
|
||||
set(_exist_member_list).difference(
|
||||
set(await GroupInfoUser.get_group_member_id_list(group_id))
|
||||
@@ -291,10 +357,8 @@ async def update_member_info(group_id: int, remind_superuser: bool = False) -> b
|
||||
)
|
||||
if _del_member_list:
|
||||
for del_user in _del_member_list:
|
||||
if await GroupInfoUser.delete_member_info(del_user, group_id):
|
||||
logger.info(f"退群用户{del_user} 所属{group_id} 已删除")
|
||||
else:
|
||||
logger.info(f"退群用户{del_user} 所属{group_id} 删除失败")
|
||||
await GroupInfoUser.filter(user_qq=del_user, group_id=group_id).delete()
|
||||
logger.info(f"退群用户{del_user} 所属{group_id} 已删除")
|
||||
if _error_member_list and remind_superuser:
|
||||
result = ""
|
||||
for error_user in _error_member_list:
|
||||
@@ -307,9 +371,11 @@ async def update_member_info(group_id: int, remind_superuser: bool = False) -> b
|
||||
|
||||
def set_group_bot_status(group_id: int, status: bool) -> str:
|
||||
"""
|
||||
设置群聊bot开关状态
|
||||
:param group_id: 群号
|
||||
:param status: 状态
|
||||
说明:
|
||||
设置群聊bot开关状态
|
||||
参数:
|
||||
:param group_id: 群号
|
||||
:param status: 状态
|
||||
"""
|
||||
if status:
|
||||
if group_manager.check_group_bot_status(group_id):
|
||||
@@ -318,6 +384,4 @@ def set_group_bot_status(group_id: int, status: bool) -> str:
|
||||
return "呜..醒来了..."
|
||||
else:
|
||||
group_manager.shutdown_group_bot_status(group_id)
|
||||
# for x in group_manager.get_task_data():
|
||||
# group_manager.close_group_task(group_id, x)
|
||||
return "那我先睡觉了..."
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
from nonebot import on_notice
|
||||
from services.log import logger
|
||||
from nonebot.adapters.onebot.v11 import GroupAdminNoticeEvent
|
||||
from models.level_user import LevelUser
|
||||
from models.group_member_info import GroupInfoUser
|
||||
from configs.config import Config
|
||||
|
||||
from configs.config import Config
|
||||
from models.group_member_info import GroupInfoUser
|
||||
from models.level_user import LevelUser
|
||||
from services.log import logger
|
||||
|
||||
__zx_plugin_name__ = "群管理员变动监测 [Hidden]"
|
||||
__plugin_version__ = 0.1
|
||||
@@ -16,11 +16,11 @@ admin_notice = on_notice(priority=5)
|
||||
|
||||
@admin_notice.handle()
|
||||
async def _(event: GroupAdminNoticeEvent):
|
||||
try:
|
||||
nickname = (
|
||||
await GroupInfoUser.get_member_info(event.user_id, event.group_id)
|
||||
).user_name
|
||||
except AttributeError:
|
||||
if user := await GroupInfoUser.filter(
|
||||
user_qq=event.user_id, group_id=event.group_id
|
||||
).first():
|
||||
nickname = user.nickname
|
||||
else:
|
||||
nickname = event.user_id
|
||||
if event.sub_type == "set":
|
||||
await LevelUser.set_level(
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
from nonebot import on_command
|
||||
from utils.utils import get_message_img
|
||||
from nonebot.adapters.onebot.v11 import GroupMessageEvent, Message
|
||||
from nonebot.params import CommandArg
|
||||
from ._data_source import custom_group_welcome
|
||||
from nonebot.adapters.onebot.v11.permission import GROUP
|
||||
from configs.config import Config
|
||||
from services.log import logger
|
||||
from typing import List
|
||||
|
||||
from configs.config import Config
|
||||
from nonebot import on_command
|
||||
from nonebot.adapters.onebot.v11 import GroupMessageEvent, Message
|
||||
from nonebot.adapters.onebot.v11.permission import GROUP
|
||||
from nonebot.params import CommandArg
|
||||
from services.log import logger
|
||||
from utils.depends import ImageList
|
||||
|
||||
from ._data_source import custom_group_welcome
|
||||
|
||||
__zx_plugin_name__ = "自定义进群欢迎消息 [Admin]"
|
||||
__plugin_usage__ = """
|
||||
@@ -17,12 +19,14 @@ usage:
|
||||
Note:可以通过[at]来确认是否艾特新成员
|
||||
示例:自定义进群欢迎消息 欢迎你[at]
|
||||
""".strip()
|
||||
__plugin_des__ = '简易的自定义群欢迎消息'
|
||||
__plugin_cmd__ = ['自定义群欢迎消息 ?[文本] ?[图片]']
|
||||
__plugin_des__ = "简易的自定义群欢迎消息"
|
||||
__plugin_cmd__ = ["自定义群欢迎消息 ?[文本] ?[图片]"]
|
||||
__plugin_version__ = 0.1
|
||||
__plugin_author__ = 'HibiKier'
|
||||
__plugin_author__ = "HibiKier"
|
||||
__plugin_settings__ = {
|
||||
"admin_level": Config.get_config("admin_bot_manage", "SET_GROUP_WELCOME_MESSAGE_LEVEL"),
|
||||
"admin_level": Config.get_config(
|
||||
"admin_bot_manage", "SET_GROUP_WELCOME_MESSAGE_LEVEL"
|
||||
),
|
||||
}
|
||||
|
||||
custom_welcome = on_command(
|
||||
@@ -35,12 +39,13 @@ custom_welcome = on_command(
|
||||
|
||||
|
||||
@custom_welcome.handle()
|
||||
async def _(event: GroupMessageEvent, arg: Message = CommandArg()):
|
||||
async def _(
|
||||
event: GroupMessageEvent, arg: Message = CommandArg(), img: List[str] = ImageList()
|
||||
):
|
||||
msg = arg.extract_plain_text().strip()
|
||||
if not msg and not img:
|
||||
await custom_welcome.finish(__plugin_usage__)
|
||||
try:
|
||||
msg = arg.extract_plain_text().strip()
|
||||
img = get_message_img(event.json())
|
||||
if not msg and not img:
|
||||
await custom_welcome.finish(__plugin_usage__)
|
||||
await custom_welcome.send(
|
||||
await custom_group_welcome(msg, img, event.user_id, event.group_id),
|
||||
at_sender=True,
|
||||
|
||||
@@ -2,30 +2,37 @@ from nonebot.adapters.onebot.v11 import Event
|
||||
from utils.manager import group_manager, plugins2settings_manager
|
||||
from utils.utils import get_message_text
|
||||
from services.log import logger
|
||||
import time
|
||||
|
||||
cmd = []
|
||||
|
||||
v = time.time()
|
||||
|
||||
|
||||
def switch_rule(event: Event) -> bool:
|
||||
"""
|
||||
检测文本是否是关闭功能命令
|
||||
:param event: pass
|
||||
说明:
|
||||
检测文本是否是关闭功能命令
|
||||
参数:
|
||||
:param event: pass
|
||||
"""
|
||||
global cmd
|
||||
global cmd, v
|
||||
try:
|
||||
if not cmd:
|
||||
if not cmd or time.time() - v > 60 * 60:
|
||||
cmd = ["关闭全部被动", "开启全部被动", "开启全部功能", "关闭全部功能"]
|
||||
_data = group_manager.get_task_data()
|
||||
for key in _data:
|
||||
cmd.append(f"开启{_data[key]}")
|
||||
cmd.append(f"关闭{_data[key]}")
|
||||
cmd.append(f"开启被动{_data[key]}")
|
||||
cmd.append(f"关闭被动{_data[key]}")
|
||||
cmd.append(f"开启 {_data[key]}")
|
||||
cmd.append(f"关闭 {_data[key]}")
|
||||
_data = plugins2settings_manager.get_data()
|
||||
for key in _data:
|
||||
for key in _data.keys():
|
||||
try:
|
||||
if isinstance(_data[key]["cmd"], list):
|
||||
for x in _data[key]["cmd"]:
|
||||
if isinstance(_data[key].cmd, list):
|
||||
for x in _data[key].cmd:
|
||||
cmd.append(f"开启{x}")
|
||||
cmd.append(f"关闭{x}")
|
||||
cmd.append(f"开启 {x}")
|
||||
@@ -37,6 +44,7 @@ def switch_rule(event: Event) -> bool:
|
||||
cmd.append(f"关闭 {key}")
|
||||
except KeyError:
|
||||
pass
|
||||
v = time.time()
|
||||
msg = get_message_text(event.json()).split()
|
||||
msg = msg[0] if msg else ""
|
||||
return msg in cmd
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
from nonebot.adapters.onebot.v11 import Bot, GroupMessageEvent, MessageEvent, GROUP
|
||||
from nonebot.adapters.onebot.v11 import Bot, GroupMessageEvent, MessageEvent, GROUP, PrivateMessageEvent
|
||||
from nonebot import on_command, on_message, on_regex
|
||||
from nonebot.params import RegexGroup
|
||||
|
||||
from utils.message_builder import image
|
||||
from ._data_source import (
|
||||
change_group_switch,
|
||||
set_plugin_status,
|
||||
get_plugin_status,
|
||||
group_current_status,
|
||||
set_group_bot_status
|
||||
set_group_bot_status, change_global_task_status
|
||||
)
|
||||
from services.log import logger
|
||||
from configs.config import NICKNAME, Config
|
||||
@@ -31,11 +33,12 @@ usage:
|
||||
""".strip()
|
||||
__plugin_superuser_usage__ = """
|
||||
usage:
|
||||
功能总开关与指定群禁用
|
||||
(私聊)功能总开关与指定群禁用
|
||||
指令:
|
||||
功能状态
|
||||
开启/关闭[功能] [group]
|
||||
开启/关闭[功能] ['private'/'group']
|
||||
开启被动/关闭被动[被动名称] # 全局被动控制
|
||||
""".strip()
|
||||
__plugin_des__ = "群内功能开关"
|
||||
__plugin_cmd__ = [
|
||||
@@ -74,7 +77,9 @@ async def _(bot: Bot, event: MessageEvent):
|
||||
if str(event.user_id) in bot.config.superusers:
|
||||
block_type = " ".join(get_message_text(event.json()).split()[1:])
|
||||
block_type = block_type if block_type else "a"
|
||||
if is_number(block_type):
|
||||
if ("关闭被动" in _cmd or "开启被动" in _cmd) and isinstance(event, PrivateMessageEvent):
|
||||
await switch_rule_matcher.send(change_global_task_status(_cmd))
|
||||
elif is_number(block_type):
|
||||
if not int(block_type) in [
|
||||
g["group_id"]
|
||||
for g in await bot.get_group_list()
|
||||
@@ -85,7 +90,7 @@ async def _(bot: Bot, event: MessageEvent):
|
||||
"group_name"
|
||||
]
|
||||
await switch_rule_matcher.send(
|
||||
f"已禁用群聊 {group_name}({block_type}) 的 {_cmd[2:]} 功能"
|
||||
f"已{_cmd[:2]}群聊 {group_name}({block_type}) 的 {_cmd[2:]} 功能"
|
||||
)
|
||||
elif block_type in ["all", "private", "group", "a", "p", "g"]:
|
||||
block_type = "all" if block_type == "a" else block_type
|
||||
@@ -110,7 +115,7 @@ async def _():
|
||||
|
||||
@group_task_status.handle()
|
||||
async def _(event: GroupMessageEvent):
|
||||
await group_task_status.send(await group_current_status(event.group_id))
|
||||
await group_task_status.send(image(b64=await group_current_status(event.group_id)))
|
||||
|
||||
|
||||
@group_status.handle()
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
from utils.utils import scheduler, get_bot
|
||||
from ._data_source import update_member_info
|
||||
from services.log import logger
|
||||
from models.group_info import GroupInfo
|
||||
from asyncpg.exceptions import ConnectionDoesNotExistError, UndefinedColumnError
|
||||
|
||||
from models.group_info import GroupInfo
|
||||
from services.log import logger
|
||||
from utils.utils import get_bot, scheduler
|
||||
|
||||
__zx_plugin_name__ = '管理方面定时任务 [Hidden]'
|
||||
__plugin_usage__ = '无'
|
||||
__plugin_des__ = '成员信息和管理权限的定时更新'
|
||||
from ._data_source import update_member_info
|
||||
|
||||
__zx_plugin_name__ = "管理方面定时任务 [Hidden]"
|
||||
__plugin_usage__ = "无"
|
||||
__plugin_des__ = "成员信息和管理权限的定时更新"
|
||||
__plugin_version__ = 0.1
|
||||
__plugin_author__ = 'HibiKier'
|
||||
__plugin_author__ = "HibiKier"
|
||||
|
||||
|
||||
# 自动更新群员信息
|
||||
@@ -42,7 +43,7 @@ async def _():
|
||||
if bot:
|
||||
gl = await bot.get_group_list()
|
||||
gl = [g["group_id"] for g in gl]
|
||||
all_group = [x.group_id for x in await GroupInfo.get_all_group()]
|
||||
all_group = [x.group_id for x in await GroupInfo.all()]
|
||||
for g in gl:
|
||||
if g not in all_group:
|
||||
await update_member_info(g, False)
|
||||
|
||||
@@ -2,6 +2,7 @@ from nonebot import on_command, on_notice
|
||||
from nonebot.adapters.onebot.v11 import Bot, GroupMessageEvent, GROUP, GroupIncreaseNoticeEvent
|
||||
from ._data_source import update_member_info
|
||||
|
||||
|
||||
__zx_plugin_name__ = "更新群组成员列表 [Admin]"
|
||||
__plugin_usage__ = """
|
||||
usage:
|
||||
|
||||
@@ -3,7 +3,7 @@ from nonebot.typing import T_State
|
||||
from nonebot.adapters import Bot
|
||||
from nonebot.adapters.onebot.v11 import GroupMessageEvent
|
||||
from utils.message_builder import image
|
||||
from .data_source import create_help_image, admin_help_image
|
||||
from .data_source import create_help_image, ADMIN_HELP_IMAGE
|
||||
|
||||
|
||||
__zx_plugin_name__ = '管理帮助 [Admin]'
|
||||
@@ -16,12 +16,12 @@ __plugin_settings__ = {
|
||||
|
||||
admin_help = on_command("管理员帮助", aliases={"管理帮助"}, priority=5, block=True)
|
||||
|
||||
if admin_help_image.exists():
|
||||
admin_help_image.unlink()
|
||||
if ADMIN_HELP_IMAGE.exists():
|
||||
ADMIN_HELP_IMAGE.unlink()
|
||||
|
||||
|
||||
@admin_help.handle()
|
||||
async def _(bot: Bot, event: GroupMessageEvent, state: T_State):
|
||||
if not admin_help_image.exists():
|
||||
if not ADMIN_HELP_IMAGE.exists():
|
||||
await create_help_image()
|
||||
await admin_help.send(image('admin_help_img.png'))
|
||||
await admin_help.send(image(ADMIN_HELP_IMAGE))
|
||||
|
||||
@@ -1,25 +1,25 @@
|
||||
from utils.image_utils import BuildImage
|
||||
from configs.path_config import IMAGE_PATH
|
||||
from services.log import logger
|
||||
from utils.utils import get_matchers
|
||||
from utils.manager import group_manager
|
||||
from nonebot.adapters.onebot.v11 import Bot
|
||||
from nonebot import Driver
|
||||
import nonebot
|
||||
|
||||
from configs.path_config import IMAGE_PATH
|
||||
from nonebot import Driver
|
||||
from services.log import logger
|
||||
from utils.image_template import help_template
|
||||
from utils.image_utils import (BuildImage, build_sort_image, group_image,
|
||||
text2image)
|
||||
from utils.manager import group_manager, plugin_data_manager
|
||||
from utils.manager.models import PluginType
|
||||
|
||||
driver: Driver = nonebot.get_driver()
|
||||
|
||||
background = IMAGE_PATH / "background" / "0.png"
|
||||
|
||||
admin_help_image = IMAGE_PATH / 'admin_help_img.png'
|
||||
ADMIN_HELP_IMAGE = IMAGE_PATH / "admin_help_img.png"
|
||||
|
||||
|
||||
@driver.on_bot_connect
|
||||
async def init_task(bot: Bot = None):
|
||||
async def init_task():
|
||||
if not group_manager.get_task_data():
|
||||
await group_manager.init_group_task()
|
||||
logger.info(f'已成功加载 {len(group_manager.get_task_data())} 个被动技能.')
|
||||
group_manager.load_task()
|
||||
logger.info(f"已成功加载 {len(group_manager.get_task_data())} 个被动技能.")
|
||||
|
||||
|
||||
async def create_help_image():
|
||||
@@ -33,58 +33,55 @@ async def _create_help_image():
|
||||
"""
|
||||
创建管理员帮助图片
|
||||
"""
|
||||
_matchers = get_matchers()
|
||||
_plugin_name_list = []
|
||||
width = 0
|
||||
_plugin_level = {}
|
||||
for matcher in _matchers:
|
||||
_plugin = nonebot.plugin.get_plugin(matcher.plugin_name)
|
||||
_module = _plugin.module
|
||||
if ADMIN_HELP_IMAGE.exists():
|
||||
return
|
||||
plugin_data_ = plugin_data_manager.get_data()
|
||||
image_list = []
|
||||
task_list = []
|
||||
for plugin_data in [plugin_data_[x] for x in plugin_data_]:
|
||||
try:
|
||||
plugin_name = _module.__getattribute__("__zx_plugin_name__")
|
||||
except AttributeError:
|
||||
continue
|
||||
try:
|
||||
if (
|
||||
"[admin]" in plugin_name.lower()
|
||||
and plugin_name not in _plugin_name_list
|
||||
and plugin_name != "管理帮助 [Admin]"
|
||||
):
|
||||
_plugin_name_list.append(plugin_name)
|
||||
plugin_settings = _module.__getattribute__("__plugin_settings__")
|
||||
plugin_des = _module.__getattribute__("__plugin_des__")
|
||||
plugin_cmd = _module.__getattribute__("__plugin_cmd__")
|
||||
plugin_cmd = [x for x in plugin_cmd if "[_superuser]" not in x]
|
||||
admin_level = int(plugin_settings["admin_level"])
|
||||
if _plugin_level.get(admin_level):
|
||||
_plugin_level[admin_level].append(
|
||||
f"[{admin_level}] {plugin_des} -> " + " / ".join(plugin_cmd)
|
||||
)
|
||||
else:
|
||||
_plugin_level[admin_level] = [
|
||||
f"[{admin_level}] {plugin_des} -> " + " / ".join(plugin_cmd)
|
||||
]
|
||||
x = len(f"[{admin_level}] {plugin_des} -> " + " / ".join(plugin_cmd)) * 23
|
||||
width = width if width > x else x
|
||||
except AttributeError:
|
||||
logger.warning(f"获取管理插件 {matcher.plugin_name}: {plugin_name} 设置失败...")
|
||||
help_str = "* 注: ‘*’ 代表可有多个相同参数 ‘?’ 代表可省略该参数 *\n\n" \
|
||||
"[权限等级] 管理员帮助:\n\n"
|
||||
x = list(_plugin_level.keys())
|
||||
x.sort()
|
||||
for level in x:
|
||||
for help_ in _plugin_level[level]:
|
||||
help_str += f"\t{help_}\n\n"
|
||||
help_str += '-----[被动技能开关]-----\n\n'
|
||||
task_data = group_manager.get_task_data()
|
||||
for i, x in enumerate(task_data.keys()):
|
||||
help_str += f'{i+1}.开启/关闭{task_data[x]}\n\n'
|
||||
height = len(help_str.split("\n")) * 33
|
||||
A = BuildImage(width, height, font_size=24)
|
||||
_background = BuildImage(width, height, background=background)
|
||||
await A.apaste(_background, alpha=True)
|
||||
await A.atext((150, 110), help_str)
|
||||
await A.asave(admin_help_image)
|
||||
logger.info(f'已成功加载 {len(_plugin_name_list)} 条管理员命令')
|
||||
|
||||
|
||||
usage = None
|
||||
if plugin_data.plugin_type == PluginType.ADMIN and plugin_data.usage:
|
||||
usage = await text2image(
|
||||
plugin_data.usage, padding=5, color=(204, 196, 151)
|
||||
)
|
||||
if usage:
|
||||
await usage.acircle_corner()
|
||||
level = 5
|
||||
if plugin_data.plugin_setting:
|
||||
level = plugin_data.plugin_setting.level or level
|
||||
image = await help_template(plugin_data.name + f"[{level}]", usage)
|
||||
image_list.append(image)
|
||||
if plugin_data.task:
|
||||
for x in plugin_data.task.keys():
|
||||
task_list.append(plugin_data.task[x])
|
||||
except Exception as e:
|
||||
logger.warning(
|
||||
f"获取群管理员插件 {plugin_data.model}: {plugin_data.name} 设置失败... {type(e)}:{e}"
|
||||
)
|
||||
task_str = "\n".join(task_list)
|
||||
task_str = "通过 开启/关闭 来控制群被动\n----------\n" + task_str
|
||||
task_image = await text2image(task_str, padding=5, color=(204, 196, 151))
|
||||
task_image = await help_template("被动任务", task_image)
|
||||
image_list.append(task_image)
|
||||
image_group, _ = group_image(image_list)
|
||||
A = await build_sort_image(image_group, color="#f9f6f2", padding_top=180)
|
||||
await A.apaste(
|
||||
BuildImage(0, 0, font="CJGaoDeGuo.otf", plain_text="群管理员帮助", font_size=50),
|
||||
(50, 30),
|
||||
True,
|
||||
)
|
||||
await A.apaste(
|
||||
BuildImage(
|
||||
0,
|
||||
0,
|
||||
font="CJGaoDeGuo.otf",
|
||||
plain_text="注: ‘*’ 代表可有多个相同参数 ‘?’ 代表可省略该参数",
|
||||
font_size=30,
|
||||
font_color="red",
|
||||
),
|
||||
(50, 90),
|
||||
True,
|
||||
)
|
||||
await A.asave(ADMIN_HELP_IMAGE)
|
||||
logger.info(f"已成功加载 {len(image_list)} 条管理员命令")
|
||||
|
||||
@@ -1,48 +1,52 @@
|
||||
from utils.message_builder import image
|
||||
from utils.utils import scheduler, get_bot
|
||||
from nonebot import on_message
|
||||
from services.log import logger
|
||||
from models.group_info import GroupInfo
|
||||
from models.friend_user import FriendUser
|
||||
from nonebot.adapters.onebot.v11.exception import ActionFailed
|
||||
from configs.config import NICKNAME, Config
|
||||
from utils.manager import group_manager
|
||||
from pathlib import Path
|
||||
import shutil
|
||||
from pathlib import Path
|
||||
from typing import List
|
||||
|
||||
import nonebot
|
||||
from nonebot import on_message
|
||||
from nonebot.adapters.onebot.v11 import ActionFailed
|
||||
|
||||
from configs.config import NICKNAME, Config
|
||||
from configs.path_config import IMAGE_PATH
|
||||
from models.friend_user import FriendUser
|
||||
from models.group_info import GroupInfo
|
||||
from services.log import logger
|
||||
from utils.message_builder import image
|
||||
from utils.utils import broadcast_group, get_bot, scheduler
|
||||
|
||||
__zx_plugin_name__ = "定时任务相关 [Hidden]"
|
||||
__plugin_version__ = 0.1
|
||||
__plugin_author__ = "HibiKier"
|
||||
__plugin_task__ = {'zwa': '早晚安'}
|
||||
__plugin_task__ = {"zwa": "早晚安"}
|
||||
|
||||
|
||||
Config.add_plugin_config(
|
||||
"_task",
|
||||
"DEFAULT_ZWA",
|
||||
True,
|
||||
help_="被动 早晚安 进群默认开关状态",
|
||||
default_value=True,
|
||||
"_task", "DEFAULT_ZWA", True, help_="被动 早晚安 进群默认开关状态", default_value=True, type=bool
|
||||
)
|
||||
|
||||
Config.add_plugin_config(
|
||||
"_backup",
|
||||
"BACKUP_FLAG",
|
||||
True,
|
||||
help_="是否开启文件备份",
|
||||
default_value=True
|
||||
"_backup", "BACKUP_FLAG", True, help_="是否开启文件备份", default_value=True, type=bool
|
||||
)
|
||||
|
||||
Config.add_plugin_config(
|
||||
"_backup",
|
||||
"BACKUP_DIR_OR_FILE",
|
||||
['data/black_word', 'data/configs', 'data/statistics', 'data/word_bank', 'data/manager', 'configs'],
|
||||
[
|
||||
"data/black_word",
|
||||
"data/configs",
|
||||
"data/statistics",
|
||||
"data/word_bank",
|
||||
"data/manager",
|
||||
"configs",
|
||||
],
|
||||
name="文件备份",
|
||||
help_="备份的文件夹或文件",
|
||||
default_value=[]
|
||||
default_value=[],
|
||||
type=List[str],
|
||||
)
|
||||
|
||||
|
||||
cx = on_message(priority=9, block=False)
|
||||
cx = on_message(priority=9999, block=False, rule=lambda: False)
|
||||
|
||||
|
||||
# 早上好
|
||||
@@ -52,18 +56,9 @@ cx = on_message(priority=9, block=False)
|
||||
minute=1,
|
||||
)
|
||||
async def _():
|
||||
try:
|
||||
bot = get_bot()
|
||||
gl = await bot.get_group_list()
|
||||
gl = [g["group_id"] for g in gl]
|
||||
for g in gl:
|
||||
result = image("zao.jpg", "zhenxun")
|
||||
try:
|
||||
await bot.send_group_msg(group_id=g, message="[[_task|zwa]]早上好" + result)
|
||||
except ActionFailed:
|
||||
logger.warning(f"{g} 群被禁言中,无法发送早安")
|
||||
except Exception as e:
|
||||
logger.error(f"早晚安错误 e:{e}")
|
||||
img = image(IMAGE_PATH / "zhenxun" / "zao.jpg")
|
||||
await broadcast_group("[[_task|zwa]]早上好" + img, log_cmd="被动早晚安")
|
||||
logger.info("每日早安发送")
|
||||
|
||||
|
||||
# 睡觉了
|
||||
@@ -73,20 +68,11 @@ async def _():
|
||||
minute=59,
|
||||
)
|
||||
async def _():
|
||||
try:
|
||||
bot = get_bot()
|
||||
gl = await bot.get_group_list()
|
||||
gl = [g["group_id"] for g in gl]
|
||||
for g in gl:
|
||||
result = image("sleep.jpg", "zhenxun")
|
||||
try:
|
||||
await bot.send_group_msg(
|
||||
group_id=g, message=f"[[_task|zwa]]{NICKNAME}要睡觉了,你们也要早点睡呀" + result
|
||||
)
|
||||
except ActionFailed:
|
||||
logger.warning(f"{g} 群被禁言中,无法发送晚安")
|
||||
except Exception as e:
|
||||
logger.error(f"早晚安错误 e:{e}")
|
||||
img = image(IMAGE_PATH / "zhenxun" / "sleep.jpg")
|
||||
await broadcast_group(
|
||||
f"[[_task|zwa]]{NICKNAME}要睡觉了,你们也要早点睡呀" + img, log_cmd="被动早晚安"
|
||||
)
|
||||
logger.info("每日晚安发送")
|
||||
|
||||
|
||||
# 自动更新群组信息
|
||||
@@ -96,21 +82,27 @@ async def _():
|
||||
minute=1,
|
||||
)
|
||||
async def _():
|
||||
try:
|
||||
bot = get_bot()
|
||||
gl = await bot.get_group_list()
|
||||
gl = [g["group_id"] for g in gl]
|
||||
for g in gl:
|
||||
group_info = await bot.get_group_info(group_id=g)
|
||||
await GroupInfo.add_group_info(
|
||||
group_info["group_id"],
|
||||
group_info["group_name"],
|
||||
group_info["max_member_count"],
|
||||
group_info["member_count"],
|
||||
)
|
||||
logger.info(f"自动更新群组 {g} 信息成功")
|
||||
except Exception as e:
|
||||
logger.error(f"自动更新群组信息错误 e:{e}")
|
||||
bots = nonebot.get_bots()
|
||||
_used_group = []
|
||||
for bot in bots.values():
|
||||
try:
|
||||
group_list = await bot.get_group_list()
|
||||
gl = [g["group_id"] for g in group_list if g["group_id"] not in _used_group]
|
||||
for g in gl:
|
||||
_used_group.append(g)
|
||||
group_info = await bot.get_group_info(group_id=g)
|
||||
await GroupInfo.update_or_create(
|
||||
group_id=group_info["group_id"],
|
||||
defaults={
|
||||
"group_name": group_info["group_name"],
|
||||
"max_member_count": group_info["max_member_count"],
|
||||
"member_count": group_info["member_count"],
|
||||
"group_flag": 1,
|
||||
},
|
||||
)
|
||||
logger.info(f"自动更新群组信息成功", group_id=g)
|
||||
except Exception as e:
|
||||
logger.error(f"Bot: {bot.self_id} 自动更新群组信息", e=e)
|
||||
|
||||
|
||||
# 自动更新好友信息
|
||||
@@ -124,10 +116,10 @@ async def _():
|
||||
bot = get_bot()
|
||||
fl = await bot.get_friend_list()
|
||||
for f in fl:
|
||||
if await FriendUser.add_friend_info(f["user_id"], f["nickname"]):
|
||||
logger.info(f'自动更新好友 {f["user_id"]} 信息成功')
|
||||
else:
|
||||
logger.warning(f'自动更新好友 {f["user_id"]} 信息失败')
|
||||
await FriendUser.create(user_id=f["user_id"], user_name=f["nickname"])
|
||||
logger.info(f'自动更新好友 {f["user_id"]} 信息成功')
|
||||
# else:
|
||||
# logger.warning(f'自动更新好友 {f["user_id"]} 信息失败')
|
||||
except Exception as e:
|
||||
logger.error(f"自动更新群组信息错误 e:{e}")
|
||||
|
||||
@@ -140,7 +132,7 @@ async def _():
|
||||
)
|
||||
async def _():
|
||||
if Config.get_config("_backup", "BACKUP_FLAG"):
|
||||
_backup_path = Path() / 'backup'
|
||||
_backup_path = Path() / "backup"
|
||||
_backup_path.mkdir(exist_ok=True, parents=True)
|
||||
for x in Config.get_config("_backup", "BACKUP_DIR_OR_FILE"):
|
||||
try:
|
||||
@@ -155,12 +147,13 @@ async def _():
|
||||
if _p.exists():
|
||||
_p.unlink()
|
||||
shutil.copy(x, _p)
|
||||
logger.info(f'已完成自动备份:{x}')
|
||||
logger.info(f"已完成自动备份:{x}")
|
||||
except Exception as e:
|
||||
logger.error(f"自动备份文件 {x} 发生错误 {type(e)}:{e}")
|
||||
|
||||
|
||||
# 一次性任务
|
||||
|
||||
|
||||
# 固定时间触发,仅触发一次:
|
||||
#
|
||||
# from datetime import datetime
|
||||
|
||||
@@ -1,15 +1,22 @@
|
||||
from typing import Tuple
|
||||
from typing import List
|
||||
|
||||
from nonebot import on_command
|
||||
from nonebot.adapters.onebot.v11 import (
|
||||
Bot,
|
||||
GroupMessageEvent,
|
||||
Message,
|
||||
MessageEvent,
|
||||
PrivateMessageEvent,
|
||||
)
|
||||
from nonebot.params import CommandArg
|
||||
from nonebot.permission import SUPERUSER
|
||||
|
||||
from configs.config import NICKNAME, Config
|
||||
from models.ban_user import BanUser
|
||||
from models.level_user import LevelUser
|
||||
from nonebot import on_command
|
||||
from nonebot.adapters.onebot.v11 import (Bot, GroupMessageEvent, Message,
|
||||
MessageEvent, PrivateMessageEvent)
|
||||
from nonebot.params import Command, CommandArg
|
||||
from nonebot.permission import SUPERUSER
|
||||
from services.log import logger
|
||||
from utils.utils import get_message_at, is_number
|
||||
from utils.depends import AtList, OneCommand
|
||||
from utils.utils import is_number
|
||||
|
||||
from .data_source import a_ban, parse_ban_time
|
||||
|
||||
@@ -49,6 +56,7 @@ __plugin_configs__ = {
|
||||
"value": 5,
|
||||
"help": "ban/unban所需要的管理员权限等级",
|
||||
"default_value": 5,
|
||||
"type": int
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,46 +75,38 @@ super_ban = on_command("b了", permission=SUPERUSER, priority=5, block=True)
|
||||
async def _(
|
||||
bot: Bot,
|
||||
event: GroupMessageEvent,
|
||||
cmd: Tuple[str, ...] = Command(),
|
||||
cmd: str = OneCommand(),
|
||||
arg: Message = CommandArg(),
|
||||
at_list: List[int] = AtList(),
|
||||
):
|
||||
cmd = cmd[0]
|
||||
result = ""
|
||||
qq = get_message_at(event.json())
|
||||
if qq:
|
||||
qq = qq[0]
|
||||
user_name = await bot.get_group_member_info(group_id=event.group_id, user_id=qq)
|
||||
user_name = user_name["card"] or user_name["nickname"]
|
||||
if at_list:
|
||||
qq = at_list[0]
|
||||
user = await bot.get_group_member_info(group_id=event.group_id, user_id=qq)
|
||||
user_name = user["card"] or user["nickname"]
|
||||
msg = arg.extract_plain_text().strip()
|
||||
time = parse_ban_time(msg)
|
||||
if isinstance(time, str):
|
||||
await ban.finish(time, at_sender=True)
|
||||
user_level = await LevelUser.get_user_level(event.user_id, event.group_id)
|
||||
is_not_superuser = str(event.user_id) not in bot.config.superusers
|
||||
if cmd in [".ban", "/ban"]:
|
||||
if (
|
||||
await LevelUser.get_user_level(event.user_id, event.group_id)
|
||||
<= await LevelUser.get_user_level(qq, event.group_id)
|
||||
and str(event.user_id) not in bot.config.superusers
|
||||
):
|
||||
at_user_level = await LevelUser.get_user_level(qq, event.group_id)
|
||||
if user_level <= at_user_level and is_not_superuser:
|
||||
await ban.finish(
|
||||
f"您的权限等级比对方低或相等, {NICKNAME}不能为您使用此功能!",
|
||||
at_sender=True,
|
||||
)
|
||||
logger.info(f"用户封禁 时长: {time}", cmd, event.user_id, event.group_id, qq)
|
||||
result = await a_ban(qq, time, user_name, event)
|
||||
else:
|
||||
if (
|
||||
await BanUser.check_ban_level(
|
||||
qq, await LevelUser.get_user_level(event.user_id, event.group_id)
|
||||
)
|
||||
and str(event.user_id) not in bot.config.superusers
|
||||
):
|
||||
if await BanUser.check_ban_level(qq, user_level) and is_not_superuser:
|
||||
await ban.finish(
|
||||
f"ban掉 {user_name} 的管理员权限比您高,无法进行unban", at_sender=True
|
||||
)
|
||||
if await BanUser.unban(qq):
|
||||
logger.info(
|
||||
f"USER {event.user_id} GROUP {event.group_id} 将 USER {qq} 解禁"
|
||||
)
|
||||
result = f"已经把 {user_name} 从黑名单中删除了!"
|
||||
logger.info(f"解除用户封禁", cmd, event.user_id, event.group_id, qq)
|
||||
result = f"已经将 {user_name} 从黑名单中删除了!"
|
||||
else:
|
||||
result = f"{user_name} 不在黑名单!"
|
||||
else:
|
||||
@@ -118,53 +118,62 @@ async def _(
|
||||
async def _(
|
||||
bot: Bot,
|
||||
event: PrivateMessageEvent,
|
||||
cmd: Tuple[str, ...] = Command(),
|
||||
cmd: str = OneCommand(),
|
||||
arg: Message = CommandArg(),
|
||||
):
|
||||
cmd = cmd[0]
|
||||
msg = arg.extract_plain_text().strip()
|
||||
if msg:
|
||||
if str(event.user_id) in bot.config.superusers:
|
||||
if is_number(arg.extract_plain_text().strip().split()[0]):
|
||||
qq = int(msg[0])
|
||||
msg = msg[1:]
|
||||
if cmd in [".ban", "/ban"]:
|
||||
time = parse_ban_time(msg)
|
||||
if isinstance(time, str):
|
||||
await ban.finish(time)
|
||||
result = await a_ban(qq, time, str(qq), event, 9)
|
||||
else:
|
||||
if await BanUser.unban(qq):
|
||||
logger.info(f"USER {event.user_id} 将 USER {qq} 解禁")
|
||||
result = f"已经把 {qq} 从黑名单中删除了!"
|
||||
else:
|
||||
result = f"{qq} 不在黑名单!"
|
||||
await ban.send(result)
|
||||
if msg and str(event.user_id) in bot.config.superusers:
|
||||
msg_split = msg.split()
|
||||
if msg_split and is_number(msg_split[0]):
|
||||
qq = int(msg_split[0])
|
||||
param = msg_split[1:]
|
||||
if cmd in [".ban", "/ban"]:
|
||||
time = parse_ban_time(" ".join(param))
|
||||
if isinstance(time, str):
|
||||
logger.info(time, cmd, event.user_id, target=qq)
|
||||
await ban.finish(time)
|
||||
result = await a_ban(qq, time, str(qq), event, 9)
|
||||
else:
|
||||
await ban.finish(
|
||||
"qq号必须是数字!\n格式:.ban [qq] [hour]? [minute]?", at_sender=True
|
||||
)
|
||||
if await BanUser.unban(qq):
|
||||
result = f"已经把 {qq} 从黑名单中删除了!"
|
||||
else:
|
||||
result = f"{qq} 不在黑名单!"
|
||||
await ban.send(result)
|
||||
logger.info(result, cmd, event.user_id, target=qq)
|
||||
else:
|
||||
await ban.send("参数不正确!\n格式:.ban [qq] [hour]? [minute]?", at_sender=True)
|
||||
|
||||
|
||||
@super_ban.handle()
|
||||
async def _(bot: Bot, event: MessageEvent, arg: Message = CommandArg()):
|
||||
async def _(
|
||||
bot: Bot,
|
||||
event: MessageEvent,
|
||||
cmd: str = OneCommand(),
|
||||
arg: Message = CommandArg(),
|
||||
at_list: List[int] = AtList(),
|
||||
):
|
||||
user_name = ""
|
||||
qq = None
|
||||
if isinstance(event, GroupMessageEvent):
|
||||
qq = get_message_at(event.json())
|
||||
if qq:
|
||||
qq = qq[0]
|
||||
if at_list:
|
||||
qq = at_list[0]
|
||||
user = await bot.get_group_member_info(group_id=event.group_id, user_id=qq)
|
||||
user_name = user["card"] or user["nickname"]
|
||||
else:
|
||||
qq = arg.extract_plain_text().strip()
|
||||
if not is_number(qq):
|
||||
msg = arg.extract_plain_text().strip()
|
||||
if not is_number(msg):
|
||||
await super_ban.finish("对象qq必须为纯数字...")
|
||||
qq = int(qq)
|
||||
user_name = qq
|
||||
qq = int(msg)
|
||||
user_name = msg
|
||||
if qq:
|
||||
if not await BanUser.ban(qq, 10, 99999999):
|
||||
await BanUser.unban(qq)
|
||||
await BanUser.ban(qq, 10, 99999999)
|
||||
await BanUser.ban(qq, 10, 99999999)
|
||||
await ban.send(f"已将 {user_name} 拉入黑名单!")
|
||||
logger.info(
|
||||
f"已将 {user_name} 拉入黑名单!",
|
||||
cmd,
|
||||
event.user_id,
|
||||
event.group_id if isinstance(event, GroupMessageEvent) else None,
|
||||
qq,
|
||||
)
|
||||
else:
|
||||
await super_ban.send("需要添加被super ban的对象,可以使用at或者指定qq..")
|
||||
await super_ban.send("需要提供被super ban的对象,可以使用at或者指定qq...")
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
from nonebot.adapters.onebot.v11 import MessageEvent, GroupMessageEvent
|
||||
from typing import Optional, Union
|
||||
|
||||
from nonebot.adapters.onebot.v11 import GroupMessageEvent, MessageEvent
|
||||
|
||||
from configs.config import NICKNAME
|
||||
from models.level_user import LevelUser
|
||||
from utils.utils import is_number
|
||||
from models.ban_user import BanUser
|
||||
from models.level_user import LevelUser
|
||||
from services.log import logger
|
||||
from typing import Union
|
||||
from utils.utils import is_number
|
||||
|
||||
|
||||
def parse_ban_time(msg: str) -> Union[int, str]:
|
||||
@@ -12,20 +14,32 @@ def parse_ban_time(msg: str) -> Union[int, str]:
|
||||
解析ban时长
|
||||
:param msg: 文本消息
|
||||
"""
|
||||
if not msg:
|
||||
return -1
|
||||
msg = msg.split()
|
||||
if len(msg) == 1:
|
||||
if not is_number(msg[0].strip()):
|
||||
return "参数必须是数字!"
|
||||
return int(msg[0]) * 60 * 60
|
||||
else:
|
||||
if not is_number(msg[0].strip()) or not is_number(msg[1].strip()):
|
||||
return "参数必须是数字!"
|
||||
return int(msg[0]) * 60 * 60 + int(msg[1]) * 60
|
||||
try:
|
||||
if not msg:
|
||||
return -1
|
||||
msg_split = msg.split()
|
||||
if len(msg_split) == 1:
|
||||
if not is_number(msg_split[0].strip()):
|
||||
return "参数必须是数字!"
|
||||
return int(msg_split[0]) * 60 * 60
|
||||
else:
|
||||
if not is_number(msg_split[0].strip()) or not is_number(
|
||||
msg_split[1].strip()
|
||||
):
|
||||
return "参数必须是数字!"
|
||||
return int(msg_split[0]) * 60 * 60 + int(msg_split[1]) * 60
|
||||
except ValueError as e:
|
||||
logger.error("解析ban时长错误", ".ban", e=e)
|
||||
return "时长不可以带小数点!"
|
||||
|
||||
|
||||
async def a_ban(qq: int, time: int, user_name: str, event: MessageEvent, ban_level: int = None) -> str:
|
||||
async def a_ban(
|
||||
qq: int,
|
||||
time: int,
|
||||
user_name: str,
|
||||
event: MessageEvent,
|
||||
ban_level: Optional[int] = None,
|
||||
) -> str:
|
||||
"""
|
||||
ban
|
||||
:param qq: qq
|
||||
@@ -34,11 +48,15 @@ async def a_ban(qq: int, time: int, user_name: str, event: MessageEvent, ban_lev
|
||||
:param event: event
|
||||
:param ban_level: ban级别
|
||||
"""
|
||||
group_id = None
|
||||
if isinstance(event, GroupMessageEvent):
|
||||
group_id = event.group_id
|
||||
ban_level = await LevelUser.get_user_level(event.user_id, event.group_id)
|
||||
if not ban_level:
|
||||
return "未查询到ban级用户权限"
|
||||
if await BanUser.ban(qq, ban_level, time):
|
||||
logger.info(
|
||||
f"USER {event.user_id} GROUP {event.group_id} 将 USER {qq} 封禁 时长 {time / 60} 分钟"
|
||||
f"将 [Target]({qq})封禁 时长 {time / 60} 分钟", ".ban", event.user_id, group_id
|
||||
)
|
||||
result = f"已经将 {user_name} 加入{NICKNAME}的黑名单了!"
|
||||
if time != -1:
|
||||
@@ -46,24 +64,14 @@ async def a_ban(qq: int, time: int, user_name: str, event: MessageEvent, ban_lev
|
||||
else:
|
||||
result += f"将在 ∞ 分钟后解封"
|
||||
else:
|
||||
time = await BanUser.check_ban_time(qq)
|
||||
if is_number(time):
|
||||
time = abs(int(time))
|
||||
if time < 60:
|
||||
time = str(time) + " 秒"
|
||||
ban_time = await BanUser.check_ban_time(qq)
|
||||
if isinstance(ban_time, int):
|
||||
ban_time = abs(float(ban_time))
|
||||
if ban_time < 60:
|
||||
ban_time = str(ban_time) + " 秒"
|
||||
else:
|
||||
time = str(int(time / 60)) + " 分钟"
|
||||
ban_time = str(int(ban_time / 60)) + " 分钟"
|
||||
else:
|
||||
time += " 分钟"
|
||||
result = f"{user_name} 已在黑名单!预计 {time}后解封"
|
||||
ban_time += " 分钟"
|
||||
result = f"{user_name} 已在黑名单!预计 {ban_time}后解封"
|
||||
return result
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
from nonebot.adapters.onebot.v11 import Bot, Message, MessageEvent
|
||||
from nonebot import on_command
|
||||
from nonebot.permission import SUPERUSER
|
||||
from nonebot.params import CommandArg
|
||||
from utils.utils import get_message_img
|
||||
from services.log import logger
|
||||
from utils.message_builder import image
|
||||
from utils.manager import group_manager
|
||||
from configs.config import Config
|
||||
import asyncio
|
||||
|
||||
from nonebot import on_command
|
||||
from nonebot.adapters.onebot.v11 import Bot, Message, MessageEvent
|
||||
from nonebot.params import CommandArg
|
||||
from nonebot.permission import SUPERUSER
|
||||
|
||||
from configs.config import Config
|
||||
from services.log import logger
|
||||
from utils.manager import group_manager
|
||||
from utils.message_builder import image
|
||||
from utils.utils import get_message_img
|
||||
|
||||
__zx_plugin_name__ = "广播 [Superuser]"
|
||||
__plugin_usage__ = """
|
||||
@@ -28,6 +29,7 @@ Config.add_plugin_config(
|
||||
True,
|
||||
help_="被动 广播 进群默认开关状态",
|
||||
default_value=True,
|
||||
type=bool,
|
||||
)
|
||||
|
||||
broadcast = on_command("广播-", priority=1, permission=SUPERUSER, block=True)
|
||||
@@ -40,11 +42,10 @@ async def _(bot: Bot, event: MessageEvent, arg: Message = CommandArg()):
|
||||
rst = ""
|
||||
for img in img_list:
|
||||
rst += image(img)
|
||||
gl = await bot.get_group_list()
|
||||
gl = [
|
||||
g["group_id"]
|
||||
for g in gl
|
||||
if await group_manager.check_group_task_status(g["group_id"], "broadcast")
|
||||
for g in await bot.get_group_list()
|
||||
if group_manager.check_group_task_status(g["group_id"], "broadcast")
|
||||
]
|
||||
g_cnt = len(gl)
|
||||
cnt = 0
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import nonebot
|
||||
from pathlib import Path
|
||||
|
||||
nonebot.load_plugins("basic_plugins/chat_history")
|
||||
nonebot.load_plugins(str(Path(__file__).parent.resolve()))
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
from configs.config import Config
|
||||
from models.chat_history import ChatHistory
|
||||
from nonebot import on_message
|
||||
from nonebot.adapters.onebot.v11 import GroupMessageEvent, MessageEvent
|
||||
|
||||
from configs.config import Config
|
||||
from models.chat_history import ChatHistory
|
||||
from services.log import logger
|
||||
from utils.depends import PlaintText
|
||||
from utils.utils import scheduler
|
||||
|
||||
from ._rule import rule
|
||||
|
||||
__zx_plugin_name__ = "消息存储 [Hidden]"
|
||||
@@ -11,21 +15,51 @@ __plugin_author__ = "HibiKier"
|
||||
|
||||
|
||||
Config.add_plugin_config(
|
||||
"chat_history", "FLAG", True, help_="是否开启消息自从存储", name="消息存储", default_value=True
|
||||
"chat_history",
|
||||
"FLAG",
|
||||
True,
|
||||
help_="是否开启消息自从存储",
|
||||
name="消息存储",
|
||||
default_value=True,
|
||||
type=bool,
|
||||
)
|
||||
|
||||
|
||||
chat_history = on_message(rule=rule, priority=1, block=False)
|
||||
|
||||
|
||||
TEMP_LIST = []
|
||||
|
||||
|
||||
@chat_history.handle()
|
||||
async def _(event: MessageEvent):
|
||||
async def _(event: MessageEvent, msg: str = PlaintText()):
|
||||
group_id = None
|
||||
if isinstance(event, GroupMessageEvent):
|
||||
await ChatHistory.add_chat_msg(
|
||||
event.user_id, event.group_id, str(event.get_message())
|
||||
)
|
||||
else:
|
||||
await ChatHistory.add_chat_msg(event.user_id, None, str(event.get_message()))
|
||||
group_id = event.group_id
|
||||
TEMP_LIST.append(
|
||||
{
|
||||
"user_qq": event.user_id,
|
||||
"group_id": group_id,
|
||||
"text": str(event.get_message()),
|
||||
"plain_text": msg,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@scheduler.scheduled_job(
|
||||
"interval",
|
||||
minutes=1,
|
||||
)
|
||||
async def _():
|
||||
try:
|
||||
message_list = TEMP_LIST.copy()
|
||||
TEMP_LIST.clear()
|
||||
if message_list:
|
||||
model_list = [ChatHistory(**x) for x in message_list]
|
||||
await ChatHistory.bulk_create(model_list)
|
||||
logger.debug(f"批量添加聊天记录 {len(message_list)} 条", "定时任务")
|
||||
except Exception as e:
|
||||
logger.error(f"定时批量添加聊天记录", "定时任务", e=e)
|
||||
|
||||
|
||||
# @test.handle()
|
||||
|
||||
@@ -1,26 +1,27 @@
|
||||
from datetime import datetime, timedelta
|
||||
from typing import Any, Tuple
|
||||
|
||||
import pytz
|
||||
from models.chat_history import ChatHistory
|
||||
from models.group_member_info import GroupInfoUser
|
||||
from nonebot import on_regex
|
||||
from nonebot.adapters.onebot.v11 import GroupMessageEvent
|
||||
from nonebot.params import RegexGroup
|
||||
from utils.image_utils import BuildImage, text2image
|
||||
from utils.utils import is_number
|
||||
from utils.message_builder import image
|
||||
from typing import Tuple, Any
|
||||
|
||||
from models.chat_history import ChatHistory
|
||||
from models.group_member_info import GroupInfoUser
|
||||
from utils.image_utils import BuildImage, text2image
|
||||
from utils.message_builder import image
|
||||
from utils.utils import is_number
|
||||
|
||||
__zx_plugin_name__ = "消息统计"
|
||||
__plugin_usage__ = """
|
||||
usage:
|
||||
发言记录统计
|
||||
regex:(周|月)?消息排行(des|DES)?(n=[0-9]{1,2})?
|
||||
regex:(周|月|日)?消息排行(des|DES)?(n=[0-9]{1,2})?
|
||||
指令:
|
||||
消息统计?(des)?(n=?)
|
||||
周消息统计?(des)?(n=?)
|
||||
月消息统计?(des)?(n=?)
|
||||
日消息统计?(des)?(n=?)
|
||||
示例:
|
||||
消息统计
|
||||
消息统计des
|
||||
@@ -28,11 +29,7 @@ usage:
|
||||
消息统计n=15
|
||||
""".strip()
|
||||
__plugin_des__ = "发言消息排行"
|
||||
__plugin_cmd__ = [
|
||||
"消息统计",
|
||||
"周消息统计",
|
||||
"月消息统计"
|
||||
]
|
||||
__plugin_cmd__ = ["消息统计", "周消息统计", "月消息统计", "日消息统计"]
|
||||
__plugin_type__ = ("数据统计", 1)
|
||||
__plugin_version__ = 0.1
|
||||
__plugin_author__ = "HibiKier"
|
||||
@@ -42,7 +39,9 @@ __plugin_settings__ = {
|
||||
}
|
||||
|
||||
|
||||
msg_handler = on_regex(r"(周|月)?消息统计(des|DES)?(n=[0-9]{1,2})?", priority=5, block=True)
|
||||
msg_handler = on_regex(
|
||||
r"^(周|月|日)?消息统计(des|DES)?(n=[0-9]{1,2})?$", priority=5, block=True
|
||||
)
|
||||
|
||||
|
||||
@msg_handler.handle()
|
||||
@@ -53,10 +52,16 @@ async def _(event: GroupMessageEvent, reg_group: Tuple[Any, ...] = RegexGroup())
|
||||
num = num.split("=")[-1] if num else 10
|
||||
if num and is_number(num) and 10 < int(num) < 50:
|
||||
num = int(num)
|
||||
if date in ["周"]:
|
||||
date_scope = (datetime.now() - timedelta(days=7), datetime.now())
|
||||
time_now = datetime.now()
|
||||
zero_today = time_now - timedelta(
|
||||
hours=time_now.hour, minutes=time_now.minute, seconds=time_now.second
|
||||
)
|
||||
if date in ["日"]:
|
||||
date_scope = (zero_today, time_now)
|
||||
elif date in ["周"]:
|
||||
date_scope = (time_now - timedelta(days=7), time_now)
|
||||
elif date in ["月"]:
|
||||
date_scope = (datetime.now() - timedelta(days=30), datetime.now())
|
||||
date_scope = (time_now - timedelta(days=30), time_now)
|
||||
if rank_data := await ChatHistory.get_group_msg_rank(
|
||||
gid, num, order or "DESC", date_scope
|
||||
):
|
||||
@@ -64,9 +69,9 @@ async def _(event: GroupMessageEvent, reg_group: Tuple[Any, ...] = RegexGroup())
|
||||
num_str = "发言次数:\n\n"
|
||||
idx = 1
|
||||
for uid, num in rank_data:
|
||||
try:
|
||||
user_name = (await GroupInfoUser.get_member_info(uid, gid)).user_name
|
||||
except AttributeError:
|
||||
if user := await GroupInfoUser.filter(user_qq=uid, group_id=gid).first():
|
||||
user_name = user.user_name
|
||||
else:
|
||||
user_name = uid
|
||||
name += f"\t{idx}.{user_name} \n\n"
|
||||
num_str += f"\t{num}\n\n"
|
||||
@@ -79,7 +84,7 @@ async def _(event: GroupMessageEvent, reg_group: Tuple[Any, ...] = RegexGroup())
|
||||
pytz.timezone("Asia/Shanghai")
|
||||
).replace(microsecond=0)
|
||||
else:
|
||||
date_scope = datetime.now().replace(microsecond=0)
|
||||
date_scope = time_now.replace(microsecond=0)
|
||||
date_str = f"日期:{date_scope} - 至今"
|
||||
else:
|
||||
date_str = f"日期:{date_scope[0].replace(microsecond=0)} - {date_scope[1].replace(microsecond=0)}"
|
||||
|
||||
@@ -1,29 +1,27 @@
|
||||
from nonebot import on_notice, on_request
|
||||
from configs.path_config import IMAGE_PATH, DATA_PATH
|
||||
from utils.message_builder import image
|
||||
from models.group_member_info import GroupInfoUser
|
||||
from datetime import datetime
|
||||
from services.log import logger
|
||||
from nonebot.adapters.onebot.v11 import (
|
||||
Bot,
|
||||
GroupIncreaseNoticeEvent,
|
||||
GroupDecreaseNoticeEvent,
|
||||
)
|
||||
from nonebot.adapters.onebot.v11.exception import ActionFailed
|
||||
from utils.manager import group_manager, plugins2settings_manager, requests_manager
|
||||
from configs.config import NICKNAME
|
||||
from models.group_info import GroupInfo
|
||||
from utils.utils import FreqLimiter
|
||||
from configs.config import Config
|
||||
from pathlib import Path
|
||||
import random
|
||||
import os
|
||||
import random
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
|
||||
try:
|
||||
import ujson as json
|
||||
except ModuleNotFoundError:
|
||||
import json
|
||||
import ujson as json
|
||||
from nonebot import on_notice, on_request
|
||||
from nonebot.adapters.onebot.v11 import (
|
||||
ActionFailed,
|
||||
Bot,
|
||||
GroupDecreaseNoticeEvent,
|
||||
GroupIncreaseNoticeEvent,
|
||||
)
|
||||
|
||||
from configs.config import NICKNAME, Config
|
||||
from configs.path_config import DATA_PATH, IMAGE_PATH
|
||||
from models.group_info import GroupInfo
|
||||
from models.group_member_info import GroupInfoUser
|
||||
from models.level_user import LevelUser
|
||||
from services.log import logger
|
||||
from utils.depends import GetConfig
|
||||
from utils.manager import group_manager, plugins2settings_manager, requests_manager
|
||||
from utils.message_builder import image
|
||||
from utils.utils import FreqLimiter
|
||||
|
||||
__zx_plugin_name__ = "群事件处理 [Hidden]"
|
||||
__plugin_version__ = 0.1
|
||||
@@ -33,10 +31,10 @@ Config.add_plugin_config(
|
||||
"invite_manager", "message", f"请不要未经同意就拉{NICKNAME}入群!告辞!", help_="强制拉群后进群回复的内容.."
|
||||
)
|
||||
Config.add_plugin_config(
|
||||
"invite_manager", "flag", True, help_="被强制拉群后是否直接退出", default_value=True
|
||||
"invite_manager", "flag", True, help_="被强制拉群后是否直接退出", default_value=True, type=bool
|
||||
)
|
||||
Config.add_plugin_config(
|
||||
"invite_manager", "welcome_msg_cd", 5, help_="群欢迎消息cd", default_value=5
|
||||
"invite_manager", "welcome_msg_cd", 5, help_="群欢迎消息cd", default_value=5, type=int
|
||||
)
|
||||
Config.add_plugin_config(
|
||||
"_task",
|
||||
@@ -44,6 +42,7 @@ Config.add_plugin_config(
|
||||
True,
|
||||
help_="被动 进群欢迎 进群默认开关状态",
|
||||
default_value=True,
|
||||
type=bool,
|
||||
)
|
||||
Config.add_plugin_config(
|
||||
"_task",
|
||||
@@ -51,10 +50,11 @@ Config.add_plugin_config(
|
||||
True,
|
||||
help_="被动 退群提醒 进群默认开关状态",
|
||||
default_value=True,
|
||||
type=bool,
|
||||
)
|
||||
|
||||
|
||||
_flmt = FreqLimiter(Config.get_config("invite_manager", "welcome_msg_cd"))
|
||||
_flmt = FreqLimiter(Config.get_config("invite_manager", "welcome_msg_cd") or 5)
|
||||
|
||||
|
||||
# 群员增加处理
|
||||
@@ -68,7 +68,7 @@ add_group = on_request(priority=1, block=False)
|
||||
@group_increase_handle.handle()
|
||||
async def _(bot: Bot, event: GroupIncreaseNoticeEvent):
|
||||
if event.user_id == int(bot.self_id):
|
||||
group = await GroupInfo.get_group_info(event.group_id)
|
||||
group = await GroupInfo.filter(group_id=event.group_id).first()
|
||||
# 群聊不存在或被强制拉群,退出该群
|
||||
if (not group or group.group_flag == 0) and Config.get_config(
|
||||
"invite_manager", "flag"
|
||||
@@ -91,25 +91,48 @@ async def _(bot: Bot, event: GroupIncreaseNoticeEvent):
|
||||
message=f"触发强制入群保护,退出群聊 {event.group_id} 失败..",
|
||||
)
|
||||
# 默认群功能开关
|
||||
elif event.group_id not in group_manager["group_manager"].keys():
|
||||
elif event.group_id not in group_manager.get_data().group_manager.keys():
|
||||
data = plugins2settings_manager.get_data()
|
||||
for plugin in data.keys():
|
||||
if not data[plugin]["default_status"]:
|
||||
if not data[plugin].default_status:
|
||||
group_manager.block_plugin(plugin, event.group_id)
|
||||
# 即刻刷新权限
|
||||
for user_info in await bot.get_group_member_list(group_id=event.group_id):
|
||||
if user_info["role"] in [
|
||||
"owner",
|
||||
"admin",
|
||||
] and not await LevelUser.is_group_flag(
|
||||
user_info["user_id"], event.group_id
|
||||
):
|
||||
await LevelUser.set_level(
|
||||
user_info["user_id"],
|
||||
user_info["group_id"],
|
||||
Config.get_config("admin_bot_manage", "ADMIN_DEFAULT_AUTH"),
|
||||
)
|
||||
if str(user_info["user_id"]) in bot.config.superusers:
|
||||
await LevelUser.set_level(
|
||||
user_info["user_id"], user_info["group_id"], 9
|
||||
)
|
||||
else:
|
||||
join_time = datetime.now()
|
||||
user_info = await bot.get_group_member_info(
|
||||
group_id=event.group_id, user_id=event.user_id
|
||||
)
|
||||
if await GroupInfoUser.add_member_info(
|
||||
user_info["user_id"],
|
||||
user_info["group_id"],
|
||||
user_info["nickname"],
|
||||
join_time,
|
||||
):
|
||||
logger.info(f"用户{user_info['user_id']} 所属{user_info['group_id']} 更新成功")
|
||||
else:
|
||||
logger.info(f"用户{user_info['user_id']} 所属{user_info['group_id']} 更新失败")
|
||||
user, _ = await GroupInfoUser.get_or_create(
|
||||
user_qq=user_info["user_id"],
|
||||
group_id=user_info["group_id"],
|
||||
defaults={
|
||||
"user_name": user_info["nickname"],
|
||||
},
|
||||
)
|
||||
user.user_join_time = join_time
|
||||
await user.save()
|
||||
# await GroupInfoUser.update_or_create(
|
||||
# user_qq=user_info["user_id"],
|
||||
# group_id=user_info["group_id"],
|
||||
# defaults={"user_name": user_info["nickname"], "user_join_time": join_time},
|
||||
# )
|
||||
logger.info(f"用户{user_info['user_id']} 所属{user_info['group_id']} 更新成功")
|
||||
|
||||
# 群欢迎消息
|
||||
if _flmt.check(event.group_id):
|
||||
@@ -128,9 +151,7 @@ async def _(bot: Bot, event: GroupIncreaseNoticeEvent):
|
||||
msg = msg.replace("[at]", "")
|
||||
at_flag = True
|
||||
if (DATA_PATH / "custom_welcome_msg" / f"{event.group_id}.jpg").exists():
|
||||
img = image(
|
||||
DATA_PATH / "custom_welcome_msg" / f"{event.group_id}.jpg"
|
||||
)
|
||||
img = image(DATA_PATH / "custom_welcome_msg" / f"{event.group_id}.jpg")
|
||||
if msg or img:
|
||||
msg = msg.strip() + img
|
||||
msg = "\n" + msg if at_flag else msg
|
||||
@@ -140,7 +161,11 @@ async def _(bot: Bot, event: GroupIncreaseNoticeEvent):
|
||||
else:
|
||||
await group_increase_handle.send(
|
||||
"[[_task|group_welcome]]新人快跑啊!!本群现状↓(快使用自定义!)"
|
||||
+ image(random.choice(os.listdir(IMAGE_PATH / "qxz")), "qxz")
|
||||
+ image(
|
||||
IMAGE_PATH
|
||||
/ "qxz"
|
||||
/ random.choice(os.listdir(IMAGE_PATH / "qxz"))
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@@ -150,13 +175,13 @@ async def _(bot: Bot, event: GroupDecreaseNoticeEvent):
|
||||
if event.sub_type == "kick_me":
|
||||
group_id = event.group_id
|
||||
operator_id = event.operator_id
|
||||
try:
|
||||
operator_name = (
|
||||
await GroupInfoUser.get_member_info(event.operator_id, event.group_id)
|
||||
).user_name
|
||||
except AttributeError:
|
||||
if user := await GroupInfoUser.get_or_none(
|
||||
user_qq=event.operator_id, group_id=event.group_id
|
||||
):
|
||||
operator_name = user.user_name
|
||||
else:
|
||||
operator_name = "None"
|
||||
group = await GroupInfo.get_group_info(group_id)
|
||||
group = await GroupInfo.filter(group_id=group_id).first()
|
||||
group_name = group.group_name if group else ""
|
||||
coffee = int(list(bot.config.superusers)[0])
|
||||
await bot.send_private_msg(
|
||||
@@ -170,16 +195,19 @@ async def _(bot: Bot, event: GroupDecreaseNoticeEvent):
|
||||
if event.user_id == int(bot.self_id):
|
||||
group_manager.delete_group(event.group_id)
|
||||
return
|
||||
try:
|
||||
user_name = (
|
||||
await GroupInfoUser.get_member_info(event.user_id, event.group_id)
|
||||
).user_name
|
||||
except AttributeError:
|
||||
user_name = str(event.user_id)
|
||||
if await GroupInfoUser.delete_member_info(event.user_id, event.group_id):
|
||||
logger.info(f"用户{user_name}, qq={event.user_id} 所属{event.group_id} 删除成功")
|
||||
if user := await GroupInfoUser.get_or_none(
|
||||
user_qq=event.user_id, group_id=event.group_id
|
||||
):
|
||||
user_name = user.user_name
|
||||
else:
|
||||
logger.info(f"用户{user_name}, qq={event.user_id} 所属{event.group_id} 删除失败")
|
||||
user_name = f"{event.user_id}"
|
||||
await GroupInfoUser.filter(user_qq=event.user_id, group_id=event.group_id).delete()
|
||||
logger.info(
|
||||
f"名称: {user_name} 退出群聊",
|
||||
"group_decrease_handle",
|
||||
event.user_id,
|
||||
event.group_id,
|
||||
)
|
||||
rst = ""
|
||||
if event.sub_type == "leave":
|
||||
rst = f"{user_name}离开了我们..."
|
||||
|
||||
@@ -6,45 +6,30 @@ from nonebot.adapters.onebot.v11 import (
|
||||
Message
|
||||
)
|
||||
from nonebot.params import CommandArg
|
||||
from nonebot.typing import T_State
|
||||
from nonebot.rule import to_me
|
||||
from configs.path_config import IMAGE_PATH, DATA_PATH
|
||||
from utils.message_builder import image
|
||||
from .data_source import create_help_img, get_plugin_help
|
||||
from ._data_source import create_help_img, get_plugin_help
|
||||
from ._utils import GROUP_HELP_PATH
|
||||
import os
|
||||
|
||||
|
||||
__zx_plugin_name__ = "帮助"
|
||||
|
||||
group_help_path = DATA_PATH / "group_help"
|
||||
help_image = IMAGE_PATH / "help.png"
|
||||
__plugin_configs__ = {
|
||||
"TYPE": {"value": "normal", "help": "帮助图片样式 ['normal', 'HTML']", "default_value": "normal"}
|
||||
}
|
||||
|
||||
simple_help_image = IMAGE_PATH / "simple_help.png"
|
||||
if help_image.exists():
|
||||
help_image.unlink()
|
||||
if simple_help_image.exists():
|
||||
simple_help_image.unlink()
|
||||
group_help_path.mkdir(exist_ok=True, parents=True)
|
||||
for x in os.listdir(group_help_path):
|
||||
group_help_image = group_help_path / x
|
||||
group_help_image.unlink()
|
||||
|
||||
_help = on_command("详细功能", rule=to_me(), aliases={"详细帮助"}, priority=1, block=True)
|
||||
|
||||
simple_help = on_command("功能", rule=to_me(), aliases={"help", "帮助"}, priority=1, block=True)
|
||||
|
||||
|
||||
@_help.handle()
|
||||
async def _(bot: Bot, event: MessageEvent, state: T_State):
|
||||
if not help_image.exists():
|
||||
if help_image.exists():
|
||||
help_image.unlink()
|
||||
if simple_help_image.exists():
|
||||
simple_help_image.unlink()
|
||||
await create_help_img(None, help_image, simple_help_image)
|
||||
await _help.finish(image("help.png"))
|
||||
|
||||
|
||||
@simple_help.handle()
|
||||
async def _(bot: Bot, event: MessageEvent, state: T_State, arg: Message = CommandArg()):
|
||||
async def _(bot: Bot, event: MessageEvent, arg: Message = CommandArg()):
|
||||
msg = arg.extract_plain_text().strip()
|
||||
is_super = False
|
||||
if msg:
|
||||
@@ -54,20 +39,18 @@ async def _(bot: Bot, event: MessageEvent, state: T_State, arg: Message = Comman
|
||||
msg = msg.replace('-super', '').strip()
|
||||
msg = get_plugin_help(msg, is_super)
|
||||
if msg:
|
||||
await _help.send(image(b64=msg))
|
||||
await simple_help.send(image(b64=msg))
|
||||
else:
|
||||
await _help.send("没有此功能的帮助信息...")
|
||||
await simple_help.send("没有此功能的帮助信息...")
|
||||
else:
|
||||
if isinstance(event, GroupMessageEvent):
|
||||
_image_path = group_help_path / f"{event.group_id}.png"
|
||||
_image_path = GROUP_HELP_PATH / f"{event.group_id}.png"
|
||||
if not _image_path.exists():
|
||||
await create_help_img(event.group_id, help_image, _image_path)
|
||||
await create_help_img(event.group_id)
|
||||
await simple_help.send(image(_image_path))
|
||||
else:
|
||||
if not simple_help_image.exists():
|
||||
if help_image.exists():
|
||||
help_image.unlink()
|
||||
if simple_help_image.exists():
|
||||
simple_help_image.unlink()
|
||||
await create_help_img(None, help_image, simple_help_image)
|
||||
await _help.finish(image("simple_help.png"))
|
||||
await create_help_img(None)
|
||||
await simple_help.finish(image("simple_help.png"))
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
from typing import Optional, List, Any, Union, Dict
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class Item(BaseModel):
|
||||
plugin_name: str
|
||||
sta: int
|
||||
|
||||
|
||||
class PluginList(BaseModel):
|
||||
plugin_type: str
|
||||
icon: str
|
||||
logo: str
|
||||
items: List[Item]
|
||||
@@ -0,0 +1,71 @@
|
||||
|
||||
from ._utils import HelpImageBuild
|
||||
from utils.image_utils import BuildImage
|
||||
from configs.path_config import IMAGE_PATH
|
||||
from utils.manager import (
|
||||
plugins2settings_manager,
|
||||
admin_manager,
|
||||
)
|
||||
from typing import Optional
|
||||
import nonebot
|
||||
|
||||
|
||||
random_bk_path = IMAGE_PATH / "background" / "help" / "simple_help"
|
||||
|
||||
background = IMAGE_PATH / "background" / "0.png"
|
||||
|
||||
|
||||
async def create_help_img(group_id: Optional[int]):
|
||||
"""
|
||||
说明:
|
||||
生成帮助图片
|
||||
参数:
|
||||
:param group_id: 群号
|
||||
"""
|
||||
await HelpImageBuild().build_image(group_id)
|
||||
|
||||
|
||||
def get_plugin_help(msg: str, is_super: bool = False) -> Optional[str]:
|
||||
"""
|
||||
说明:
|
||||
获取功能的帮助信息
|
||||
参数:
|
||||
:param msg: 功能cmd
|
||||
:param is_super: 是否为超级用户
|
||||
"""
|
||||
module = plugins2settings_manager.get_plugin_module(
|
||||
msg
|
||||
) or admin_manager.get_plugin_module(msg)
|
||||
if module:
|
||||
try:
|
||||
plugin = nonebot.plugin.get_plugin(module)
|
||||
metadata = plugin.metadata
|
||||
if plugin:
|
||||
if is_super:
|
||||
result = plugin.module.__getattribute__(
|
||||
"__plugin_superuser_usage__"
|
||||
)
|
||||
else:
|
||||
result = (
|
||||
metadata.usage
|
||||
if metadata
|
||||
else plugin.module.__getattribute__("__plugin_usage__")
|
||||
)
|
||||
if result:
|
||||
width = 0
|
||||
for x in result.split("\n"):
|
||||
_width = len(x) * 24
|
||||
width = width if width > _width else _width
|
||||
height = len(result.split("\n")) * 45
|
||||
A = BuildImage(width, height, font_size=24)
|
||||
bk = BuildImage(
|
||||
width,
|
||||
height,
|
||||
background=IMAGE_PATH / "background" / "1.png",
|
||||
)
|
||||
A.paste(bk, alpha=True)
|
||||
A.text((int(width * 0.048), int(height * 0.21)), result)
|
||||
return A.pic2bs4()
|
||||
except AttributeError:
|
||||
pass
|
||||
return None
|
||||
@@ -0,0 +1,255 @@
|
||||
import os
|
||||
import random
|
||||
from typing import Dict, List, Optional
|
||||
|
||||
from configs.config import Config
|
||||
from configs.path_config import DATA_PATH, IMAGE_PATH, TEMPLATE_PATH
|
||||
from utils.decorator import Singleton
|
||||
from utils.image_utils import BuildImage, build_sort_image, group_image
|
||||
from utils.manager import group_manager, plugin_data_manager
|
||||
from utils.manager.models import PluginData, PluginType
|
||||
|
||||
from ._config import Item
|
||||
|
||||
GROUP_HELP_PATH = DATA_PATH / "group_help"
|
||||
GROUP_HELP_PATH.mkdir(exist_ok=True, parents=True)
|
||||
for x in os.listdir(GROUP_HELP_PATH):
|
||||
group_help_image = GROUP_HELP_PATH / x
|
||||
group_help_image.unlink()
|
||||
|
||||
BACKGROUND_PATH = IMAGE_PATH / "background" / "help" / "simple_help"
|
||||
|
||||
LOGO_PATH = TEMPLATE_PATH / "menu" / "res" / "logo"
|
||||
|
||||
|
||||
@Singleton
|
||||
class HelpImageBuild:
|
||||
def __init__(self):
|
||||
self._data: Dict[str, PluginData] = plugin_data_manager.get_data()
|
||||
self._sort_data: Dict[str, List[PluginData]] = {}
|
||||
self._image_list = []
|
||||
self.icon2str = {
|
||||
"normal": "fa fa-cog",
|
||||
"原神相关": "fa fa-circle-o",
|
||||
"常规插件": "fa fa-cubes",
|
||||
"联系管理员": "fa fa-envelope-o",
|
||||
"抽卡相关": "fa fa-credit-card-alt",
|
||||
"来点好康的": "fa fa-picture-o",
|
||||
"数据统计": "fa fa-bar-chart",
|
||||
"一些工具": "fa fa-shopping-cart",
|
||||
"商店": "fa fa-shopping-cart",
|
||||
"其它": "fa fa-tags",
|
||||
"群内小游戏": "fa fa-gamepad",
|
||||
}
|
||||
|
||||
def sort_type(self):
|
||||
"""
|
||||
说明:
|
||||
对插件按照菜单类型分类
|
||||
"""
|
||||
if not self._sort_data.keys():
|
||||
for key in self._data.keys():
|
||||
plugin_data = self._data[key]
|
||||
if plugin_data.plugin_type == PluginType.NORMAL:
|
||||
if not self._sort_data.get(plugin_data.menu_type[0]):
|
||||
self._sort_data[plugin_data.menu_type[0]] = []
|
||||
self._sort_data[plugin_data.menu_type[0]].append(self._data[key])
|
||||
|
||||
async def build_image(self, group_id: Optional[int]):
|
||||
if group_id:
|
||||
help_image = GROUP_HELP_PATH / f"{group_id}.png"
|
||||
else:
|
||||
help_image = IMAGE_PATH / f"simple_help.png"
|
||||
build_type = Config.get_config("help", "TYPE")
|
||||
if build_type == "HTML":
|
||||
byt = await self.build_html_image(group_id)
|
||||
with open(help_image, "wb") as f:
|
||||
f.write(byt)
|
||||
else:
|
||||
img = await self.build_pil_image(group_id)
|
||||
img.save(help_image)
|
||||
|
||||
async def build_html_image(self, group_id: Optional[int]) -> bytes:
|
||||
from nonebot_plugin_htmlrender import template_to_pic
|
||||
self.sort_type()
|
||||
classify = {}
|
||||
for menu in self._sort_data:
|
||||
for plugin in self._sort_data[menu]:
|
||||
sta = 0
|
||||
if not plugin.plugin_status.status:
|
||||
if group_id and plugin.plugin_status.block_type in ["all", "group"]:
|
||||
sta = 2
|
||||
if not group_id and plugin.plugin_status.block_type in [
|
||||
"all",
|
||||
"private",
|
||||
]:
|
||||
sta = 2
|
||||
if group_id and not group_manager.get_plugin_super_status(
|
||||
plugin.model, group_id
|
||||
):
|
||||
sta = 2
|
||||
if group_id and not group_manager.get_plugin_status(
|
||||
plugin.model, group_id
|
||||
):
|
||||
sta = 1
|
||||
if classify.get(menu):
|
||||
classify[menu].append(Item(plugin_name=plugin.name, sta=sta))
|
||||
else:
|
||||
classify[menu] = [Item(plugin_name=plugin.name, sta=sta)]
|
||||
max_len = 0
|
||||
flag_index = -1
|
||||
max_data = None
|
||||
plugin_list = []
|
||||
for index, plu in enumerate(classify.keys()):
|
||||
if plu in self.icon2str.keys():
|
||||
icon = self.icon2str[plu]
|
||||
else:
|
||||
icon = "fa fa-pencil-square-o"
|
||||
logo = LOGO_PATH / random.choice(os.listdir(LOGO_PATH))
|
||||
data = {
|
||||
"name": plu if plu != "normal" else "功能",
|
||||
"items": classify[plu],
|
||||
"icon": icon,
|
||||
"logo": str(logo.absolute()),
|
||||
}
|
||||
if len(classify[plu]) > max_len:
|
||||
max_len = len(classify[plu])
|
||||
flag_index = index
|
||||
max_data = data
|
||||
plugin_list.append(data)
|
||||
del plugin_list[flag_index]
|
||||
plugin_list.insert(0, max_data)
|
||||
pic = await template_to_pic(
|
||||
template_path=str((TEMPLATE_PATH / "menu").absolute()),
|
||||
template_name="zhenxun_menu.html",
|
||||
templates={"plugin_list": plugin_list},
|
||||
pages={
|
||||
"viewport": {"width": 1903, "height": 975},
|
||||
"base_url": f"file://{TEMPLATE_PATH}",
|
||||
},
|
||||
wait=2,
|
||||
)
|
||||
return pic
|
||||
|
||||
async def build_pil_image(self, group_id: Optional[int]) -> BuildImage:
|
||||
"""
|
||||
说明:
|
||||
构造帮助图片
|
||||
参数:
|
||||
:param group_id: 群号
|
||||
"""
|
||||
self._image_list = []
|
||||
self.sort_type()
|
||||
font_size = 24
|
||||
build_type = Config.get_config("help", "TYPE")
|
||||
_image = BuildImage(0, 0, plain_text="1", font_size=font_size)
|
||||
for idx, menu_type in enumerate(self._sort_data.keys()):
|
||||
plugin_list = self._sort_data[menu_type]
|
||||
wh_list = [_image.getsize(x.name) for x in plugin_list]
|
||||
wh_list.append(_image.getsize(menu_type))
|
||||
# sum_height = sum([x[1] for x in wh_list])
|
||||
if build_type == "VV":
|
||||
sum_height = 50 * len(plugin_list) + 10
|
||||
else:
|
||||
sum_height = (font_size + 6) * len(plugin_list) + 10
|
||||
max_width = max([x[0] for x in wh_list]) + 20
|
||||
bk = BuildImage(
|
||||
max_width + 40,
|
||||
sum_height + 50,
|
||||
font_size=30,
|
||||
color="#a7d1fc",
|
||||
font="CJGaoDeGuo.otf",
|
||||
)
|
||||
title_size = bk.getsize(menu_type)
|
||||
max_width = max_width if max_width > title_size[0] else title_size[0]
|
||||
B = BuildImage(
|
||||
max_width + 40,
|
||||
sum_height,
|
||||
font_size=font_size,
|
||||
color="white" if not idx % 2 else "black",
|
||||
)
|
||||
curr_h = 10
|
||||
for i, plugin_data in enumerate(plugin_list):
|
||||
text_color = (255, 255, 255) if idx % 2 else (0, 0, 0)
|
||||
if group_id and not group_manager.get_plugin_status(
|
||||
plugin_data.model, group_id
|
||||
):
|
||||
text_color = (252, 75, 13)
|
||||
pos = None
|
||||
# 禁用状态划线
|
||||
if (
|
||||
not plugin_data.plugin_status.status
|
||||
and plugin_data.plugin_status.block_type in ["group", "all"]
|
||||
) or (group_id and not group_manager.get_plugin_super_status(
|
||||
plugin_data.model, group_id
|
||||
)):
|
||||
w = curr_h + int(B.getsize(plugin_data.name)[1] / 2) + 2
|
||||
pos = (
|
||||
7,
|
||||
w,
|
||||
B.getsize(plugin_data.name)[0] + 35,
|
||||
w,
|
||||
)
|
||||
if build_type == "VV":
|
||||
name_image = await self.build_name_image(
|
||||
max_width,
|
||||
plugin_data.name,
|
||||
"black" if not idx % 2 else "white",
|
||||
text_color,
|
||||
pos,
|
||||
)
|
||||
await B.apaste(
|
||||
name_image, (0, curr_h), True, center_type="by_width"
|
||||
)
|
||||
curr_h += name_image.h + 5
|
||||
else:
|
||||
await B.atext(
|
||||
(10, curr_h), f"{i + 1}.{plugin_data.name}", text_color
|
||||
)
|
||||
if pos:
|
||||
await B.aline(pos, (236, 66, 7), 3)
|
||||
curr_h += font_size + 5
|
||||
if menu_type == "normal":
|
||||
menu_type = "功能"
|
||||
await bk.atext((0, 14), menu_type, center_type="by_width")
|
||||
await bk.apaste(B, (0, 50))
|
||||
await bk.atransparent(2)
|
||||
# await bk.acircle_corner(point_list=['lt', 'rt'])
|
||||
self._image_list.append(bk)
|
||||
image_group, h = group_image(self._image_list)
|
||||
B = await build_sort_image(
|
||||
image_group,
|
||||
h,
|
||||
background_path=BACKGROUND_PATH,
|
||||
background_handle=lambda image: image.filter("GaussianBlur", 5),
|
||||
)
|
||||
w = 10
|
||||
h = 10
|
||||
for msg in [
|
||||
"目前支持的功能列表:",
|
||||
"可以通过 ‘帮助[功能名称]’ 来获取对应功能的使用方法",
|
||||
]:
|
||||
text = BuildImage(
|
||||
0,
|
||||
0,
|
||||
plain_text=msg,
|
||||
font_size=24,
|
||||
font="HYWenHei-85W.ttf",
|
||||
)
|
||||
B.paste(text, (w, h), True)
|
||||
h += 50
|
||||
if msg == "目前支持的功能列表:":
|
||||
w += 50
|
||||
await B.apaste(
|
||||
BuildImage(
|
||||
0,
|
||||
0,
|
||||
plain_text="注: 红字代表功能被群管理员禁用,红线代表功能正在维护",
|
||||
font_size=24,
|
||||
font="HYWenHei-85W.ttf",
|
||||
font_color=(231, 74, 57),
|
||||
),
|
||||
(300, 10),
|
||||
True,
|
||||
)
|
||||
return B
|
||||
@@ -1,367 +0,0 @@
|
||||
from utils.image_utils import BuildImage
|
||||
from configs.path_config import IMAGE_PATH
|
||||
from utils.manager import (
|
||||
plugins2settings_manager,
|
||||
admin_manager,
|
||||
plugins_manager,
|
||||
group_manager,
|
||||
)
|
||||
from typing import Optional
|
||||
from services.log import logger
|
||||
from pathlib import Path
|
||||
from utils.utils import get_matchers
|
||||
import random
|
||||
import asyncio
|
||||
import nonebot
|
||||
import os
|
||||
|
||||
|
||||
random_bk_path = IMAGE_PATH / "background" / "help" / "simple_help"
|
||||
|
||||
background = IMAGE_PATH / "background" / "0.png"
|
||||
|
||||
|
||||
async def create_help_img(
|
||||
group_id: Optional[int], help_image: Path, simple_help_image: Path
|
||||
):
|
||||
"""
|
||||
生成帮助图片
|
||||
:param group_id: 群号
|
||||
:param help_image: 图片路径
|
||||
:param simple_help_image: 简易帮助图片路径
|
||||
"""
|
||||
return await asyncio.get_event_loop().run_in_executor(
|
||||
None, _create_help_img, group_id, help_image, simple_help_image
|
||||
)
|
||||
|
||||
|
||||
def _create_help_img(
|
||||
group_id: Optional[int], help_image: Path, simple_help_image: Path
|
||||
):
|
||||
"""
|
||||
生成帮助图片
|
||||
:param group_id: 群号
|
||||
:param help_image: 图片路径
|
||||
:param simple_help_image: 简易帮助图片路径
|
||||
"""
|
||||
_matchers = get_matchers()
|
||||
width = 0
|
||||
matchers_data = {}
|
||||
_des_tmp = {}
|
||||
_plugin_name_tmp = []
|
||||
_tmp = []
|
||||
tmp_img = BuildImage(0, 0, plain_text="1", font_size=24)
|
||||
font_height = tmp_img.h
|
||||
# 插件分类
|
||||
for matcher in _matchers:
|
||||
plugin_name = None
|
||||
_plugin = nonebot.plugin.get_plugin(matcher.plugin_name)
|
||||
_module = _plugin.module
|
||||
try:
|
||||
plugin_name = _module.__getattribute__("__zx_plugin_name__")
|
||||
try:
|
||||
plugin_des = _module.__getattribute__("__plugin_des__")
|
||||
except AttributeError:
|
||||
plugin_des = "_"
|
||||
if (
|
||||
"[hidden]" in plugin_name.lower()
|
||||
or "[admin]" in plugin_name.lower()
|
||||
or "[superuser]" in plugin_name.lower()
|
||||
or plugin_name in _plugin_name_tmp
|
||||
or plugin_name == "帮助"
|
||||
):
|
||||
continue
|
||||
plugin_type = ("normal",)
|
||||
text_type = 0
|
||||
if plugins2settings_manager.get(
|
||||
matcher.plugin_name
|
||||
) and plugins2settings_manager[matcher.plugin_name].get("plugin_type"):
|
||||
plugin_type = tuple(
|
||||
plugins2settings_manager.get_plugin_data(matcher.plugin_name)[
|
||||
"plugin_type"
|
||||
]
|
||||
)
|
||||
else:
|
||||
try:
|
||||
plugin_type = _module.__getattribute__("__plugin_type__")
|
||||
except AttributeError:
|
||||
pass
|
||||
if len(plugin_type) > 1:
|
||||
try:
|
||||
text_type = int(plugin_type[1])
|
||||
except ValueError as e:
|
||||
logger.warning(f"生成列向帮助排列失败 {plugin_name}: {type(e)}: {e}")
|
||||
plugin_type = plugin_type[0]
|
||||
else:
|
||||
plugin_type = plugin_type[0]
|
||||
try:
|
||||
plugin_cmd = _module.__getattribute__("__plugin_cmd__")
|
||||
plugin_cmd = [x for x in plugin_cmd if "[_superuser]" not in x]
|
||||
except AttributeError:
|
||||
plugin_cmd = []
|
||||
if plugin_type not in matchers_data.keys():
|
||||
matchers_data[plugin_type] = {}
|
||||
if plugin_des in _des_tmp.keys():
|
||||
try:
|
||||
matchers_data[plugin_type][_des_tmp[plugin_des]]["cmd"] = (
|
||||
matchers_data[plugin_type][_des_tmp[plugin_des]]["cmd"]
|
||||
+ plugin_cmd
|
||||
)
|
||||
except KeyError as e:
|
||||
logger.warning(f"{type(e)}: {e}")
|
||||
else:
|
||||
matchers_data[plugin_type][plugin_name] = {
|
||||
"modules": matcher.plugin_name,
|
||||
"des": plugin_des,
|
||||
"cmd": plugin_cmd,
|
||||
"text_type": text_type,
|
||||
}
|
||||
try:
|
||||
if text_type == 0:
|
||||
x = tmp_img.getsize(
|
||||
f'{plugin_name}: {matchers_data[plugin_type][plugin_name]["des"]} ->'
|
||||
+ " / ".join(matchers_data[plugin_type][plugin_name]["cmd"])
|
||||
)[0]
|
||||
width = width if width > x else x
|
||||
except KeyError:
|
||||
pass
|
||||
if plugin_des not in _des_tmp:
|
||||
_des_tmp[plugin_des] = plugin_name
|
||||
except AttributeError as e:
|
||||
if plugin_name not in _plugin_name_tmp:
|
||||
logger.warning(f"获取功能 {matcher.plugin_name}: {plugin_name} 设置失败...e:{e}")
|
||||
if plugin_name not in _plugin_name_tmp:
|
||||
_plugin_name_tmp.append(plugin_name)
|
||||
help_img_list = []
|
||||
simple_help_img_list = []
|
||||
types = list(matchers_data.keys())
|
||||
types.sort()
|
||||
ix = 0
|
||||
# 详细帮助
|
||||
for type_ in types:
|
||||
keys = list(matchers_data[type_].keys())
|
||||
keys.sort()
|
||||
help_str = f"{type_ if type_ != 'normal' else '功能'}:\n\n"
|
||||
simple_help_str = f"{type_ if type_ != 'normal' else '功能'}:\n\n"
|
||||
for i, k in enumerate(keys):
|
||||
# 禁用flag
|
||||
flag = True
|
||||
if plugins_manager.get_plugin_status(
|
||||
matchers_data[type_][k]["modules"], "all"
|
||||
):
|
||||
flag = False
|
||||
if group_id:
|
||||
flag = flag and plugins_manager.get_plugin_status(
|
||||
matchers_data[type_][k]["modules"], "group"
|
||||
)
|
||||
simple_help_str += (
|
||||
f"{i+1}.{k}<|_|~|>"
|
||||
f"{group_manager.get_plugin_status(matchers_data[type_][k]['modules'], group_id) if group_id else '_'}|"
|
||||
f"{flag}\n"
|
||||
)
|
||||
if matchers_data[type_][k]["text_type"] == 1:
|
||||
_x = tmp_img.getsize(
|
||||
f"{i+1}".rjust(5)
|
||||
+ f'.{k}: {matchers_data[type_][k]["des"]} {"->" if matchers_data[type_][k]["cmd"] else ""} '
|
||||
)[0]
|
||||
_str = (
|
||||
f"{i+1}".rjust(5)
|
||||
+ f'.{k}: {matchers_data[type_][k]["des"]} {"->" if matchers_data[type_][k]["cmd"] else ""} '
|
||||
)
|
||||
_str += matchers_data[type_][k]["cmd"][0] + "\n"
|
||||
for c in matchers_data[type_][k]["cmd"][1:]:
|
||||
_str += "".rjust(int(_x * 0.125) + 1) + f"{c}\n"
|
||||
help_str += _str
|
||||
else:
|
||||
help_str += (
|
||||
f"{i+1}".rjust(5)
|
||||
+ f'.{k}: {matchers_data[type_][k]["des"]} {"->" if matchers_data[type_][k]["cmd"] else ""} '
|
||||
+ " / ".join(matchers_data[type_][k]["cmd"])
|
||||
+ "\n"
|
||||
)
|
||||
height = len(help_str.split("\n")) * (font_height + 5)
|
||||
simple_height = len(simple_help_str.split("\n")) * (font_height + 5)
|
||||
A = BuildImage(
|
||||
width + 150, height, font_size=24, color="white" if not ix % 2 else "black"
|
||||
)
|
||||
A.text((10, 10), help_str, (255, 255, 255) if ix % 2 else (0, 0, 0))
|
||||
# 生成各个分类的插件简易帮助图片
|
||||
simple_width = 0
|
||||
for x in [
|
||||
tmp_img.getsize(x.split("<|_|~|>")[0])[0]
|
||||
for x in simple_help_str.split("\n")
|
||||
]:
|
||||
simple_width = simple_width if simple_width > x else x
|
||||
bk = BuildImage(simple_width + 20, simple_height, font_size=24, color="#6495ED")
|
||||
B = BuildImage(
|
||||
simple_width + 20,
|
||||
simple_height,
|
||||
font_size=24,
|
||||
color="white" if not ix % 2 else "black",
|
||||
)
|
||||
# 切分,判断插件开关状态
|
||||
_s_height = 10
|
||||
for _s in simple_help_str.split("\n"):
|
||||
text_color = (255, 255, 255) if ix % 2 else (0, 0, 0)
|
||||
_line_flag = False
|
||||
if "<|_|~|>" in _s:
|
||||
_x = _s.split("<|_|~|>")
|
||||
_flag_sp = _x[-1].split("|")
|
||||
if group_id:
|
||||
if _flag_sp[0].lower() != "true":
|
||||
text_color = (252, 75, 13)
|
||||
if _flag_sp[1].lower() == "true":
|
||||
_line_flag = True
|
||||
_s = _x[0]
|
||||
B.text((10, _s_height), _s, text_color)
|
||||
if _line_flag:
|
||||
B.line(
|
||||
(
|
||||
7,
|
||||
_s_height + int(B.getsize(_s)[1] / 2) + 2,
|
||||
B.getsize(_s)[0] + 11,
|
||||
_s_height + int(B.getsize(_s)[1] / 2) + 2,
|
||||
),
|
||||
(236, 66, 7),
|
||||
3,
|
||||
)
|
||||
_s_height += B.getsize("1")[1] + 5
|
||||
# B.text((10, 10), simple_help_str, (255, 255, 255) if ix % 2 else (0, 0, 0))
|
||||
bk.paste(B, center_type="center")
|
||||
bk.transparent(2)
|
||||
ix += 1
|
||||
help_img_list.append(A)
|
||||
simple_help_img_list.append(bk)
|
||||
height = 0
|
||||
for img in help_img_list:
|
||||
height += img.h
|
||||
if not group_id:
|
||||
A = BuildImage(width + 150, height + 50, font_size=24)
|
||||
A.text(
|
||||
(10, 10), '* 注: ‘*’ 代表可有多个相同参数 ‘?’ 代表可省略该参数 *\n\n" "功能名: 功能简介 -> 指令\n\n'
|
||||
)
|
||||
current_height = 50
|
||||
for img in help_img_list:
|
||||
A.paste(img, (0, current_height))
|
||||
current_height += img.h
|
||||
A.save(help_image)
|
||||
# 详细帮助生成完毕
|
||||
# 简易帮助图片合成
|
||||
height = 0
|
||||
width = 0
|
||||
for img in simple_help_img_list:
|
||||
if img.h > height:
|
||||
height = img.h
|
||||
width += img.w + 10
|
||||
B = BuildImage(width + 100, height + 250, font_size=24)
|
||||
width, _ = get_max_width_or_paste(simple_help_img_list, B)
|
||||
bk = None
|
||||
random_bk = os.listdir(random_bk_path)
|
||||
if random_bk:
|
||||
bk = random.choice(random_bk)
|
||||
x = max(width + 50, height + 250)
|
||||
B = BuildImage(
|
||||
x,
|
||||
x,
|
||||
font_size=24,
|
||||
color="#FFEFD5",
|
||||
background=random_bk_path / bk,
|
||||
)
|
||||
B.filter("GaussianBlur", 10)
|
||||
_, B = get_max_width_or_paste(simple_help_img_list, B, True)
|
||||
w = 10
|
||||
h = 10
|
||||
for msg in ["目前支持的功能列表:", "可以通过 ‘帮助[功能名称]’ 来获取对应功能的使用方法", "或者使用 ‘详细帮助’ 来获取所有功能方法"]:
|
||||
text = BuildImage(
|
||||
0,
|
||||
0,
|
||||
plain_text=msg,
|
||||
font_size=24,
|
||||
font="yuanshen.ttf",
|
||||
)
|
||||
B.paste(text, (w, h), True)
|
||||
h += 50
|
||||
if msg == "目前支持的功能列表:":
|
||||
w += 50
|
||||
B.paste(
|
||||
BuildImage(
|
||||
0,
|
||||
0,
|
||||
plain_text="注: 红字代表功能被群管理员禁用,红线代表功能正在维护",
|
||||
font_size=24,
|
||||
font="yuanshen.ttf",
|
||||
font_color=(231, 74, 57)
|
||||
),
|
||||
(300, 10),
|
||||
True,
|
||||
)
|
||||
B.save(simple_help_image)
|
||||
|
||||
|
||||
def get_max_width_or_paste(
|
||||
simple_help_img_list: list, B: BuildImage = None, is_paste: bool = False
|
||||
) -> "int, BuildImage":
|
||||
"""
|
||||
获取最大宽度,或直接贴图
|
||||
:param simple_help_img_list: 简单帮助图片列表
|
||||
:param B: 背景图
|
||||
:param is_paste: 是否直接贴图
|
||||
"""
|
||||
current_width = 50
|
||||
current_height = 180
|
||||
max_width = simple_help_img_list[0].w
|
||||
for i in range(len(simple_help_img_list)):
|
||||
try:
|
||||
if is_paste and B:
|
||||
B.paste(simple_help_img_list[i], (current_width, current_height), True)
|
||||
current_height += simple_help_img_list[i].h + 40
|
||||
if current_height + simple_help_img_list[i + 1].h > B.h - 10:
|
||||
current_height = 180
|
||||
current_width += max_width + 30
|
||||
max_width = 0
|
||||
elif simple_help_img_list[i].w > max_width:
|
||||
max_width = simple_help_img_list[i].w
|
||||
except IndexError:
|
||||
pass
|
||||
if current_width > simple_help_img_list[0].w + 50:
|
||||
current_width += simple_help_img_list[-1].w
|
||||
return current_width, B
|
||||
|
||||
|
||||
def get_plugin_help(msg: str, is_super: bool = False) -> Optional[str]:
|
||||
"""
|
||||
获取功能的帮助信息
|
||||
:param msg: 功能cmd
|
||||
:param is_super: 是否为超级用户
|
||||
"""
|
||||
module = plugins2settings_manager.get_plugin_module(msg)
|
||||
if not module:
|
||||
module = admin_manager.get_plugin_module(msg)
|
||||
if module:
|
||||
try:
|
||||
plugin = nonebot.plugin.get_plugin(module)
|
||||
if plugin:
|
||||
if is_super:
|
||||
result = plugin.module.__getattribute__(
|
||||
"__plugin_superuser_usage__"
|
||||
)
|
||||
else:
|
||||
result = plugin.module.__getattribute__("__plugin_usage__")
|
||||
if result:
|
||||
width = 0
|
||||
for x in result.split("\n"):
|
||||
_width = len(x) * 24
|
||||
width = width if width > _width else _width
|
||||
height = len(result.split("\n")) * 45
|
||||
A = BuildImage(width, height, font_size=24)
|
||||
bk = BuildImage(
|
||||
width,
|
||||
height,
|
||||
background=IMAGE_PATH / "background" / "1.png",
|
||||
)
|
||||
A.paste(bk, alpha=True)
|
||||
A.text((int(width * 0.048), int(height * 0.21)), result)
|
||||
return A.pic2bs4()
|
||||
except AttributeError:
|
||||
pass
|
||||
return None
|
||||
@@ -1,13 +1,13 @@
|
||||
from configs.config import Config
|
||||
|
||||
|
||||
Config.add_plugin_config(
|
||||
"hook",
|
||||
"CHECK_NOTICE_INFO_CD",
|
||||
300,
|
||||
name="基础hook配置",
|
||||
help_="群检测,个人权限检测等各种检测提示信息cd",
|
||||
default_value=300
|
||||
default_value=300,
|
||||
type=int,
|
||||
)
|
||||
|
||||
Config.add_plugin_config(
|
||||
@@ -15,7 +15,8 @@ Config.add_plugin_config(
|
||||
"MALICIOUS_BAN_TIME",
|
||||
30,
|
||||
help_="恶意命令触发检测触发后ban的时长(分钟)",
|
||||
default_value=30
|
||||
default_value=30,
|
||||
type=int,
|
||||
)
|
||||
|
||||
Config.add_plugin_config(
|
||||
@@ -23,14 +24,10 @@ Config.add_plugin_config(
|
||||
"MALICIOUS_CHECK_TIME",
|
||||
5,
|
||||
help_="恶意命令触发检测规定时间内(秒)",
|
||||
default_value=5
|
||||
default_value=5,
|
||||
type=int,
|
||||
)
|
||||
|
||||
Config.add_plugin_config(
|
||||
"hook",
|
||||
"MALICIOUS_BAN_COUNT",
|
||||
6,
|
||||
help_="恶意命令触发检测最大触发次数",
|
||||
default_value=6
|
||||
"hook", "MALICIOUS_BAN_COUNT", 6, help_="恶意命令触发检测最大触发次数", default_value=6, type=int
|
||||
)
|
||||
|
||||
|
||||
@@ -1,6 +1,40 @@
|
||||
from nonebot.adapters.onebot.v11 import GroupMessageEvent, PrivateMessageEvent
|
||||
from utils.manager import plugins2block_manager, StaticData
|
||||
import time
|
||||
from typing import Optional
|
||||
|
||||
import nonebot
|
||||
from nonebot.adapters.onebot.v11 import (
|
||||
Bot,
|
||||
Event,
|
||||
GroupMessageEvent,
|
||||
Message,
|
||||
MessageEvent,
|
||||
PokeNotifyEvent,
|
||||
PrivateMessageEvent,
|
||||
)
|
||||
from nonebot.exception import ActionFailed, IgnoredException
|
||||
from nonebot.internal.matcher import Matcher
|
||||
|
||||
from configs.config import Config
|
||||
from models.bag_user import BagUser
|
||||
from models.ban_user import BanUser
|
||||
from models.friend_user import FriendUser
|
||||
from models.group_member_info import GroupInfoUser
|
||||
from models.level_user import LevelUser
|
||||
from models.user_shop_gold_log import UserShopGoldLog
|
||||
from services.log import logger
|
||||
from utils.decorator import Singleton
|
||||
from utils.manager import (
|
||||
StaticData,
|
||||
admin_manager,
|
||||
group_manager,
|
||||
plugins2block_manager,
|
||||
plugins2cd_manager,
|
||||
plugins2count_manager,
|
||||
plugins2settings_manager,
|
||||
plugins_manager,
|
||||
)
|
||||
from utils.message_builder import at
|
||||
from utils.utils import FreqLimiter
|
||||
|
||||
ignore_rst_module = ["ai", "poke", "dialogue"]
|
||||
|
||||
@@ -8,7 +42,6 @@ other_limit_plugins = ["poke"]
|
||||
|
||||
|
||||
class StatusMessageManager(StaticData):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__(None)
|
||||
|
||||
@@ -39,8 +72,8 @@ def set_block_limit_false(event, module):
|
||||
"""
|
||||
if plugins2block_manager.check_plugin_block_status(module):
|
||||
plugin_block_data = plugins2block_manager.get_plugin_block_data(module)
|
||||
check_type = plugin_block_data["check_type"]
|
||||
limit_type = plugin_block_data["limit_type"]
|
||||
check_type = plugin_block_data.check_type
|
||||
limit_type = plugin_block_data.limit_type
|
||||
if not (
|
||||
(isinstance(event, GroupMessageEvent) and check_type == "private")
|
||||
or (isinstance(event, PrivateMessageEvent) and check_type == "group")
|
||||
@@ -50,3 +83,443 @@ def set_block_limit_false(event, module):
|
||||
block_type_ = event.group_id
|
||||
plugins2block_manager.set_false(block_type_, module)
|
||||
|
||||
|
||||
async def send_msg(msg: str, bot: Bot, event: MessageEvent):
|
||||
"""
|
||||
说明:
|
||||
发送信息
|
||||
参数:
|
||||
:param msg: pass
|
||||
:param bot: pass
|
||||
:param event: pass
|
||||
"""
|
||||
if "[uname]" in msg:
|
||||
uname = event.sender.card or event.sender.nickname
|
||||
msg = msg.replace("[uname]", uname)
|
||||
if "[nickname]" in msg:
|
||||
if isinstance(event, GroupMessageEvent):
|
||||
nickname = await GroupInfoUser.get_user_nickname(
|
||||
event.user_id, event.group_id
|
||||
)
|
||||
else:
|
||||
nickname = await FriendUser.get_user_nickname(event.user_id)
|
||||
msg = msg.replace("[nickname]", nickname)
|
||||
if "[at]" in msg and isinstance(event, GroupMessageEvent):
|
||||
msg = msg.replace("[at]", str(at(event.user_id)))
|
||||
try:
|
||||
if isinstance(event, GroupMessageEvent):
|
||||
status_message_manager.add(event.group_id)
|
||||
await bot.send_group_msg(group_id=event.group_id, message=Message(msg))
|
||||
else:
|
||||
status_message_manager.add(event.user_id)
|
||||
await bot.send_private_msg(user_id=event.user_id, message=Message(msg))
|
||||
except ActionFailed:
|
||||
pass
|
||||
|
||||
|
||||
class IsSuperuserException(Exception):
|
||||
pass
|
||||
|
||||
|
||||
@Singleton
|
||||
class AuthChecker:
|
||||
"""
|
||||
权限检查
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self._flmt = FreqLimiter(Config.get_config("hook", "CHECK_NOTICE_INFO_CD"))
|
||||
self._flmt_g = FreqLimiter(Config.get_config("hook", "CHECK_NOTICE_INFO_CD"))
|
||||
self._flmt_s = FreqLimiter(Config.get_config("hook", "CHECK_NOTICE_INFO_CD"))
|
||||
self._flmt_c = FreqLimiter(Config.get_config("hook", "CHECK_NOTICE_INFO_CD"))
|
||||
|
||||
async def auth(self, matcher: Matcher, bot: Bot, event: Event):
|
||||
"""
|
||||
说明:
|
||||
权限检查
|
||||
参数:
|
||||
:param matcher: matcher
|
||||
:param bot: bot
|
||||
:param event: event
|
||||
"""
|
||||
try:
|
||||
if plugin_name := matcher.plugin_name:
|
||||
cost_gold = await self.auth_cost(plugin_name, bot, event)
|
||||
user_id = getattr(event, "user_id", None)
|
||||
if user_id and str(user_id) not in bot.config.superusers:
|
||||
await self.auth_basic(plugin_name, bot, event)
|
||||
self.auth_group(plugin_name, bot, event)
|
||||
await self.auth_admin(plugin_name, matcher, bot, event)
|
||||
await self.auth_plugin(plugin_name, matcher, bot, event)
|
||||
await self.auth_limit(plugin_name, bot, event)
|
||||
if cost_gold:
|
||||
await BagUser.spend_gold(
|
||||
event.user_id, event.group_id, cost_gold
|
||||
)
|
||||
except IsSuperuserException:
|
||||
return
|
||||
|
||||
async def auth_limit(self, plugin_name: str, bot: Bot, event: Event):
|
||||
"""
|
||||
说明:
|
||||
插件限制
|
||||
参数:
|
||||
:param plugin_name: 模块名
|
||||
:param bot: bot
|
||||
:param event: event
|
||||
"""
|
||||
if plugins2cd_manager.check_plugin_cd_status(plugin_name):
|
||||
plugin_cd_data = plugins2cd_manager.get_plugin_cd_data(plugin_name)
|
||||
check_type = plugin_cd_data.check_type
|
||||
limit_type = plugin_cd_data.limit_type
|
||||
msg = plugin_cd_data.rst
|
||||
if (
|
||||
(isinstance(event, PrivateMessageEvent) and check_type == "private")
|
||||
or (isinstance(event, GroupMessageEvent) and check_type == "group")
|
||||
or plugins2cd_manager.get_plugin_data(plugin_name).check_type == "all"
|
||||
):
|
||||
cd_type_ = event.user_id
|
||||
if limit_type == "group" and isinstance(event, GroupMessageEvent):
|
||||
cd_type_ = event.group_id
|
||||
if not plugins2cd_manager.check(plugin_name, cd_type_):
|
||||
if msg:
|
||||
await send_msg(msg, bot, event)
|
||||
raise IgnoredException(f"{plugin_name} 正在cd中...")
|
||||
else:
|
||||
plugins2cd_manager.start_cd(plugin_name, cd_type_)
|
||||
# Block
|
||||
if plugins2block_manager.check_plugin_block_status(plugin_name):
|
||||
plugin_block_data = plugins2block_manager.get_plugin_block_data(plugin_name)
|
||||
check_type = plugin_block_data.check_type
|
||||
limit_type = plugin_block_data.limit_type
|
||||
msg = plugin_block_data.rst
|
||||
if (
|
||||
(isinstance(event, PrivateMessageEvent) and check_type == "private")
|
||||
or (isinstance(event, GroupMessageEvent) and check_type == "group")
|
||||
or check_type == "all"
|
||||
):
|
||||
block_type_ = event.user_id
|
||||
if limit_type == "group" and isinstance(event, GroupMessageEvent):
|
||||
block_type_ = event.group_id
|
||||
if plugins2block_manager.check(block_type_, plugin_name):
|
||||
if msg:
|
||||
await send_msg(msg, bot, event)
|
||||
raise IgnoredException(f"{event.user_id}正在调用{plugin_name}....")
|
||||
else:
|
||||
plugins2block_manager.set_true(block_type_, plugin_name)
|
||||
# Count
|
||||
if (
|
||||
plugins2count_manager.check_plugin_count_status(plugin_name)
|
||||
and event.user_id not in bot.config.superusers
|
||||
):
|
||||
plugin_count_data = plugins2count_manager.get_plugin_count_data(plugin_name)
|
||||
limit_type = plugin_count_data.limit_type
|
||||
msg = plugin_count_data.rst
|
||||
count_type_ = event.user_id
|
||||
if limit_type == "group" and isinstance(event, GroupMessageEvent):
|
||||
count_type_ = event.group_id
|
||||
if not plugins2count_manager.check(plugin_name, count_type_):
|
||||
if msg:
|
||||
await send_msg(msg, bot, event)
|
||||
raise IgnoredException(f"{plugin_name} count次数限制...")
|
||||
else:
|
||||
plugins2count_manager.increase(plugin_name, count_type_)
|
||||
|
||||
async def auth_plugin(
|
||||
self, plugin_name: str, matcher: Matcher, bot: Bot, event: Event
|
||||
):
|
||||
"""
|
||||
说明:
|
||||
插件状态
|
||||
参数:
|
||||
:param plugin_name: 模块名
|
||||
:param matcher: matcher
|
||||
:param bot: bot
|
||||
:param event: event
|
||||
"""
|
||||
if plugin_name in plugins2settings_manager.keys() and matcher.priority not in [
|
||||
1,
|
||||
999,
|
||||
]:
|
||||
# 戳一戳单独判断
|
||||
if (
|
||||
isinstance(event, GroupMessageEvent)
|
||||
or isinstance(event, PokeNotifyEvent)
|
||||
or matcher.plugin_name in other_limit_plugins
|
||||
):
|
||||
if status_message_manager.get(event.group_id) is None:
|
||||
status_message_manager.delete(event.group_id)
|
||||
if plugins2settings_manager[
|
||||
plugin_name
|
||||
].level > group_manager.get_group_level(event.group_id):
|
||||
try:
|
||||
if (
|
||||
self._flmt_g.check(event.user_id)
|
||||
and plugin_name not in ignore_rst_module
|
||||
):
|
||||
self._flmt_g.start_cd(event.user_id)
|
||||
await bot.send_group_msg(
|
||||
group_id=event.group_id, message="群权限不足..."
|
||||
)
|
||||
except ActionFailed:
|
||||
pass
|
||||
if event.is_tome():
|
||||
status_message_manager.add(event.group_id)
|
||||
set_block_limit_false(event, plugin_name)
|
||||
raise IgnoredException("群权限不足")
|
||||
# 插件状态
|
||||
if not group_manager.get_plugin_status(plugin_name, event.group_id):
|
||||
try:
|
||||
if plugin_name not in ignore_rst_module and self._flmt_s.check(
|
||||
event.group_id
|
||||
):
|
||||
self._flmt_s.start_cd(event.group_id)
|
||||
await bot.send_group_msg(
|
||||
group_id=event.group_id, message="该群未开启此功能.."
|
||||
)
|
||||
except ActionFailed:
|
||||
pass
|
||||
if event.is_tome():
|
||||
status_message_manager.add(event.group_id)
|
||||
set_block_limit_false(event, plugin_name)
|
||||
raise IgnoredException("未开启此功能...")
|
||||
# 管理员禁用
|
||||
if not group_manager.get_plugin_status(
|
||||
f"{plugin_name}:super", event.group_id
|
||||
):
|
||||
try:
|
||||
if (
|
||||
self._flmt_s.check(event.group_id)
|
||||
and plugin_name not in ignore_rst_module
|
||||
):
|
||||
self._flmt_s.start_cd(event.group_id)
|
||||
await bot.send_group_msg(
|
||||
group_id=event.group_id, message="管理员禁用了此群该功能..."
|
||||
)
|
||||
except ActionFailed:
|
||||
pass
|
||||
if event.is_tome():
|
||||
status_message_manager.add(event.group_id)
|
||||
set_block_limit_false(event, plugin_name)
|
||||
raise IgnoredException("管理员禁用了此群该功能...")
|
||||
# 群聊禁用
|
||||
if not plugins_manager.get_plugin_status(
|
||||
plugin_name, block_type="group"
|
||||
):
|
||||
try:
|
||||
if (
|
||||
self._flmt_c.check(event.group_id)
|
||||
and plugin_name not in ignore_rst_module
|
||||
):
|
||||
self._flmt_c.start_cd(event.group_id)
|
||||
await bot.send_group_msg(
|
||||
group_id=event.group_id, message="该功能在群聊中已被禁用..."
|
||||
)
|
||||
except ActionFailed:
|
||||
pass
|
||||
if event.is_tome():
|
||||
status_message_manager.add(event.group_id)
|
||||
set_block_limit_false(event, plugin_name)
|
||||
raise IgnoredException("该插件在群聊中已被禁用...")
|
||||
else:
|
||||
# 私聊禁用
|
||||
if not plugins_manager.get_plugin_status(
|
||||
plugin_name, block_type="private"
|
||||
):
|
||||
try:
|
||||
if self._flmt_c.check(event.user_id):
|
||||
self._flmt_c.start_cd(event.user_id)
|
||||
await bot.send_private_msg(
|
||||
user_id=event.user_id, message="该功能在私聊中已被禁用..."
|
||||
)
|
||||
except ActionFailed:
|
||||
pass
|
||||
if event.is_tome():
|
||||
status_message_manager.add(event.user_id)
|
||||
set_block_limit_false(event, plugin_name)
|
||||
raise IgnoredException("该插件在私聊中已被禁用...")
|
||||
# 维护
|
||||
if not plugins_manager.get_plugin_status(plugin_name, block_type="all"):
|
||||
if isinstance(
|
||||
event, GroupMessageEvent
|
||||
) and group_manager.check_group_is_white(event.group_id):
|
||||
raise IsSuperuserException()
|
||||
try:
|
||||
if isinstance(event, GroupMessageEvent):
|
||||
if (
|
||||
self._flmt_c.check(event.group_id)
|
||||
and plugin_name not in ignore_rst_module
|
||||
):
|
||||
self._flmt_c.start_cd(event.group_id)
|
||||
logger.info(f"{event.user_id} ||XXXXXX: {matcher.module}")
|
||||
await bot.send_group_msg(
|
||||
group_id=event.group_id, message="此功能正在维护..."
|
||||
)
|
||||
else:
|
||||
await bot.send_private_msg(
|
||||
user_id=event.user_id, message="此功能正在维护..."
|
||||
)
|
||||
except ActionFailed:
|
||||
pass
|
||||
if event.is_tome():
|
||||
id_ = (
|
||||
event.group_id
|
||||
if isinstance(event, GroupMessageEvent)
|
||||
else event.user_id
|
||||
)
|
||||
status_message_manager.add(id_)
|
||||
set_block_limit_false(event, plugin_name)
|
||||
raise IgnoredException("此功能正在维护...")
|
||||
|
||||
async def auth_admin(
|
||||
self, plugin_name: str, matcher: Matcher, bot: Bot, event: Event
|
||||
):
|
||||
"""
|
||||
说明:
|
||||
管理员命令 个人权限
|
||||
参数:
|
||||
:param plugin_name: 模块名
|
||||
:param matcher: matcher
|
||||
:param bot: bot
|
||||
:param event: event
|
||||
"""
|
||||
if plugin_name in admin_manager.keys() and matcher.priority not in [1, 999]:
|
||||
if isinstance(event, GroupMessageEvent):
|
||||
# 个人权限
|
||||
if (
|
||||
not await LevelUser.check_level(
|
||||
event.user_id,
|
||||
event.group_id,
|
||||
admin_manager.get_plugin_level(plugin_name),
|
||||
)
|
||||
and admin_manager.get_plugin_level(plugin_name) > 0
|
||||
):
|
||||
try:
|
||||
if self._flmt.check(event.user_id):
|
||||
self._flmt.start_cd(event.user_id)
|
||||
await bot.send_group_msg(
|
||||
group_id=event.group_id,
|
||||
message=f"{at(event.user_id)}你的权限不足喔,该功能需要的权限等级:"
|
||||
f"{admin_manager.get_plugin_level(plugin_name)}",
|
||||
)
|
||||
except ActionFailed:
|
||||
pass
|
||||
set_block_limit_false(event, plugin_name)
|
||||
if event.is_tome():
|
||||
status_message_manager.add(event.group_id)
|
||||
raise IgnoredException("权限不足")
|
||||
else:
|
||||
if not await LevelUser.check_level(
|
||||
event.user_id, 0, admin_manager.get_plugin_level(plugin_name)
|
||||
):
|
||||
try:
|
||||
await bot.send_private_msg(
|
||||
user_id=event.user_id,
|
||||
message=f"你的权限不足喔,该功能需要的权限等级:{admin_manager.get_plugin_level(plugin_name)}",
|
||||
)
|
||||
except ActionFailed:
|
||||
pass
|
||||
set_block_limit_false(event, plugin_name)
|
||||
if event.is_tome():
|
||||
status_message_manager.add(event.user_id)
|
||||
raise IgnoredException("权限不足")
|
||||
|
||||
def auth_group(self, plugin_name: str, bot: Bot, event: Event):
|
||||
"""
|
||||
说明:
|
||||
群黑名单检测 群总开关检测
|
||||
参数:
|
||||
:param plugin_name: 模块名
|
||||
:param bot: bot
|
||||
:param event: event
|
||||
"""
|
||||
if isinstance(event, GroupMessageEvent) or plugin_name in other_limit_plugins:
|
||||
try:
|
||||
if (
|
||||
group_manager.get_group_level(event.group_id) < 0
|
||||
and str(event.user_id) not in bot.config.superusers
|
||||
):
|
||||
raise IgnoredException("群黑名单")
|
||||
if not group_manager.check_group_bot_status(event.group_id):
|
||||
try:
|
||||
if str(event.get_message()) != "醒来":
|
||||
raise IgnoredException("功能总开关关闭状态")
|
||||
except ValueError:
|
||||
raise IgnoredException("功能总开关关闭状态")
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
async def auth_basic(self, plugin_name: str, bot: Bot, event: Event):
|
||||
"""
|
||||
说明:
|
||||
检测是否满足超级用户权限,是否被ban等
|
||||
参数:
|
||||
:param plugin_name: 模块名
|
||||
:param bot: bot
|
||||
:param event: event
|
||||
"""
|
||||
try:
|
||||
if (
|
||||
(
|
||||
not isinstance(event, MessageEvent)
|
||||
and plugin_name not in other_limit_plugins
|
||||
)
|
||||
or await BanUser.is_ban(event.user_id)
|
||||
and str(event.user_id) not in bot.config.superusers
|
||||
) or (
|
||||
str(event.user_id) in bot.config.superusers
|
||||
and plugins2settings_manager.get_plugin_data(plugin_name)
|
||||
and not plugins2settings_manager.get_plugin_data(
|
||||
plugin_name
|
||||
).limit_superuser
|
||||
):
|
||||
raise IsSuperuserException()
|
||||
_plugin = nonebot.plugin.get_plugin(plugin_name)
|
||||
_module = _plugin.module
|
||||
_plugin_name = _module.__getattribute__("__zx_plugin_name__")
|
||||
if (
|
||||
"[superuser]" in _plugin_name.lower()
|
||||
and str(event.user_id) in bot.config.superusers
|
||||
):
|
||||
raise IsSuperuserException()
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
async def auth_cost(self, plugin_name: str, bot: Bot, event: Event) -> int:
|
||||
"""
|
||||
说明:
|
||||
检测是否满足金币条件
|
||||
参数:
|
||||
:param plugin_name: 模块名
|
||||
:param bot: bot
|
||||
:param event: event
|
||||
"""
|
||||
cost_gold = 0
|
||||
if isinstance(event, GroupMessageEvent) and (
|
||||
psm := plugins2settings_manager.get_plugin_data(plugin_name)
|
||||
):
|
||||
if psm.cost_gold > 0:
|
||||
if (
|
||||
await BagUser.get_gold(event.user_id, event.group_id)
|
||||
< psm.cost_gold
|
||||
):
|
||||
await send_msg(f"金币不足..该功能需要{psm.cost_gold}金币..", bot, event)
|
||||
raise IgnoredException(f"{plugin_name} 金币限制...")
|
||||
# 当插件不阻塞超级用户时,超级用户提前扣除金币
|
||||
if (
|
||||
str(event.user_id) in bot.config.superusers
|
||||
and not psm.limit_superuser
|
||||
):
|
||||
await BagUser.spend_gold(
|
||||
event.user_id, event.group_id, psm.cost_gold
|
||||
)
|
||||
await UserShopGoldLog.create(
|
||||
user_qq=event.user_id,
|
||||
group_id=event.group_id,
|
||||
type=2,
|
||||
name=plugin_name,
|
||||
num=1,
|
||||
spend_gold=psm.cost_gold,
|
||||
)
|
||||
cost_gold = psm.cost_gold
|
||||
return cost_gold
|
||||
|
||||
@@ -1,386 +1,36 @@
|
||||
from nonebot.matcher import Matcher
|
||||
from nonebot.message import run_preprocessor, run_postprocessor, IgnoredException
|
||||
from nonebot.adapters.onebot.v11.exception import ActionFailed
|
||||
from models.friend_user import FriendUser
|
||||
from models.group_member_info import GroupInfoUser
|
||||
from models.bag_user import BagUser
|
||||
from utils.manager import (
|
||||
plugins2settings_manager,
|
||||
admin_manager,
|
||||
group_manager,
|
||||
plugins_manager,
|
||||
plugins2cd_manager,
|
||||
plugins2block_manager,
|
||||
plugins2count_manager,
|
||||
)
|
||||
from ._utils import (
|
||||
set_block_limit_false,
|
||||
status_message_manager,
|
||||
ignore_rst_module,
|
||||
other_limit_plugins,
|
||||
)
|
||||
from nonebot.typing import T_State
|
||||
from typing import Optional
|
||||
|
||||
from nonebot.adapters.onebot.v11 import (
|
||||
Bot,
|
||||
MessageEvent,
|
||||
GroupMessageEvent,
|
||||
PokeNotifyEvent,
|
||||
PrivateMessageEvent,
|
||||
Message,
|
||||
Event,
|
||||
)
|
||||
from configs.config import Config
|
||||
from models.ban_user import BanUser
|
||||
from utils.utils import FreqLimiter
|
||||
from utils.message_builder import at
|
||||
from models.level_user import LevelUser
|
||||
import nonebot
|
||||
from nonebot.matcher import Matcher
|
||||
from nonebot.message import run_preprocessor, run_postprocessor
|
||||
from nonebot.typing import T_State
|
||||
|
||||
_flmt = FreqLimiter(Config.get_config("hook", "CHECK_NOTICE_INFO_CD"))
|
||||
_flmt_g = FreqLimiter(Config.get_config("hook", "CHECK_NOTICE_INFO_CD"))
|
||||
_flmt_s = FreqLimiter(Config.get_config("hook", "CHECK_NOTICE_INFO_CD"))
|
||||
_flmt_c = FreqLimiter(Config.get_config("hook", "CHECK_NOTICE_INFO_CD"))
|
||||
from ._utils import (
|
||||
set_block_limit_false,
|
||||
AuthChecker,
|
||||
)
|
||||
|
||||
|
||||
# 权限检测
|
||||
# # 权限检测
|
||||
@run_preprocessor
|
||||
async def _(matcher: Matcher, bot: Bot, event: Event, state: T_State):
|
||||
module = matcher.plugin_name
|
||||
plugins2info_dict = plugins2settings_manager.get_data()
|
||||
# 功能的金币检测 #######################################
|
||||
# 功能的金币检测 #######################################
|
||||
# 功能的金币检测 #######################################
|
||||
cost_gold = 0
|
||||
if isinstance(
|
||||
event, GroupMessageEvent
|
||||
) and plugins2settings_manager.get_plugin_data(module).get("cost_gold"):
|
||||
cost_gold = plugins2settings_manager.get_plugin_data(module).get("cost_gold")
|
||||
if await BagUser.get_gold(event.user_id, event.group_id) < cost_gold:
|
||||
await send_msg(f"金币不足..该功能需要{cost_gold}金币..", bot, event)
|
||||
raise IgnoredException(f"{module} 金币限制...")
|
||||
# 当插件不阻塞超级用户时,超级用户提前扣除金币
|
||||
if (
|
||||
str(event.user_id) in bot.config.superusers
|
||||
and not plugins2info_dict[module]["limit_superuser"]
|
||||
):
|
||||
await BagUser.spend_gold(event.user_id, event.group_id, cost_gold)
|
||||
try:
|
||||
if (
|
||||
(not isinstance(event, MessageEvent) and module not in other_limit_plugins)
|
||||
or await BanUser.is_ban(event.user_id)
|
||||
and str(event.user_id) not in bot.config.superusers
|
||||
) or (
|
||||
str(event.user_id) in bot.config.superusers
|
||||
and plugins2info_dict.get(module)
|
||||
and not plugins2info_dict[module]["limit_superuser"]
|
||||
):
|
||||
return
|
||||
except AttributeError:
|
||||
pass
|
||||
# 超级用户命令
|
||||
try:
|
||||
_plugin = nonebot.plugin.get_plugin(module)
|
||||
_module = _plugin.module
|
||||
plugin_name = _module.__getattribute__("__zx_plugin_name__")
|
||||
if (
|
||||
"[superuser]" in plugin_name.lower()
|
||||
and str(event.user_id) in bot.config.superusers
|
||||
):
|
||||
return
|
||||
except AttributeError:
|
||||
pass
|
||||
# 群黑名单检测 群总开关检测
|
||||
if isinstance(event, GroupMessageEvent) or matcher.plugin_name == other_limit_plugins:
|
||||
try:
|
||||
if (
|
||||
group_manager.get_group_level(event.group_id) < 0
|
||||
and str(event.user_id) not in bot.config.superusers
|
||||
):
|
||||
raise IgnoredException("群黑名单")
|
||||
if not group_manager.check_group_bot_status(event.group_id):
|
||||
try:
|
||||
if str(event.get_message()) != "醒来":
|
||||
raise IgnoredException("功能总开关关闭状态")
|
||||
except ValueError:
|
||||
raise IgnoredException("功能总开关关闭状态")
|
||||
except AttributeError:
|
||||
pass
|
||||
if module in admin_manager.keys() and matcher.priority not in [1, 9]:
|
||||
if isinstance(event, GroupMessageEvent):
|
||||
# 个人权限
|
||||
if (
|
||||
not await LevelUser.check_level(
|
||||
event.user_id,
|
||||
event.group_id,
|
||||
admin_manager.get_plugin_level(module),
|
||||
)
|
||||
and admin_manager.get_plugin_level(module) > 0
|
||||
):
|
||||
try:
|
||||
if _flmt.check(event.user_id):
|
||||
_flmt.start_cd(event.user_id)
|
||||
await bot.send_group_msg(
|
||||
group_id=event.group_id,
|
||||
message=f"{at(event.user_id)}你的权限不足喔,该功能需要的权限等级:"
|
||||
f"{admin_manager.get_plugin_level(module)}",
|
||||
)
|
||||
except ActionFailed:
|
||||
pass
|
||||
set_block_limit_false(event, module)
|
||||
if event.is_tome():
|
||||
status_message_manager.add(event.group_id)
|
||||
raise IgnoredException("权限不足")
|
||||
else:
|
||||
if not await LevelUser.check_level(
|
||||
event.user_id, 0, admin_manager.get_plugin_level(module)
|
||||
):
|
||||
try:
|
||||
await bot.send_private_msg(
|
||||
user_id=event.user_id,
|
||||
message=f"你的权限不足喔,该功能需要的权限等级:{admin_manager.get_plugin_level(module)}",
|
||||
)
|
||||
except ActionFailed:
|
||||
pass
|
||||
set_block_limit_false(event, module)
|
||||
if event.is_tome():
|
||||
status_message_manager.add(event.user_id)
|
||||
raise IgnoredException("权限不足")
|
||||
if module in plugins2info_dict.keys() and matcher.priority not in [1, 9]:
|
||||
# 戳一戳单独判断
|
||||
if isinstance(event, GroupMessageEvent) or isinstance(event, PokeNotifyEvent) or matcher.plugin_name in other_limit_plugins:
|
||||
if status_message_manager.get(event.group_id) is None:
|
||||
status_message_manager.delete(event.group_id)
|
||||
if plugins2info_dict[module]["level"] > group_manager.get_group_level(
|
||||
event.group_id
|
||||
):
|
||||
try:
|
||||
if _flmt_g.check(event.user_id) and module not in ignore_rst_module:
|
||||
_flmt_g.start_cd(event.user_id)
|
||||
await bot.send_group_msg(
|
||||
group_id=event.group_id, message="群权限不足..."
|
||||
)
|
||||
except ActionFailed:
|
||||
pass
|
||||
if event.is_tome():
|
||||
status_message_manager.add(event.group_id)
|
||||
set_block_limit_false(event, module)
|
||||
raise IgnoredException("群权限不足")
|
||||
# 插件状态
|
||||
if not group_manager.get_plugin_status(module, event.group_id):
|
||||
try:
|
||||
if module not in ignore_rst_module and _flmt_s.check(
|
||||
event.group_id
|
||||
):
|
||||
_flmt_s.start_cd(event.group_id)
|
||||
await bot.send_group_msg(
|
||||
group_id=event.group_id, message="该群未开启此功能.."
|
||||
)
|
||||
except ActionFailed:
|
||||
pass
|
||||
if event.is_tome():
|
||||
status_message_manager.add(event.group_id)
|
||||
set_block_limit_false(event, module)
|
||||
raise IgnoredException("未开启此功能...")
|
||||
# 管理员禁用
|
||||
if not group_manager.get_plugin_status(f"{module}:super", event.group_id):
|
||||
try:
|
||||
if (
|
||||
_flmt_s.check(event.group_id)
|
||||
and module not in ignore_rst_module
|
||||
):
|
||||
_flmt_s.start_cd(event.group_id)
|
||||
await bot.send_group_msg(
|
||||
group_id=event.group_id, message="管理员禁用了此群该功能..."
|
||||
)
|
||||
except ActionFailed:
|
||||
pass
|
||||
if event.is_tome():
|
||||
status_message_manager.add(event.group_id)
|
||||
set_block_limit_false(event, module)
|
||||
raise IgnoredException("管理员禁用了此群该功能...")
|
||||
# 群聊禁用
|
||||
if not plugins_manager.get_plugin_status(module, block_type="group"):
|
||||
try:
|
||||
if (
|
||||
_flmt_c.check(event.group_id)
|
||||
and module not in ignore_rst_module
|
||||
):
|
||||
_flmt_c.start_cd(event.group_id)
|
||||
await bot.send_group_msg(
|
||||
group_id=event.group_id, message="该功能在群聊中已被禁用..."
|
||||
)
|
||||
except ActionFailed:
|
||||
pass
|
||||
if event.is_tome():
|
||||
status_message_manager.add(event.group_id)
|
||||
set_block_limit_false(event, module)
|
||||
raise IgnoredException("该插件在群聊中已被禁用...")
|
||||
else:
|
||||
# 私聊禁用
|
||||
if not plugins_manager.get_plugin_status(module, block_type="private"):
|
||||
try:
|
||||
if _flmt_c.check(event.user_id):
|
||||
_flmt_c.start_cd(event.user_id)
|
||||
await bot.send_private_msg(
|
||||
user_id=event.user_id, message="该功能在私聊中已被禁用..."
|
||||
)
|
||||
except ActionFailed:
|
||||
pass
|
||||
if event.is_tome():
|
||||
status_message_manager.add(event.user_id)
|
||||
set_block_limit_false(event, module)
|
||||
raise IgnoredException("该插件在私聊中已被禁用...")
|
||||
# 维护
|
||||
if not plugins_manager.get_plugin_status(module, block_type="all"):
|
||||
if isinstance(
|
||||
event, GroupMessageEvent
|
||||
) and group_manager.check_group_is_white(event.group_id):
|
||||
return
|
||||
try:
|
||||
if isinstance(event, GroupMessageEvent):
|
||||
if (
|
||||
_flmt_c.check(event.group_id)
|
||||
and module not in ignore_rst_module
|
||||
):
|
||||
_flmt_c.start_cd(event.group_id)
|
||||
await bot.send_group_msg(
|
||||
group_id=event.group_id, message="此功能正在维护..."
|
||||
)
|
||||
else:
|
||||
await bot.send_private_msg(
|
||||
user_id=event.user_id, message="此功能正在维护..."
|
||||
)
|
||||
except ActionFailed:
|
||||
pass
|
||||
if event.is_tome():
|
||||
id_ = (
|
||||
event.group_id
|
||||
if isinstance(event, GroupMessageEvent)
|
||||
else event.user_id
|
||||
)
|
||||
status_message_manager.add(id_)
|
||||
set_block_limit_false(event, module)
|
||||
raise IgnoredException("此功能正在维护...")
|
||||
|
||||
# 以下为限制检测 #######################################################
|
||||
# 以下为限制检测 #######################################################
|
||||
# 以下为限制检测 #######################################################
|
||||
# 以下为限制检测 #######################################################
|
||||
# 以下为限制检测 #######################################################
|
||||
# 以下为限制检测 #######################################################
|
||||
# Cd
|
||||
if plugins2cd_manager.check_plugin_cd_status(module):
|
||||
plugin_cd_data = plugins2cd_manager.get_plugin_cd_data(module)
|
||||
check_type = plugin_cd_data["check_type"]
|
||||
limit_type = plugin_cd_data["limit_type"]
|
||||
rst = plugin_cd_data["rst"]
|
||||
if (
|
||||
(isinstance(event, PrivateMessageEvent) and check_type == "private")
|
||||
or (isinstance(event, GroupMessageEvent) and check_type == "group")
|
||||
or plugins2cd_manager.get_plugin_data(module).get("check_type") == "all"
|
||||
):
|
||||
cd_type_ = event.user_id
|
||||
if limit_type == "group" and isinstance(event, GroupMessageEvent):
|
||||
cd_type_ = event.group_id
|
||||
if not plugins2cd_manager.check(module, cd_type_):
|
||||
if rst:
|
||||
rst = await init_rst(rst, event)
|
||||
await send_msg(rst, bot, event)
|
||||
raise IgnoredException(f"{module} 正在cd中...")
|
||||
else:
|
||||
plugins2cd_manager.start_cd(module, cd_type_)
|
||||
# Block
|
||||
if plugins2block_manager.check_plugin_block_status(module):
|
||||
plugin_block_data = plugins2block_manager.get_plugin_block_data(module)
|
||||
check_type = plugin_block_data["check_type"]
|
||||
limit_type = plugin_block_data["limit_type"]
|
||||
rst = plugin_block_data["rst"]
|
||||
if (
|
||||
(isinstance(event, PrivateMessageEvent) and check_type == "private")
|
||||
or (isinstance(event, GroupMessageEvent) and check_type == "group")
|
||||
or check_type == "all"
|
||||
):
|
||||
block_type_ = event.user_id
|
||||
if limit_type == "group" and isinstance(event, GroupMessageEvent):
|
||||
block_type_ = event.group_id
|
||||
if plugins2block_manager.check(block_type_, module):
|
||||
if rst:
|
||||
rst = await init_rst(rst, event)
|
||||
await send_msg(rst, bot, event)
|
||||
raise IgnoredException(f"{event.user_id}正在调用{module}....")
|
||||
else:
|
||||
plugins2block_manager.set_true(block_type_, module)
|
||||
# Count
|
||||
if (
|
||||
plugins2count_manager.check_plugin_count_status(module)
|
||||
and event.user_id not in bot.config.superusers
|
||||
):
|
||||
plugin_count_data = plugins2count_manager.get_plugin_count_data(module)
|
||||
limit_type = plugin_count_data["limit_type"]
|
||||
rst = plugin_count_data["rst"]
|
||||
count_type_ = event.user_id
|
||||
if limit_type == "group" and isinstance(event, GroupMessageEvent):
|
||||
count_type_ = event.group_id
|
||||
if not plugins2count_manager.check(module, count_type_):
|
||||
if rst:
|
||||
rst = await init_rst(rst, event)
|
||||
await send_msg(rst, bot, event)
|
||||
raise IgnoredException(f"{module} count次数限制...")
|
||||
else:
|
||||
plugins2count_manager.increase(module, count_type_)
|
||||
# 功能花费的金币 #######################################
|
||||
# 功能花费的金币 #######################################
|
||||
if cost_gold:
|
||||
await BagUser.spend_gold(event.user_id, event.group_id, cost_gold)
|
||||
|
||||
|
||||
async def send_msg(rst: str, bot: Bot, event: MessageEvent):
|
||||
"""
|
||||
发送信息
|
||||
:param rst: pass
|
||||
:param bot: pass
|
||||
:param event: pass
|
||||
"""
|
||||
rst = await init_rst(rst, event)
|
||||
try:
|
||||
if isinstance(event, GroupMessageEvent):
|
||||
status_message_manager.add(event.group_id)
|
||||
await bot.send_group_msg(group_id=event.group_id, message=Message(rst))
|
||||
else:
|
||||
status_message_manager.add(event.user_id)
|
||||
await bot.send_private_msg(user_id=event.user_id, message=Message(rst))
|
||||
except ActionFailed:
|
||||
pass
|
||||
async def _(matcher: Matcher, bot: Bot, event: Event):
|
||||
await AuthChecker().auth(matcher, bot, event)
|
||||
|
||||
|
||||
# 解除命令block阻塞
|
||||
@run_postprocessor
|
||||
async def _(
|
||||
matcher: Matcher,
|
||||
exception: Optional[Exception],
|
||||
bot: Bot,
|
||||
event: Event,
|
||||
state: T_State,
|
||||
matcher: Matcher,
|
||||
exception: Optional[Exception],
|
||||
bot: Bot,
|
||||
event: Event,
|
||||
state: T_State,
|
||||
):
|
||||
if not isinstance(event, MessageEvent) and matcher.plugin_name != "poke":
|
||||
return
|
||||
module = matcher.plugin_name
|
||||
set_block_limit_false(event, module)
|
||||
|
||||
|
||||
async def init_rst(rst: str, event: MessageEvent):
|
||||
if "[uname]" in rst:
|
||||
uname = event.sender.card or event.sender.nickname
|
||||
rst = rst.replace("[uname]", uname)
|
||||
if "[nickname]" in rst:
|
||||
if isinstance(event, GroupMessageEvent):
|
||||
nickname = await GroupInfoUser.get_group_member_nickname(
|
||||
event.user_id, event.group_id
|
||||
)
|
||||
else:
|
||||
nickname = await FriendUser.get_friend_nickname(event.user_id)
|
||||
rst = rst.replace("[nickname]", nickname)
|
||||
if "[at]" in rst and isinstance(event, GroupMessageEvent):
|
||||
rst = rst.replace("[at]", str(at(event.user_id)))
|
||||
return rst
|
||||
|
||||
@@ -1,20 +1,21 @@
|
||||
from nonebot.matcher import Matcher
|
||||
from nonebot.message import run_preprocessor, IgnoredException
|
||||
from nonebot.adapters.onebot.v11.exception import ActionFailed
|
||||
from nonebot.typing import T_State
|
||||
from nonebot.adapters.onebot.v11 import (
|
||||
ActionFailed,
|
||||
Bot,
|
||||
Event,
|
||||
GroupMessageEvent,
|
||||
MessageEvent,
|
||||
PokeNotifyEvent,
|
||||
GroupMessageEvent,
|
||||
)
|
||||
from nonebot.matcher import Matcher
|
||||
from nonebot.message import IgnoredException, run_preprocessor
|
||||
from nonebot.typing import T_State
|
||||
|
||||
from configs.config import Config
|
||||
from models.ban_user import BanUser
|
||||
from utils.utils import is_number, static_flmt, FreqLimiter
|
||||
from utils.message_builder import at
|
||||
from ._utils import ignore_rst_module, other_limit_plugins
|
||||
from utils.utils import FreqLimiter, is_number, static_flmt
|
||||
|
||||
from ._utils import ignore_rst_module, other_limit_plugins
|
||||
|
||||
Config.add_plugin_config(
|
||||
"hook",
|
||||
@@ -29,10 +30,13 @@ _flmt = FreqLimiter(300)
|
||||
# 检查是否被ban
|
||||
@run_preprocessor
|
||||
async def _(matcher: Matcher, bot: Bot, event: Event, state: T_State):
|
||||
if (
|
||||
(isinstance(event, MessageEvent) or isinstance(event, PokeNotifyEvent))
|
||||
and matcher.priority not in [1, 9]
|
||||
) or matcher.plugin_name in other_limit_plugins:
|
||||
if hasattr(event, "user_id") and (
|
||||
(
|
||||
(isinstance(event, MessageEvent) or isinstance(event, PokeNotifyEvent))
|
||||
and matcher.priority not in [1, 999]
|
||||
)
|
||||
or matcher.plugin_name in other_limit_plugins
|
||||
):
|
||||
try:
|
||||
if (
|
||||
await BanUser.is_super_ban(event.user_id)
|
||||
@@ -46,7 +50,7 @@ async def _(matcher: Matcher, bot: Bot, event: Event, state: T_State):
|
||||
and str(event.user_id) not in bot.config.superusers
|
||||
):
|
||||
time = await BanUser.check_ban_time(event.user_id)
|
||||
if is_number(time):
|
||||
if isinstance(time, int):
|
||||
time = abs(int(time))
|
||||
if time < 60:
|
||||
time = str(time) + " 秒"
|
||||
@@ -58,7 +62,7 @@ async def _(matcher: Matcher, bot: Bot, event: Event, state: T_State):
|
||||
if not static_flmt.check(event.user_id):
|
||||
raise IgnoredException("用户处于黑名单中")
|
||||
static_flmt.start_cd(event.user_id)
|
||||
if matcher.priority != 9:
|
||||
if matcher.priority != 999:
|
||||
try:
|
||||
ban_result = Config.get_config("hook", "BAN_RESULT")
|
||||
if (
|
||||
@@ -79,7 +83,7 @@ async def _(matcher: Matcher, bot: Bot, event: Event, state: T_State):
|
||||
if not static_flmt.check(event.user_id):
|
||||
raise IgnoredException("用户处于黑名单中")
|
||||
static_flmt.start_cd(event.user_id)
|
||||
if matcher.priority != 9:
|
||||
if matcher.priority != 999:
|
||||
try:
|
||||
ban_result = Config.get_config("hook", "BAN_RESULT")
|
||||
if ban_result and matcher.plugin_name not in ignore_rst_module:
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
from nonebot.matcher import Matcher
|
||||
from nonebot.message import run_preprocessor, IgnoredException
|
||||
from nonebot.adapters.onebot.v11.exception import ActionFailed
|
||||
from nonebot.typing import T_State
|
||||
from nonebot.adapters.onebot.v11 import (
|
||||
Bot,
|
||||
ActionFailed,
|
||||
MessageEvent,
|
||||
GroupMessageEvent,
|
||||
)
|
||||
@@ -25,15 +25,15 @@ _blmt = BanCheckLimiter(
|
||||
async def _(matcher: Matcher, bot: Bot, event: GroupMessageEvent, state: T_State):
|
||||
if not isinstance(event, MessageEvent):
|
||||
return
|
||||
if matcher.type == "message" and matcher.priority not in [1, 9]:
|
||||
if matcher.type == "message" and matcher.priority not in [1, 999]:
|
||||
if state["_prefix"]["raw_command"]:
|
||||
if _blmt.check(f'{event.user_id}{state["_prefix"]["raw_command"]}'):
|
||||
if await BanUser.ban(
|
||||
await BanUser.ban(
|
||||
event.user_id,
|
||||
9,
|
||||
Config.get_config("hook", "MALICIOUS_BAN_TIME") * 60,
|
||||
):
|
||||
logger.info(f"USER {event.user_id} 触发了恶意触发检测")
|
||||
)
|
||||
logger.info(f"USER {event.user_id} 触发了恶意触发检测")
|
||||
if isinstance(event, GroupMessageEvent):
|
||||
try:
|
||||
await bot.send_group_msg(
|
||||
|
||||
@@ -2,15 +2,12 @@ from nonebot.matcher import Matcher
|
||||
from nonebot.message import run_preprocessor, IgnoredException
|
||||
from nonebot.typing import T_State
|
||||
from ._utils import status_message_manager
|
||||
from utils.image_utils import text2image
|
||||
from typing import Dict, Any
|
||||
from nonebot.adapters.onebot.v11 import (
|
||||
Bot,
|
||||
MessageEvent,
|
||||
PrivateMessageEvent,
|
||||
GroupMessageEvent,
|
||||
)
|
||||
import re
|
||||
|
||||
|
||||
# 为什么AI会自己和自己聊天
|
||||
@@ -41,11 +38,6 @@ async def _(matcher: Matcher, bot: Bot, event: MessageEvent, state: T_State):
|
||||
status_message_manager.delete(event.user_id)
|
||||
raise IgnoredException("有命令就别说话了")
|
||||
|
||||
# @Bot.on_calling_api
|
||||
# async def handle_api_call(bot: Bot, api: str, data: Dict[str, Any]):
|
||||
# if api in ["send_msg", "send_group_msg", "send_private_msg"]:
|
||||
# msg = str(data["message"])
|
||||
# if (r := re.search("\[\[To_Img\|?(.*?)]]", msg)) or (r := re.search("[[To_Img\|?(.*?)[[")):
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,47 +1,56 @@
|
||||
from nonebot.exception import MockApiException
|
||||
from nonebot.adapters.onebot.v11 import Bot, Message
|
||||
from utils.manager import group_manager
|
||||
from services.log import logger
|
||||
from typing import Dict, Any
|
||||
import re
|
||||
|
||||
|
||||
@Bot.on_calling_api
|
||||
async def handle_api_call(bot: Bot, api: str, data: Dict[str, Any]):
|
||||
async def _(bot: Bot, api: str, data: Dict[str, Any]):
|
||||
r = None
|
||||
if (
|
||||
(
|
||||
(api == "send_msg" and data.get("message_type") == "group")
|
||||
or api == "send_group_msg"
|
||||
)
|
||||
and (
|
||||
task = None
|
||||
group_id = None
|
||||
try:
|
||||
if (
|
||||
(
|
||||
r := re.search(
|
||||
"^\[\[_task\|(.*)]]",
|
||||
data["message"].strip()
|
||||
if isinstance(data["message"], str)
|
||||
else str(data["message"]["text"]).strip(),
|
||||
(api == "send_msg" and data.get("message_type") == "group")
|
||||
or api == "send_group_msg"
|
||||
)
|
||||
and (
|
||||
(
|
||||
r := re.search(
|
||||
"^\[\[_task\|(.*)]]",
|
||||
data["message"].strip()
|
||||
if isinstance(data["message"], str)
|
||||
else str(data["message"]["text"]).strip(),
|
||||
)
|
||||
)
|
||||
or (
|
||||
r := re.search(
|
||||
"^[[_task\|(.*)]]",
|
||||
data["message"].strip()
|
||||
if isinstance(data["message"], str)
|
||||
else str(data["message"]["text"]).strip(),
|
||||
)
|
||||
)
|
||||
)
|
||||
or (
|
||||
r := re.search(
|
||||
"^[[_task\|(.*)]]",
|
||||
data["message"].strip()
|
||||
if isinstance(data["message"], str)
|
||||
else str(data["message"]["text"]).strip(),
|
||||
and r.group(1) in group_manager.get_task_data().keys()
|
||||
):
|
||||
task = r.group(1)
|
||||
group_id = data["group_id"]
|
||||
except Exception as e:
|
||||
logger.error(f"TaskHook ERROR {type(e)}:{e}")
|
||||
else:
|
||||
if task and group_id:
|
||||
if (
|
||||
group_manager.get_group_level(group_id) < 0
|
||||
or not group_manager.check_task_status(task, group_id)
|
||||
):
|
||||
raise MockApiException(f"被动技能 {task} 处于关闭状态...")
|
||||
else:
|
||||
msg = str(data["message"]).strip()
|
||||
msg = msg.replace(f"[[_task|{task}]]", "").replace(
|
||||
f"[[_task|{task}]]", ""
|
||||
)
|
||||
)
|
||||
)
|
||||
and r.group(1) in group_manager.get_task_data().keys()
|
||||
):
|
||||
task = r.group(1)
|
||||
group_id = data["group_id"]
|
||||
if group_manager.get_group_level(
|
||||
group_id
|
||||
) < 0 or not await group_manager.check_group_task_status(group_id, task):
|
||||
raise MockApiException(f"被动技能 {task} 处于关闭状态...")
|
||||
else:
|
||||
msg = str(data["message"]).strip()
|
||||
msg = msg.replace(f"[[_task|{task}]]", "").replace(
|
||||
f"[[_task|{task}]]", ""
|
||||
)
|
||||
data["message"] = Message(msg)
|
||||
data["message"] = Message(msg)
|
||||
|
||||
@@ -1,23 +1,23 @@
|
||||
from .init_group_manager import init_group_manager, group_manager
|
||||
from .init_plugins_config import init_plugins_config
|
||||
from .init_plugins_data import init_plugins_data, plugins_manager
|
||||
from .init_none_plugin_count_manager import init_none_plugin_count_manager
|
||||
from .init_plugins_resources import init_plugins_resources
|
||||
from .init_plugins_settings import init_plugins_settings
|
||||
from .init_plugins_limit import (
|
||||
init_plugins_block_limit,
|
||||
init_plugins_count_limit,
|
||||
init_plugins_cd_limit,
|
||||
)
|
||||
from .init import init
|
||||
from .check_plugin_status import check_plugin_status
|
||||
import nonebot
|
||||
from nonebot import Driver
|
||||
from nonebot.adapters.onebot.v11 import Bot
|
||||
|
||||
from configs.path_config import DATA_PATH
|
||||
from services.log import logger
|
||||
from pathlib import Path
|
||||
from nonebot import Driver
|
||||
import nonebot
|
||||
|
||||
from .check_plugin_status import check_plugin_status
|
||||
from .init import init
|
||||
from .init_none_plugin_count_manager import init_none_plugin_count_manager
|
||||
from .init_plugin_info import init_plugin_info
|
||||
from .init_plugins_config import init_plugins_config
|
||||
from .init_plugins_data import init_plugins_data, plugins_manager
|
||||
from .init_plugins_limit import (
|
||||
init_plugins_block_limit,
|
||||
init_plugins_cd_limit,
|
||||
init_plugins_count_limit,
|
||||
)
|
||||
from .init_plugins_resources import init_plugins_resources
|
||||
from .init_plugins_settings import init_plugins_settings
|
||||
|
||||
__zx_plugin_name__ = "初始化插件数据 [Hidden]"
|
||||
__plugin_version__ = 0.1
|
||||
@@ -28,27 +28,22 @@ driver: Driver = nonebot.get_driver()
|
||||
|
||||
|
||||
@driver.on_startup
|
||||
def _():
|
||||
async def _():
|
||||
"""
|
||||
初始化数据
|
||||
"""
|
||||
_flag = False
|
||||
config_file = DATA_PATH / "configs" / "plugins2config.yaml"
|
||||
if not config_file.exists():
|
||||
_flag = True
|
||||
_flag = not config_file.exists()
|
||||
init()
|
||||
init_plugins_settings(DATA_PATH)
|
||||
init_plugins_cd_limit(DATA_PATH)
|
||||
init_plugins_block_limit(DATA_PATH)
|
||||
init_plugins_count_limit(DATA_PATH)
|
||||
init_plugins_data(DATA_PATH)
|
||||
init_plugins_config(DATA_PATH)
|
||||
init_plugin_info()
|
||||
init_plugins_settings()
|
||||
init_plugins_cd_limit()
|
||||
init_plugins_block_limit()
|
||||
init_plugins_count_limit()
|
||||
init_plugins_data()
|
||||
init_plugins_config()
|
||||
init_plugins_resources()
|
||||
init_none_plugin_count_manager()
|
||||
x = group_manager.get_super_old_data()
|
||||
if x:
|
||||
for key in x.keys():
|
||||
plugins_manager.block_plugin(key, block_type=x[key])
|
||||
if _flag:
|
||||
raise Exception("首次运行,已在configs目录下生成配置文件config.yaml,修改后重启即可...")
|
||||
logger.info("初始化数据完成...")
|
||||
@@ -56,5 +51,5 @@ def _():
|
||||
|
||||
@driver.on_bot_connect
|
||||
async def _(bot: Bot):
|
||||
await init_group_manager()
|
||||
# await init_group_manager()
|
||||
await check_plugin_status(bot)
|
||||
|
||||
@@ -6,13 +6,13 @@ async def check_plugin_status(bot: Bot):
|
||||
"""
|
||||
遍历查看插件加载情况
|
||||
"""
|
||||
rst = ""
|
||||
msg = ""
|
||||
for plugin in plugins_manager.keys():
|
||||
data = plugins_manager.get(plugin)
|
||||
if data.get("error") or data.get("error") is None:
|
||||
rst += f'{plugin}:{data["plugin_name"]}\n'
|
||||
if rst:
|
||||
rst = "以下插件加载失败..\n" + rst
|
||||
if data.error:
|
||||
msg += f'{plugin}:{data.plugin_name}\n'
|
||||
if msg and bot.config.superusers:
|
||||
msg = "以下插件加载失败..\n" + msg
|
||||
await bot.send_private_msg(
|
||||
user_id=int(list(bot.config.superusers)[0]), message=rst[:-1]
|
||||
user_id=int(list(bot.config.superusers)[0]), message=msg.strip()
|
||||
)
|
||||
|
||||
@@ -1,73 +0,0 @@
|
||||
from pathlib import Path
|
||||
from utils.manager import group_manager
|
||||
from services.db_context import db
|
||||
from asyncpg.exceptions import DuplicateColumnError
|
||||
from services.log import logger
|
||||
|
||||
try:
|
||||
import ujson as json
|
||||
except ModuleNotFoundError:
|
||||
import json
|
||||
try:
|
||||
from models.group_remind import GroupRemind
|
||||
except ModuleNotFoundError:
|
||||
pass
|
||||
|
||||
|
||||
async def init_group_manager():
|
||||
"""
|
||||
旧数据格式替换为新格式
|
||||
初始化数据
|
||||
"""
|
||||
old_group_level_file = Path() / "data" / "manager" / "group_level.json"
|
||||
old_plugin_list_file = Path() / "data" / "manager" / "plugin_list.json"
|
||||
if old_group_level_file.exists():
|
||||
data = json.load(open(old_group_level_file, "r", encoding="utf8"))
|
||||
for key in data.keys():
|
||||
group = key
|
||||
level = data[key]
|
||||
group_manager.set_group_level(group, level)
|
||||
old_group_level_file.unlink()
|
||||
group_manager.save()
|
||||
|
||||
if old_plugin_list_file.exists():
|
||||
data = json.load(open(old_plugin_list_file, "r", encoding="utf8"))
|
||||
for plugin in data.keys():
|
||||
for group in data[plugin].keys():
|
||||
if group == "default" and not data[plugin]["default"]:
|
||||
group_manager.block_plugin(plugin)
|
||||
elif not data[plugin][group]:
|
||||
group_manager.block_plugin(plugin, group)
|
||||
old_plugin_list_file.unlink()
|
||||
old_data_table = Path() / "models" / "group_remind.py"
|
||||
try:
|
||||
if old_data_table.exists():
|
||||
b = {
|
||||
"hy": "group_welcome",
|
||||
"kxcz": "open_case_reset_remind",
|
||||
"zwa": "zwa",
|
||||
"blpar": "bilibili_parse",
|
||||
"epic": "epic_free_game",
|
||||
"pa": "pa",
|
||||
"almanac": "genshin_alc",
|
||||
}
|
||||
for group in group_manager.get_data()["group_manager"]:
|
||||
for remind in b:
|
||||
try:
|
||||
status = await GroupRemind.get_status(int(group), remind)
|
||||
if status is not None:
|
||||
if status:
|
||||
await group_manager.open_group_task(group, b[remind])
|
||||
logger.info(f"读取旧数据-->{group} 开启 {b[remind]}")
|
||||
else:
|
||||
await group_manager.close_group_task(group, b[remind])
|
||||
logger.info(f"读取旧数据-->{group} 关闭 {b[remind]}")
|
||||
except Exception as e:
|
||||
pass
|
||||
query = db.text("DROP TABLE group_reminds;")
|
||||
await db.first(query)
|
||||
old_data_table.unlink()
|
||||
logger.info("旧数据读取完毕,删除了舍弃表 group_reminds...")
|
||||
except (ModuleNotFoundError, DuplicateColumnError):
|
||||
pass
|
||||
group_manager.save()
|
||||
@@ -5,7 +5,6 @@ from utils.manager import (
|
||||
plugins2settings_manager,
|
||||
plugins2block_manager,
|
||||
plugins_manager,
|
||||
resources_manager
|
||||
)
|
||||
from services.log import logger
|
||||
from utils.utils import get_matchers
|
||||
@@ -20,26 +19,44 @@ def init_none_plugin_count_manager():
|
||||
"""
|
||||
清除已删除插件数据
|
||||
"""
|
||||
modules = [x.module for x in get_matchers()]
|
||||
for module in none_plugin_count_manager.keys():
|
||||
if module not in modules:
|
||||
none_plugin_count_manager.add_count(module)
|
||||
else:
|
||||
none_plugin_count_manager.reset(module)
|
||||
if none_plugin_count_manager.check(module):
|
||||
try:
|
||||
plugin_name = plugins_manager.get(module)["plugin_name"]
|
||||
except (AttributeError, KeyError):
|
||||
plugin_name = ""
|
||||
try:
|
||||
plugins2settings_manager.delete(module)
|
||||
plugins2count_manager.delete(module)
|
||||
plugins2cd_manager.delete(module)
|
||||
plugins2block_manager.delete(module)
|
||||
plugins_manager.delete(module)
|
||||
resources_manager.remove_resource(module)
|
||||
logger.info(f"{module}:{plugin_name} 插件疑似已删除,清除对应插件数据...")
|
||||
except Exception as e:
|
||||
logger.error(
|
||||
f"{module}:{plugin_name} 插件疑似已删除,清除对应插件数据失败...{type(e)}:{e}"
|
||||
)
|
||||
modules = [x.plugin_name for x in get_matchers(True)]
|
||||
plugins_manager_list = list(plugins_manager.keys())
|
||||
for module in plugins_manager_list:
|
||||
try:
|
||||
if module not in modules or none_plugin_count_manager.check(module):
|
||||
try:
|
||||
plugin_name = plugins_manager.get(module).plugin_name
|
||||
except (AttributeError, KeyError):
|
||||
plugin_name = ""
|
||||
if none_plugin_count_manager.check(module):
|
||||
try:
|
||||
plugins2settings_manager.delete(module)
|
||||
plugins2count_manager.delete(module)
|
||||
plugins2cd_manager.delete(module)
|
||||
plugins2block_manager.delete(module)
|
||||
plugins_manager.delete(module)
|
||||
plugins_manager.save()
|
||||
# resources_manager.remove_resource(module)
|
||||
none_plugin_count_manager.delete(module)
|
||||
logger.info(f"{module}:{plugin_name} 插件疑似已删除,清除对应插件数据...")
|
||||
except Exception as e:
|
||||
logger.exception(
|
||||
f"{module}:{plugin_name} 插件疑似已删除,清除对应插件数据失败...{type(e)}:{e}"
|
||||
)
|
||||
else:
|
||||
none_plugin_count_manager.add_count(module)
|
||||
logger.info(
|
||||
f"{module}:{plugin_name} 插件疑似已删除,"
|
||||
f"加载{none_plugin_count_manager._max_count}次失败后将清除对应插件数据,"
|
||||
f"当前次数:{none_plugin_count_manager.get(module)}"
|
||||
)
|
||||
else:
|
||||
none_plugin_count_manager.reset(module)
|
||||
except Exception as e:
|
||||
logger.error(f"清除插件数据错误 {type(e)}:{e}")
|
||||
plugins2settings_manager.save()
|
||||
plugins2count_manager.save()
|
||||
plugins2cd_manager.save()
|
||||
plugins2block_manager.save()
|
||||
plugins_manager.save()
|
||||
none_plugin_count_manager.save()
|
||||
|
||||
@@ -0,0 +1,147 @@
|
||||
import random
|
||||
from types import ModuleType
|
||||
from typing import Any, Dict
|
||||
|
||||
from configs.config import Config
|
||||
from services import logger
|
||||
from utils.manager import (
|
||||
plugin_data_manager,
|
||||
plugins2block_manager,
|
||||
plugins2cd_manager,
|
||||
plugins2count_manager,
|
||||
plugins2settings_manager,
|
||||
plugins_manager,
|
||||
)
|
||||
from utils.manager.models import (
|
||||
Plugin,
|
||||
PluginBlock,
|
||||
PluginCd,
|
||||
PluginCount,
|
||||
PluginData,
|
||||
PluginSetting,
|
||||
PluginType,
|
||||
)
|
||||
from utils.utils import get_matchers
|
||||
|
||||
|
||||
def get_attr(module: ModuleType, name: str, default: Any = None) -> Any:
|
||||
"""
|
||||
说明:
|
||||
获取属性
|
||||
参数:
|
||||
:param module: module
|
||||
:param name: name
|
||||
:param default: default
|
||||
"""
|
||||
return getattr(module, name, None) or default
|
||||
|
||||
|
||||
def init_plugin_info():
|
||||
|
||||
for matcher in [x for x in get_matchers(True)]:
|
||||
try:
|
||||
if (plugin := matcher.plugin) and matcher.plugin_name:
|
||||
metadata = plugin.metadata
|
||||
extra = metadata.extra if metadata else {}
|
||||
if hasattr(plugin, "module"):
|
||||
module = plugin.module
|
||||
plugin_model = matcher.plugin_name
|
||||
plugin_name = (
|
||||
metadata.name
|
||||
if metadata and metadata.name
|
||||
else get_attr(module, "__zx_plugin_name__", matcher.plugin_name)
|
||||
)
|
||||
if not plugin_name:
|
||||
logger.warning(f"配置文件 模块:{plugin_model} 获取 plugin_name 失败...")
|
||||
continue
|
||||
if "[Admin]" in plugin_name:
|
||||
plugin_type = PluginType.ADMIN
|
||||
plugin_name = plugin_name.replace("[Admin]", "").strip()
|
||||
elif "[Hidden]" in plugin_name:
|
||||
plugin_type = PluginType.HIDDEN
|
||||
plugin_name = plugin_name.replace("[Hidden]", "").strip()
|
||||
elif "[Superuser]" in plugin_name:
|
||||
plugin_type = PluginType.SUPERUSER
|
||||
plugin_name = plugin_name.replace("[Superuser]", "").strip()
|
||||
else:
|
||||
plugin_type = PluginType.NORMAL
|
||||
plugin_usage = (
|
||||
metadata.usage
|
||||
if metadata and metadata.usage
|
||||
else get_attr(module, "__plugin_usage__")
|
||||
)
|
||||
plugin_des = (
|
||||
metadata.description
|
||||
if metadata and metadata.description
|
||||
else get_attr(module, "__plugin_des__")
|
||||
)
|
||||
menu_type = get_attr(module, "__plugin_type__") or ("normal",)
|
||||
plugin_setting = get_attr(module, "__plugin_settings__")
|
||||
if plugin_setting:
|
||||
plugin_setting = PluginSetting(**plugin_setting)
|
||||
plugin_setting.plugin_type = menu_type
|
||||
plugin_superuser_usage = get_attr(module, "__plugin_super_usage__")
|
||||
plugin_task = get_attr(module, "__plugin_task__")
|
||||
plugin_version = extra.get("__plugin_version__") or get_attr(
|
||||
module, "__plugin_version__"
|
||||
)
|
||||
plugin_author = extra.get("__plugin_author__") or get_attr(
|
||||
module, "__plugin_author__"
|
||||
)
|
||||
plugin_cd = get_attr(module, "__plugin_cd_limit__")
|
||||
if plugin_cd:
|
||||
plugin_cd = PluginCd(**plugin_cd)
|
||||
plugin_block = get_attr(module, "__plugin_block_limit__")
|
||||
if plugin_block:
|
||||
plugin_block = PluginBlock(**plugin_block)
|
||||
plugin_count = get_attr(module, "__plugin_count_limit__")
|
||||
if plugin_count:
|
||||
plugin_count = PluginCount(**plugin_count)
|
||||
plugin_resources = get_attr(module, "__plugin_resources__")
|
||||
plugin_configs = get_attr(module, "__plugin_configs__")
|
||||
if settings := plugins2settings_manager.get(plugin_model):
|
||||
plugin_setting = settings
|
||||
if plugin_cd_limit := plugins2cd_manager.get(plugin_model):
|
||||
plugin_cd = plugin_cd_limit
|
||||
if plugin_block_limit := plugins2block_manager.get(plugin_model):
|
||||
plugin_block = plugin_block_limit
|
||||
if plugin_count_limit := plugins2count_manager.get(plugin_model):
|
||||
plugin_count = plugin_count_limit
|
||||
if plugin_cfg := Config.get(plugin_model):
|
||||
if plugin_configs:
|
||||
for config_name in plugin_configs:
|
||||
config: Dict[str, Any] = plugin_configs[config_name] # type: ignore
|
||||
Config.add_plugin_config(
|
||||
plugin_model,
|
||||
config_name,
|
||||
config.get("value"),
|
||||
help_=config.get("help"),
|
||||
default_value=config.get("default_value"),
|
||||
type=config.get("type"),
|
||||
)
|
||||
plugin_configs = plugin_cfg.configs
|
||||
plugin_status = plugins_manager.get(plugin_model)
|
||||
if not plugin_status:
|
||||
plugin_status = Plugin(plugin_name=plugin_model)
|
||||
plugin_status.author = plugin_author
|
||||
plugin_status.version = plugin_version
|
||||
plugin_data = PluginData(
|
||||
model=plugin_model,
|
||||
name=plugin_name.strip(),
|
||||
plugin_type=plugin_type,
|
||||
usage=plugin_usage,
|
||||
superuser_usage=plugin_superuser_usage,
|
||||
des=plugin_des,
|
||||
task=plugin_task,
|
||||
menu_type=menu_type,
|
||||
plugin_setting=plugin_setting,
|
||||
plugin_cd=plugin_cd,
|
||||
plugin_block=plugin_block,
|
||||
plugin_count=plugin_count,
|
||||
plugin_resources=plugin_resources,
|
||||
plugin_configs=plugin_configs, # type: ignore
|
||||
plugin_status=plugin_status,
|
||||
)
|
||||
plugin_data_manager.add_plugin_info(plugin_data)
|
||||
except Exception as e:
|
||||
logger.error(f"构造插件数据失败 {matcher.plugin_name}", e=e)
|
||||
@@ -1,79 +1,86 @@
|
||||
from pathlib import Path
|
||||
from ruamel.yaml import round_trip_load, round_trip_dump, YAML
|
||||
from utils.manager import admin_manager, plugins_manager
|
||||
from configs.config import Config
|
||||
from services.log import logger
|
||||
from utils.utils import get_matchers
|
||||
from ruamel import yaml
|
||||
import nonebot
|
||||
|
||||
from ruamel import yaml
|
||||
from ruamel.yaml import YAML, round_trip_dump, round_trip_load
|
||||
|
||||
from configs.config import Config
|
||||
from configs.path_config import DATA_PATH
|
||||
from services.log import logger
|
||||
from utils.manager import admin_manager, plugin_data_manager, plugins_manager
|
||||
from utils.text_utils import prompt2cn
|
||||
from utils.utils import get_matchers
|
||||
|
||||
_yaml = YAML(typ="safe")
|
||||
|
||||
|
||||
def init_plugins_config(data_path):
|
||||
def init_plugins_config():
|
||||
"""
|
||||
初始化插件数据配置
|
||||
"""
|
||||
plugins2config_file = data_path / "configs" / "plugins2config.yaml"
|
||||
plugins2config_file.parent.mkdir(parents=True, exist_ok=True)
|
||||
_data = {}
|
||||
if plugins2config_file.exists():
|
||||
_data = _yaml.load(open(plugins2config_file, "r", encoding="utf8"))
|
||||
_matchers = get_matchers()
|
||||
for matcher in _matchers:
|
||||
_plugin = nonebot.plugin.get_plugin(matcher.plugin_name)
|
||||
try:
|
||||
_module = _plugin.module
|
||||
except AttributeError:
|
||||
continue
|
||||
try:
|
||||
plugin_version = _module.__getattribute__("__plugin_version__")
|
||||
except AttributeError:
|
||||
plugin_version = None
|
||||
try:
|
||||
plugin_configs = _module.__getattribute__("__plugin_configs__")
|
||||
except AttributeError:
|
||||
continue
|
||||
# 插件配置版本更新或为Version为None或不在存储配置内
|
||||
if (
|
||||
plugin_version is None
|
||||
or (
|
||||
_data.get(matcher.plugin_name)
|
||||
and _data[matcher.plugin_name].keys() != plugin_configs.keys()
|
||||
)
|
||||
or plugin_version > plugins_manager.get(matcher.plugin_name)["version"]
|
||||
or matcher.plugin_name not in _data.keys()
|
||||
):
|
||||
for key in plugin_configs:
|
||||
if isinstance(plugin_configs[key], dict):
|
||||
Config.add_plugin_config(
|
||||
matcher.plugin_name,
|
||||
key,
|
||||
plugin_configs[key].get("value"),
|
||||
help_=plugin_configs[key].get("help"),
|
||||
default_value=plugin_configs[key].get("default_value"),
|
||||
_override=True,
|
||||
plugins2config_file = DATA_PATH / "configs" / "plugins2config.yaml"
|
||||
_data = Config.get_data()
|
||||
# 优先使用 metadata 数据
|
||||
for matcher in get_matchers(True):
|
||||
if matcher.plugin_name:
|
||||
if plugin_data := plugin_data_manager.get(matcher.plugin_name):
|
||||
# 插件配置版本更新或为Version为None或不在存储配置内,当使用metadata时,必定更新
|
||||
version = plugin_data.plugin_status.version
|
||||
config = _data.get(matcher.plugin_name)
|
||||
plugin = plugins_manager.get(matcher.plugin_name)
|
||||
if plugin_data.plugin_configs and (
|
||||
isinstance(version, str)
|
||||
or (
|
||||
version is None
|
||||
or (
|
||||
config
|
||||
and config.configs.keys()
|
||||
!= plugin_data.plugin_configs.keys()
|
||||
)
|
||||
or version > int(plugin.version or 0)
|
||||
or matcher.plugin_name not in _data.keys()
|
||||
)
|
||||
else:
|
||||
Config.add_plugin_config(matcher.plugin_name, key, plugin_configs[key])
|
||||
else:
|
||||
plugin_configs = _data[matcher.plugin_name]
|
||||
for key in plugin_configs:
|
||||
Config.add_plugin_config(
|
||||
matcher.plugin_name,
|
||||
key,
|
||||
plugin_configs[key]["value"],
|
||||
help_=plugin_configs[key]["help"],
|
||||
default_value=plugin_configs[key]["default_value"],
|
||||
_override=True,
|
||||
)
|
||||
):
|
||||
plugin_configs = plugin_data.plugin_configs
|
||||
for key in plugin_configs:
|
||||
if isinstance(plugin_data.plugin_configs[key], dict):
|
||||
Config.add_plugin_config(
|
||||
matcher.plugin_name,
|
||||
key,
|
||||
plugin_configs[key].get("value"),
|
||||
help_=plugin_configs[key].get("help"),
|
||||
default_value=plugin_configs[key].get("default_value"),
|
||||
_override=True,
|
||||
type=plugin_configs[key].get("type"),
|
||||
)
|
||||
else:
|
||||
config = plugin_configs[key]
|
||||
Config.add_plugin_config(
|
||||
matcher.plugin_name,
|
||||
key,
|
||||
config.value,
|
||||
name=config.name,
|
||||
help_=config.help,
|
||||
default_value=config.default_value,
|
||||
_override=True,
|
||||
type=config.type,
|
||||
)
|
||||
elif plugin_configs := _data.get(matcher.plugin_name):
|
||||
for key in plugin_configs.configs:
|
||||
Config.add_plugin_config(
|
||||
matcher.plugin_name,
|
||||
key,
|
||||
plugin_configs.configs[key].value,
|
||||
help_=plugin_configs.configs[key].help,
|
||||
default_value=plugin_configs.configs[key].default_value,
|
||||
_override=True,
|
||||
type=plugin_configs.configs[key].type,
|
||||
)
|
||||
if not Config.is_empty():
|
||||
Config.save()
|
||||
_data = round_trip_load(open(plugins2config_file, encoding="utf8"))
|
||||
for plugin in _data.keys():
|
||||
try:
|
||||
plugin_name = plugins_manager.get(plugin)["plugin_name"]
|
||||
plugin_name = plugins_manager.get(plugin).plugin_name
|
||||
except (AttributeError, TypeError):
|
||||
plugin_name = plugin
|
||||
_data[plugin].yaml_set_start_comment(plugin_name, indent=2)
|
||||
@@ -85,69 +92,82 @@ def init_plugins_config(data_path):
|
||||
round_trip_dump(
|
||||
_data, wf, indent=2, Dumper=yaml.RoundTripDumper, allow_unicode=True
|
||||
)
|
||||
# 再开始读取用户配置
|
||||
user_config_file = Path() / "configs" / "config.yaml"
|
||||
_data = {}
|
||||
_tmp_data = {}
|
||||
if user_config_file.exists():
|
||||
with open(user_config_file, "r", encoding="utf8") as f:
|
||||
_data = _yaml.load(f)
|
||||
# 数据替换
|
||||
for plugin in Config.keys():
|
||||
_tmp_data[plugin] = {}
|
||||
for k in Config[plugin].keys():
|
||||
_replace_config()
|
||||
|
||||
|
||||
def _replace_config():
|
||||
"""
|
||||
说明:
|
||||
定时任务加载的配置读取替换
|
||||
"""
|
||||
# 再开始读取用户配置
|
||||
user_config_file = Path() / "configs" / "config.yaml"
|
||||
_data = {}
|
||||
_tmp_data = {}
|
||||
if user_config_file.exists():
|
||||
with open(user_config_file, "r", encoding="utf8") as f:
|
||||
_data = _yaml.load(f)
|
||||
# 数据替换
|
||||
for plugin in Config.keys():
|
||||
_tmp_data[plugin] = {}
|
||||
for k in Config[plugin].configs.keys():
|
||||
try:
|
||||
if _data.get(plugin) and k in _data[plugin].keys():
|
||||
Config.set_config(plugin, k, _data[plugin][k])
|
||||
if level2module := Config.get_level2module(plugin, k):
|
||||
try:
|
||||
admin_manager.set_admin_level(level2module, _data[plugin][k])
|
||||
admin_manager.set_admin_level(
|
||||
level2module, _data[plugin][k]
|
||||
)
|
||||
except KeyError:
|
||||
logger.warning(f"{level2module} 设置权限等级失败:{_data[plugin][k]}")
|
||||
logger.warning(
|
||||
f"{level2module} 设置权限等级失败:{_data[plugin][k]}"
|
||||
)
|
||||
_tmp_data[plugin][k] = Config.get_config(plugin, k)
|
||||
Config.save()
|
||||
temp_file = Path() / "configs" / "temp_config.yaml"
|
||||
try:
|
||||
with open(temp_file, "w", encoding="utf8") as wf:
|
||||
yaml.dump(
|
||||
_tmp_data, wf, Dumper=yaml.RoundTripDumper, allow_unicode=True
|
||||
except AttributeError as e:
|
||||
raise AttributeError(
|
||||
f"{e}\n" + prompt2cn("可能为config.yaml配置文件填写不规范", 46)
|
||||
)
|
||||
with open(temp_file, "r", encoding="utf8") as rf:
|
||||
_data = round_trip_load(rf)
|
||||
# 添加注释
|
||||
for plugin in _data.keys():
|
||||
rst = ""
|
||||
plugin_name = None
|
||||
try:
|
||||
plugin_data = Config.get(plugin)
|
||||
for x in list(Config.get(plugin).keys()):
|
||||
Config.save()
|
||||
temp_file = Path() / "configs" / "temp_config.yaml"
|
||||
try:
|
||||
with open(temp_file, "w", encoding="utf8") as wf:
|
||||
yaml.dump(_tmp_data, wf, Dumper=yaml.RoundTripDumper, allow_unicode=True)
|
||||
with open(temp_file, "r", encoding="utf8") as rf:
|
||||
_data = round_trip_load(rf)
|
||||
# 添加注释
|
||||
for plugin in _data.keys():
|
||||
rst = ""
|
||||
plugin_name = None
|
||||
try:
|
||||
if config_group := Config.get(plugin):
|
||||
for key in list(config_group.configs.keys()):
|
||||
try:
|
||||
_x = plugin_data[x].get("name")
|
||||
if _x:
|
||||
plugin_name = _x
|
||||
if config := config_group.configs[key]:
|
||||
if config.name:
|
||||
plugin_name = config.name
|
||||
except AttributeError:
|
||||
pass
|
||||
except (KeyError, AttributeError):
|
||||
plugin_name = None
|
||||
if not plugin_name:
|
||||
try:
|
||||
plugin_name = plugins_manager.get(plugin)["plugin_name"]
|
||||
except (AttributeError, TypeError):
|
||||
plugin_name = plugin
|
||||
plugin_name = (
|
||||
plugin_name.replace("[Hidden]", "")
|
||||
.replace("[Superuser]", "")
|
||||
.replace("[Admin]", "")
|
||||
.strip()
|
||||
)
|
||||
rst += plugin_name + "\n"
|
||||
for k in _data[plugin].keys():
|
||||
rst += f'{k}: {Config[plugin][k]["help"]}' + "\n"
|
||||
_data[plugin].yaml_set_start_comment(rst[:-1], indent=2)
|
||||
with open(Path() / "configs" / "config.yaml", "w", encoding="utf8") as wf:
|
||||
round_trip_dump(
|
||||
_data, wf, Dumper=yaml.RoundTripDumper, allow_unicode=True
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f"生成简易配置注释错误 {type(e)}:{e}")
|
||||
if temp_file.exists():
|
||||
temp_file.unlink()
|
||||
except (KeyError, AttributeError):
|
||||
plugin_name = None
|
||||
if not plugin_name:
|
||||
try:
|
||||
plugin_name = plugins_manager.get(plugin).plugin_name
|
||||
except (AttributeError, TypeError):
|
||||
plugin_name = plugin
|
||||
plugin_name = (
|
||||
plugin_name.replace("[Hidden]", "")
|
||||
.replace("[Superuser]", "")
|
||||
.replace("[Admin]", "")
|
||||
.strip()
|
||||
)
|
||||
rst += plugin_name + "\n"
|
||||
for k in _data[plugin].keys():
|
||||
rst += f"{k}: {Config[plugin].configs[k].help}" + "\n"
|
||||
_data[plugin].yaml_set_start_comment(rst[:-1], indent=2)
|
||||
with open(Path() / "configs" / "config.yaml", "w", encoding="utf8") as wf:
|
||||
round_trip_dump(_data, wf, Dumper=yaml.RoundTripDumper, allow_unicode=True)
|
||||
except Exception as e:
|
||||
logger.error(f"生成简易配置注释错误 {type(e)}:{e}")
|
||||
if temp_file.exists():
|
||||
temp_file.unlink()
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
from pathlib import Path
|
||||
|
||||
from ruamel.yaml import YAML
|
||||
from utils.manager import plugins_manager
|
||||
from utils.manager import plugins_manager, plugin_data_manager
|
||||
from utils.utils import get_matchers
|
||||
import nonebot
|
||||
from services.log import logger
|
||||
|
||||
try:
|
||||
import ujson as json
|
||||
@@ -13,70 +13,45 @@ except ModuleNotFoundError:
|
||||
_yaml = YAML(typ="safe")
|
||||
|
||||
|
||||
def init_plugins_data(data_path):
|
||||
def init_plugins_data():
|
||||
"""
|
||||
初始化插件数据信息
|
||||
"""
|
||||
plugin2data_file = data_path / "manager" / "plugin_manager.json"
|
||||
plugin2data_file.parent.mkdir(parents=True, exist_ok=True)
|
||||
_data = {}
|
||||
if plugin2data_file.exists():
|
||||
_data = json.load(open(plugin2data_file, "r", encoding="utf8"))
|
||||
_matchers = get_matchers()
|
||||
for matcher in _matchers:
|
||||
_plugin = nonebot.plugin.get_plugin(matcher.plugin_name)
|
||||
for matcher in get_matchers(True):
|
||||
_plugin = matcher.plugin
|
||||
if not _plugin:
|
||||
continue
|
||||
try:
|
||||
_module = _plugin.module
|
||||
except AttributeError:
|
||||
if matcher.plugin_name not in _data.keys():
|
||||
if matcher.plugin_name not in plugins_manager.keys():
|
||||
plugins_manager.add_plugin_data(
|
||||
matcher.plugin_name, matcher.plugin_name, error=True
|
||||
)
|
||||
else:
|
||||
plugins_manager.set_module_data(matcher.plugin_name, "error", True)
|
||||
plugin_data = plugins_manager.get(matcher.plugin_name)
|
||||
if plugin_data:
|
||||
plugins_manager.set_module_data(
|
||||
matcher.plugin_name, "version", plugin_data.get("version")
|
||||
)
|
||||
plugins_manager[matcher.plugin_name].error = True
|
||||
else:
|
||||
try:
|
||||
plugin_version = _module.__getattribute__("__plugin_version__")
|
||||
except AttributeError:
|
||||
plugin_version = None
|
||||
try:
|
||||
plugin_name = _module.__getattribute__("__zx_plugin_name__")
|
||||
except AttributeError:
|
||||
plugin_name = matcher.plugin_name
|
||||
try:
|
||||
plugin_author = _module.__getattribute__("__plugin_author__")
|
||||
except AttributeError:
|
||||
plugin_author = None
|
||||
if matcher.plugin_name in plugins_manager.keys():
|
||||
plugins_manager.set_module_data(matcher.plugin_name, "error", False)
|
||||
if matcher.plugin_name not in plugins_manager.keys():
|
||||
plugins_manager.add_plugin_data(
|
||||
matcher.plugin_name,
|
||||
plugin_name=plugin_name,
|
||||
author=plugin_author,
|
||||
version=plugin_version,
|
||||
)
|
||||
elif plugins_manager[matcher.plugin_name]["version"] is None or (
|
||||
plugin_version is not None
|
||||
and plugin_version > plugins_manager[matcher.plugin_name]["version"]
|
||||
):
|
||||
plugins_manager.set_module_data(
|
||||
matcher.plugin_name, "plugin_name", plugin_name
|
||||
)
|
||||
plugins_manager.set_module_data(matcher.plugin_name, "author", plugin_author)
|
||||
plugins_manager.set_module_data(
|
||||
matcher.plugin_name, "version", plugin_version
|
||||
)
|
||||
if matcher.plugin_name in _data.keys():
|
||||
plugins_manager.set_module_data(
|
||||
matcher.plugin_name, "error", _data[matcher.plugin_name]["error"]
|
||||
)
|
||||
plugins_manager.set_module_data(
|
||||
matcher.plugin_name, "plugin_name", _data[matcher.plugin_name]["plugin_name"]
|
||||
)
|
||||
if plugin_data := plugin_data_manager.get(matcher.plugin_name):
|
||||
try:
|
||||
plugin_version = plugin_data.plugin_status.version
|
||||
plugin_name = plugin_data.name
|
||||
plugin_author = plugin_data.plugin_status.author
|
||||
if matcher.plugin_name in plugins_manager.keys():
|
||||
plugins_manager[matcher.plugin_name].error = False
|
||||
if matcher.plugin_name not in plugins_manager.keys():
|
||||
plugins_manager.add_plugin_data(
|
||||
matcher.plugin_name,
|
||||
plugin_name=plugin_name,
|
||||
author=plugin_author,
|
||||
version=plugin_version,
|
||||
)
|
||||
elif isinstance(plugin_version, str) or plugins_manager[matcher.plugin_name].version is None or (
|
||||
plugin_version is not None
|
||||
and plugin_version > float(plugins_manager[matcher.plugin_name].version)
|
||||
):
|
||||
plugins_manager[matcher.plugin_name].plugin_name = plugin_name
|
||||
plugins_manager[matcher.plugin_name].author = plugin_author
|
||||
plugins_manager[matcher.plugin_name].version = plugin_version
|
||||
except Exception as e:
|
||||
logger.error(f"插件数据 {matcher.plugin_name} 加载发生错误 {type(e)}:{e}")
|
||||
plugins_manager.save()
|
||||
|
||||
@@ -1,157 +1,64 @@
|
||||
from pathlib import Path
|
||||
from ruamel.yaml import round_trip_load, round_trip_dump, YAML
|
||||
from utils.manager import (
|
||||
plugins2cd_manager,
|
||||
plugins2block_manager,
|
||||
plugins2count_manager,
|
||||
plugin_data_manager,
|
||||
)
|
||||
from utils.utils import get_matchers
|
||||
from ruamel import yaml
|
||||
import nonebot
|
||||
from configs.path_config import DATA_PATH
|
||||
|
||||
|
||||
_yaml = YAML(typ="safe")
|
||||
|
||||
|
||||
def init_plugins_cd_limit(data_path):
|
||||
def init_plugins_cd_limit():
|
||||
"""
|
||||
加载 cd 限制
|
||||
"""
|
||||
plugins2cd_file = data_path / "configs" / "plugins2cd.yaml"
|
||||
plugins2cd_file = DATA_PATH / "configs" / "plugins2cd.yaml"
|
||||
plugins2cd_file.parent.mkdir(exist_ok=True, parents=True)
|
||||
_data = {}
|
||||
_matchers = get_matchers()
|
||||
for matcher in _matchers:
|
||||
if not plugins2cd_manager.get_plugin_cd_data(matcher.plugin_name):
|
||||
_plugin = nonebot.plugin.get_plugin(matcher.plugin_name)
|
||||
try:
|
||||
_module = _plugin.module
|
||||
plugin_cd_limit = _module.__getattribute__("__plugin_cd_limit__")
|
||||
for matcher in get_matchers(True):
|
||||
if not plugins2cd_manager.get_plugin_cd_data(matcher.plugin_name) and (
|
||||
plugin_data := plugin_data_manager.get(matcher.plugin_name)
|
||||
):
|
||||
if plugin_data.plugin_cd:
|
||||
plugins2cd_manager.add_cd_limit(
|
||||
matcher.plugin_name, data_dict=plugin_cd_limit
|
||||
matcher.plugin_name, plugin_data.plugin_cd
|
||||
)
|
||||
except AttributeError:
|
||||
pass
|
||||
if not plugins2cd_manager.keys():
|
||||
plugins2cd_manager.add_cd_limit(
|
||||
"这是一个示例"
|
||||
)
|
||||
_tmp_data = {"PluginCdLimit": plugins2cd_manager.get_data()}
|
||||
with open(plugins2cd_file, "w", encoding="utf8") as wf:
|
||||
yaml.dump(_tmp_data, wf, Dumper=yaml.RoundTripDumper, allow_unicode=True)
|
||||
_data = round_trip_load(open(plugins2cd_file, encoding="utf8"))
|
||||
_data["PluginCdLimit"].yaml_set_start_comment(
|
||||
"""# 需要cd的功能
|
||||
# 自定义的功能需要cd也可以在此配置
|
||||
# key:模块名称
|
||||
# cd:cd 时长(秒)
|
||||
# status:此限制的开关状态
|
||||
# check_type:'private'/'group'/'all',限制私聊/群聊/全部
|
||||
# limit_type:监听对象,以user_id或group_id作为键来限制,'user':用户id,'group':群id
|
||||
# 示例:'user':用户N秒内触发1次,'group':群N秒内触发1次
|
||||
# rst:回复的话,可以添加[at],[uname],[nickname]来对应艾特,用户群名称,昵称系统昵称
|
||||
# rst 为 "" 或 None 时则不回复
|
||||
# rst示例:"[uname]你冲的太快了,[nickname]先生,请稍后再冲[at]"
|
||||
# rst回复:"老色批你冲的太快了,欧尼酱先生,请稍后再冲@老色批"
|
||||
# 用户昵称↑ 昵称系统的昵称↑ 艾特用户↑""",
|
||||
indent=2,
|
||||
)
|
||||
with open(plugins2cd_file, "w", encoding="utf8") as wf:
|
||||
round_trip_dump(_data, wf, Dumper=yaml.RoundTripDumper, allow_unicode=True)
|
||||
plugins2cd_manager.add_cd_limit("这是一个示例")
|
||||
plugins2cd_manager.save()
|
||||
plugins2cd_manager.reload_cd_limit()
|
||||
|
||||
|
||||
def init_plugins_block_limit(data_path):
|
||||
def init_plugins_block_limit():
|
||||
"""
|
||||
加载阻塞限制
|
||||
"""
|
||||
plugins2block_file = data_path / "configs" / "plugins2block.yaml"
|
||||
plugins2block_file.parent.mkdir(exist_ok=True, parents=True)
|
||||
_data = {}
|
||||
_matchers = get_matchers()
|
||||
for matcher in _matchers:
|
||||
if not plugins2block_manager.get_plugin_block_data(matcher.plugin_name):
|
||||
_plugin = nonebot.plugin.get_plugin(matcher.plugin_name)
|
||||
try:
|
||||
_module = _plugin.module
|
||||
plugin_block_limit = _module.__getattribute__("__plugin_block_limit__")
|
||||
for matcher in get_matchers(True):
|
||||
if not plugins2block_manager.get_plugin_block_data(matcher.plugin_name) and (
|
||||
plugin_data := plugin_data_manager.get(matcher.plugin_name)
|
||||
):
|
||||
if plugin_data.plugin_block:
|
||||
plugins2block_manager.add_block_limit(
|
||||
matcher.plugin_name, data_dict=plugin_block_limit
|
||||
matcher.plugin_name, plugin_data.plugin_block
|
||||
)
|
||||
except AttributeError:
|
||||
pass
|
||||
if not plugins2block_manager.keys():
|
||||
plugins2block_manager.add_block_limit(
|
||||
"这是一个示例"
|
||||
)
|
||||
_tmp_data = {"PluginBlockLimit": plugins2block_manager.get_data()}
|
||||
with open(plugins2block_file, "w", encoding="utf8") as wf:
|
||||
yaml.dump(_tmp_data, wf, Dumper=yaml.RoundTripDumper, allow_unicode=True)
|
||||
_data = round_trip_load(open(plugins2block_file, encoding="utf8"))
|
||||
_data["PluginBlockLimit"].yaml_set_start_comment(
|
||||
"""# 用户调用阻塞
|
||||
# 即 当用户调用此功能还未结束时
|
||||
# 用发送消息阻止用户重复调用此命令直到该命令结束
|
||||
# key:模块名称
|
||||
# status:此限制的开关状态
|
||||
# check_type:'private'/'group'/'all',限制私聊/群聊/全部
|
||||
# limit_type:监听对象,以user_id或group_id作为键来限制,'user':用户id,'group':群id
|
||||
# 示例:'user':阻塞用户,'group':阻塞群聊
|
||||
# rst:回复的话,可以添加[at],[uname],[nickname]来对应艾特,用户群名称,昵称系统昵称
|
||||
# rst 为 "" 或 None 时则不回复
|
||||
# rst示例:"[uname]你冲的太快了,[nickname]先生,请稍后再冲[at]"
|
||||
# rst回复:"老色批你冲的太快了,欧尼酱先生,请稍后再冲@老色批"
|
||||
# 用户昵称↑ 昵称系统的昵称↑ 艾特用户↑""",
|
||||
indent=2,
|
||||
)
|
||||
with open(plugins2block_file, "w", encoding="utf8") as wf:
|
||||
round_trip_dump(_data, wf, Dumper=yaml.RoundTripDumper, allow_unicode=True)
|
||||
plugins2block_manager.add_block_limit("这是一个示例")
|
||||
plugins2block_manager.save()
|
||||
plugins2block_manager.reload_block_limit()
|
||||
|
||||
|
||||
def init_plugins_count_limit(data_path):
|
||||
def init_plugins_count_limit():
|
||||
"""
|
||||
加载次数限制
|
||||
"""
|
||||
plugins2count_file = data_path / "configs" / "plugins2count.yaml"
|
||||
plugins2count_file.parent.mkdir(exist_ok=True, parents=True)
|
||||
_data = {}
|
||||
_matchers = get_matchers()
|
||||
for matcher in _matchers:
|
||||
if not plugins2count_manager.get_plugin_count_data(matcher.plugin_name):
|
||||
_plugin = nonebot.plugin.get_plugin(matcher.plugin_name)
|
||||
try:
|
||||
_module = _plugin.module
|
||||
plugin_count_limit = _module.__getattribute__("__plugin_count_limit__")
|
||||
for matcher in get_matchers(True):
|
||||
if not plugins2count_manager.get_plugin_count_data(matcher.plugin_name) and (
|
||||
plugin_data := plugin_data_manager.get(matcher.plugin_name)
|
||||
):
|
||||
if plugin_data.plugin_count:
|
||||
plugins2count_manager.add_count_limit(
|
||||
matcher.plugin_name, data_dict=plugin_count_limit
|
||||
matcher.plugin_name, plugin_data.plugin_count
|
||||
)
|
||||
except AttributeError:
|
||||
pass
|
||||
if not plugins2count_manager.keys():
|
||||
plugins2count_manager.add_count_limit(
|
||||
"这是一个示例"
|
||||
)
|
||||
_tmp_data = {"PluginCountLimit": plugins2count_manager.get_data()}
|
||||
with open(plugins2count_file, "w", encoding="utf8") as wf:
|
||||
yaml.dump(_tmp_data, wf, Dumper=yaml.RoundTripDumper, allow_unicode=True)
|
||||
_data = round_trip_load(open(plugins2count_file, encoding="utf8"))
|
||||
_data["PluginCountLimit"].yaml_set_start_comment(
|
||||
"""# 命令每日次数限制
|
||||
# 即 用户/群聊 每日可调用命令的次数 [数据内存存储,重启将会重置]
|
||||
# 每日调用直到 00:00 刷新
|
||||
# key:模块名称
|
||||
# max_count: 每日调用上限
|
||||
# status:此限制的开关状态
|
||||
# limit_type:监听对象,以user_id或group_id作为键来限制,'user':用户id,'group':群id
|
||||
# 示例:'user':用户上限,'group':群聊上限
|
||||
# rst:回复的话,可以添加[at],[uname],[nickname]来对应艾特,用户群名称,昵称系统昵称
|
||||
# rst 为 "" 或 None 时则不回复
|
||||
# rst示例:"[uname]你冲的太快了,[nickname]先生,请稍后再冲[at]"
|
||||
# rst回复:"老色批你冲的太快了,欧尼酱先生,请稍后再冲@老色批"
|
||||
# 用户昵称↑ 昵称系统的昵称↑ 艾特用户↑""",
|
||||
indent=2,
|
||||
)
|
||||
with open(plugins2count_file, "w", encoding="utf8") as wf:
|
||||
round_trip_dump(_data, wf, Dumper=yaml.RoundTripDumper, allow_unicode=True)
|
||||
plugins2count_manager.add_count_limit("这是一个示例")
|
||||
plugins2count_manager.save()
|
||||
plugins2count_manager.reload_count_limit()
|
||||
|
||||
@@ -1,43 +1,25 @@
|
||||
from utils.manager import resources_manager
|
||||
from utils.manager import resources_manager, plugin_data_manager
|
||||
from utils.utils import get_matchers
|
||||
from services.log import logger
|
||||
from pathlib import Path
|
||||
import nonebot
|
||||
|
||||
|
||||
def init_plugins_resources():
|
||||
"""
|
||||
资源文件路径的移动
|
||||
"""
|
||||
_tmp = []
|
||||
for matcher in get_matchers():
|
||||
if matcher.plugin_name not in _tmp:
|
||||
_tmp.append(matcher.plugin_name)
|
||||
_plugin = nonebot.plugin.get_plugin(matcher.plugin_name)
|
||||
for matcher in get_matchers(True):
|
||||
if plugin_data := plugin_data_manager.get(matcher.plugin_name):
|
||||
try:
|
||||
_module = _plugin.module
|
||||
_module = matcher.plugin.module
|
||||
except AttributeError:
|
||||
logger.warning(f"插件 {matcher.plugin_name} 加载失败...,资源控制未加载...")
|
||||
else:
|
||||
try:
|
||||
resources = _module.__getattribute__("__plugin_resources__")
|
||||
except AttributeError:
|
||||
pass
|
||||
else:
|
||||
if resources := plugin_data.plugin_resources:
|
||||
path = Path(_module.__getattribute__("__file__")).parent
|
||||
for resource in resources.keys():
|
||||
resources_manager.add_resource(matcher.plugin_name, path / resource, resources[resource])
|
||||
resources_manager.add_resource(
|
||||
matcher.plugin_name, path / resource, resources[resource]
|
||||
)
|
||||
resources_manager.save()
|
||||
resources_manager.start_move()
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,128 +1,44 @@
|
||||
from pathlib import Path
|
||||
from ruamel.yaml import round_trip_load, round_trip_dump, YAML
|
||||
from utils.manager import plugins2settings_manager, admin_manager
|
||||
from utils.manager import plugins2settings_manager, admin_manager, plugin_data_manager
|
||||
from services.log import logger
|
||||
from utils.manager.models import PluginType
|
||||
from utils.utils import get_matchers
|
||||
from ruamel import yaml
|
||||
import nonebot
|
||||
|
||||
|
||||
_yaml = YAML(typ="safe")
|
||||
|
||||
|
||||
def init_plugins_settings(data_path: str):
|
||||
def init_plugins_settings():
|
||||
"""
|
||||
初始化插件设置,从插件中获取 __zx_plugin_name__,__plugin_cmd__,__plugin_settings__
|
||||
"""
|
||||
plugins2settings_file = data_path / "configs" / "plugins2settings.yaml"
|
||||
plugins2settings_file.parent.mkdir(exist_ok=True, parents=True)
|
||||
_matchers = get_matchers()
|
||||
_tmp_module = {}
|
||||
_tmp = []
|
||||
for x in plugins2settings_manager.keys():
|
||||
# for x in plugins2settings_manager.keys():
|
||||
# try:
|
||||
# _plugin = nonebot.plugin.get_plugin(x)
|
||||
# _module = _plugin.module
|
||||
# _module.__getattribute__("__zx_plugin_name__")
|
||||
# except (KeyError, AttributeError) as e:
|
||||
# logger.warning(f"配置文件 模块:{x} 获取 plugin_name 失败...{e}")
|
||||
for matcher in get_matchers(True):
|
||||
try:
|
||||
_plugin = nonebot.plugin.get_plugin(x)
|
||||
_module = _plugin.module
|
||||
plugin_name = _module.__getattribute__("__zx_plugin_name__")
|
||||
_tmp_module[x] = plugin_name
|
||||
except (KeyError, AttributeError) as e:
|
||||
logger.warning(f"配置文件 模块:{x} 获取 plugin_name 失败...{e}")
|
||||
_tmp_module[x] = ""
|
||||
for matcher in _matchers:
|
||||
if matcher.plugin_name not in plugins2settings_manager.keys():
|
||||
_plugin = nonebot.plugin.get_plugin(matcher.plugin_name)
|
||||
try:
|
||||
_module = _plugin.module
|
||||
except AttributeError:
|
||||
logger.warning(f"插件 {matcher.plugin_name} 加载失败...,插件控制未加载.")
|
||||
else:
|
||||
if matcher.plugin_name not in plugins2settings_manager.keys():
|
||||
_plugin = matcher.plugin
|
||||
try:
|
||||
plugin_name = _module.__getattribute__("__zx_plugin_name__")
|
||||
if "[admin]" in plugin_name.lower():
|
||||
try:
|
||||
admin_settings = _module.__getattribute__(
|
||||
"__plugin_settings__"
|
||||
)
|
||||
level = admin_settings["admin_level"]
|
||||
cmd = admin_settings.get("cmd")
|
||||
except (AttributeError, KeyError):
|
||||
level = 5
|
||||
cmd = None
|
||||
if level is None:
|
||||
level = 5
|
||||
admin_manager.add_admin_plugin_settings(
|
||||
matcher.plugin_name, cmd, level
|
||||
)
|
||||
if (
|
||||
"[hidden]" in plugin_name.lower()
|
||||
or "[admin]" in plugin_name.lower()
|
||||
or "[superuser]" in plugin_name.lower()
|
||||
or matcher.plugin_name in plugins2settings_manager.keys()
|
||||
):
|
||||
continue
|
||||
_module = _plugin.module
|
||||
except AttributeError:
|
||||
if matcher.plugin_name not in _tmp:
|
||||
logger.warning(
|
||||
f"获取插件 {matcher.plugin_name} __zx_plugin_name__ 失败...,插件控制未加载."
|
||||
)
|
||||
logger.warning(f"插件 {matcher.plugin_name} 加载失败...,插件控制未加载.")
|
||||
else:
|
||||
try:
|
||||
_tmp_module[matcher.plugin_name] = plugin_name
|
||||
plugin_settings = _module.__getattribute__(
|
||||
"__plugin_settings__"
|
||||
)
|
||||
if plugin_settings.get('cost_gold') is None:
|
||||
plugin_settings['cost_gold'] = 0
|
||||
if (
|
||||
plugin_settings.get("cmd") is not None
|
||||
and plugin_name not in plugin_settings["cmd"]
|
||||
):
|
||||
plugin_settings["cmd"].append(plugin_name)
|
||||
if plugins2settings_manager.get(
|
||||
matcher.plugin_name
|
||||
) and plugins2settings_manager[matcher.plugin_name].get(
|
||||
"plugin_type"
|
||||
):
|
||||
plugin_type = tuple(
|
||||
plugins2settings_manager.get_plugin_data(
|
||||
matcher.plugin_name
|
||||
)["plugin_type"]
|
||||
)
|
||||
else:
|
||||
try:
|
||||
plugin_type = _module.__getattribute__(
|
||||
"__plugin_type__"
|
||||
if plugin_data := plugin_data_manager.get(matcher.plugin_name):
|
||||
if plugin_settings := plugin_data.plugin_setting:
|
||||
if (name := _module.__getattribute__("__zx_plugin_name__")) not in plugin_settings.cmd:
|
||||
plugin_settings.cmd.append(name)
|
||||
# 管理员命令
|
||||
if plugin_data.plugin_type == PluginType.ADMIN:
|
||||
admin_manager.add_admin_plugin_settings(
|
||||
matcher.plugin_name, plugin_settings.cmd, plugin_settings.level
|
||||
)
|
||||
except AttributeError:
|
||||
plugin_type = ("normal",)
|
||||
if plugin_settings and matcher.plugin_name:
|
||||
plugins2settings_manager.add_plugin_settings(
|
||||
matcher.plugin_name,
|
||||
plugin_type=plugin_type,
|
||||
**plugin_settings,
|
||||
)
|
||||
except AttributeError:
|
||||
pass
|
||||
_tmp.append(matcher.plugin_name)
|
||||
_tmp_data = {"PluginSettings": plugins2settings_manager.get_data()}
|
||||
with open(plugins2settings_file, "w", encoding="utf8") as wf:
|
||||
yaml.dump(_tmp_data, wf, Dumper=yaml.RoundTripDumper, allow_unicode=True)
|
||||
_data = round_trip_load(open(plugins2settings_file, encoding="utf8"))
|
||||
_data["PluginSettings"].yaml_set_start_comment(
|
||||
"""# 模块与对应命令和对应群权限
|
||||
# 用于生成帮助图片 和 开关功能
|
||||
# key:模块名称
|
||||
# level:需要的群等级
|
||||
# default_status:加入群时功能的默认开关状态
|
||||
# limit_superuser: 功能状态是否限制超级用户
|
||||
# cmd: 关闭[cmd] 都会触发命令 关闭对应功能,cmd列表第一个词为统计的功能名称
|
||||
# plugin_type: 帮助类别 示例:('原神相关',) 或 ('原神相关', 1),1代表帮助命令列向排列,否则为横向排列""",
|
||||
indent=2,
|
||||
)
|
||||
for plugin in _data["PluginSettings"].keys():
|
||||
_data["PluginSettings"][plugin].yaml_set_start_comment(
|
||||
f"{plugin}:{_tmp_module[plugin]}", indent=2
|
||||
)
|
||||
with open(plugins2settings_file, "w", encoding="utf8") as wf:
|
||||
round_trip_dump(_data, wf, Dumper=yaml.RoundTripDumper, allow_unicode=True)
|
||||
else:
|
||||
plugins2settings_manager.add_plugin_settings(
|
||||
matcher.plugin_name, plugin_settings
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f'{matcher.plugin_name} 初始化 plugin_settings 发生错误 {type(e)}:{e}')
|
||||
plugins2settings_manager.save()
|
||||
logger.info(f"已成功加载 {len(plugins2settings_manager.get_data())} 个非限制插件.")
|
||||
|
||||
@@ -1,127 +1,145 @@
|
||||
from nonebot import on_request, on_message
|
||||
import asyncio
|
||||
import re
|
||||
import time
|
||||
from datetime import datetime
|
||||
|
||||
from nonebot import on_message, on_request
|
||||
from nonebot.adapters.onebot.v11 import (
|
||||
ActionFailed,
|
||||
Bot,
|
||||
FriendRequestEvent,
|
||||
GroupRequestEvent,
|
||||
MessageEvent,
|
||||
)
|
||||
from models.friend_user import FriendUser
|
||||
from datetime import datetime
|
||||
|
||||
from configs.config import NICKNAME, Config
|
||||
from nonebot.adapters.onebot.v11.exception import ActionFailed
|
||||
from utils.manager import requests_manager
|
||||
from models.friend_user import FriendUser
|
||||
from models.group_info import GroupInfo
|
||||
from services.log import logger
|
||||
from utils.manager import requests_manager
|
||||
from utils.utils import scheduler
|
||||
import asyncio
|
||||
import time
|
||||
import re
|
||||
|
||||
from .utils import time_manager
|
||||
|
||||
__zx_plugin_name__ = "好友群聊处理请求 [Hidden]"
|
||||
__plugin_version__ = 0.1
|
||||
__plugin_author__ = "HibiKier"
|
||||
__plugin_configs__ = {
|
||||
"AUTO_ADD_FRIEND": {"value": False, "help": "是否自动同意好友添加", "default_value": False}
|
||||
"AUTO_ADD_FRIEND": {
|
||||
"value": False,
|
||||
"help": "是否自动同意好友添加",
|
||||
"default_value": False,
|
||||
"type": bool,
|
||||
}
|
||||
}
|
||||
|
||||
friend_req = on_request(priority=5, block=True)
|
||||
group_req = on_request(priority=5, block=True)
|
||||
x = on_message(priority=9, block=False)
|
||||
|
||||
exists_data = {"private": {}, "group": {}}
|
||||
x = on_message(priority=999, block=False, rule=lambda: False)
|
||||
|
||||
|
||||
@friend_req.handle()
|
||||
async def _(bot: Bot, event: FriendRequestEvent):
|
||||
global exists_data
|
||||
if exists_data["private"].get(event.user_id):
|
||||
if time.time() - exists_data["private"][event.user_id] < 60 * 5:
|
||||
return
|
||||
exists_data["private"][event.user_id] = time.time()
|
||||
user = await bot.get_stranger_info(user_id=event.user_id)
|
||||
nickname = user["nickname"]
|
||||
sex = user["sex"]
|
||||
age = str(user["age"])
|
||||
comment = event.comment
|
||||
await bot.send_private_msg(
|
||||
user_id=int(list(bot.config.superusers)[0]),
|
||||
message=f"*****一份好友申请*****\n"
|
||||
f"昵称:{nickname}({event.user_id})\n"
|
||||
f"自动同意:{'√' if Config.get_config('invite_manager', 'AUTO_ADD_FRIEND') else '×'}\n"
|
||||
f"日期:{str(datetime.now()).split('.')[0]}\n"
|
||||
f"备注:{event.comment}",
|
||||
)
|
||||
if Config.get_config("invite_manager", "AUTO_ADD_FRIEND"):
|
||||
await bot.set_friend_add_request(flag=event.flag, approve=True)
|
||||
await FriendUser.add_friend_info(user["user_id"], user["nickname"])
|
||||
else:
|
||||
requests_manager.add_request(
|
||||
event.user_id,
|
||||
"private",
|
||||
event.flag,
|
||||
nickname=nickname,
|
||||
sex=sex,
|
||||
age=age,
|
||||
comment=comment,
|
||||
if time_manager.add_user_request(event.user_id):
|
||||
logger.debug(f"收录 用户[{event.user_id}] 好友请求", "好友请求")
|
||||
user = await bot.get_stranger_info(user_id=event.user_id)
|
||||
nickname = user["nickname"]
|
||||
sex = user["sex"]
|
||||
age = str(user["age"])
|
||||
comment = event.comment
|
||||
await bot.send_private_msg(
|
||||
user_id=int(list(bot.config.superusers)[0]),
|
||||
message=f"*****一份好友申请*****\n"
|
||||
f"昵称:{nickname}({event.user_id})\n"
|
||||
f"自动同意:{'√' if Config.get_config('invite_manager', 'AUTO_ADD_FRIEND') else '×'}\n"
|
||||
f"日期:{str(datetime.now()).split('.')[0]}\n"
|
||||
f"备注:{event.comment}",
|
||||
)
|
||||
if Config.get_config("invite_manager", "AUTO_ADD_FRIEND"):
|
||||
logger.debug(f"已开启好友请求自动同意,成功通过该请求", "好友请求", target=event.user_id)
|
||||
await bot.set_friend_add_request(flag=event.flag, approve=True)
|
||||
await FriendUser.create(user_id=user["user_id"], user_name=user["nickname"])
|
||||
else:
|
||||
requests_manager.add_request(
|
||||
event.user_id,
|
||||
"private",
|
||||
event.flag,
|
||||
nickname=nickname,
|
||||
sex=sex,
|
||||
age=age,
|
||||
comment=comment,
|
||||
)
|
||||
else:
|
||||
logger.debug(f"好友请求五分钟内重复, 已忽略", "好友请求", target=event.user_id)
|
||||
|
||||
|
||||
@group_req.handle()
|
||||
async def _(bot: Bot, event: GroupRequestEvent):
|
||||
global exists_data
|
||||
# 邀请
|
||||
if event.sub_type == "invite":
|
||||
if str(event.user_id) in bot.config.superusers:
|
||||
try:
|
||||
if await GroupInfo.get_group_info(event.group_id):
|
||||
await GroupInfo.set_group_flag(event.group_id, 1)
|
||||
else:
|
||||
group_info = await bot.get_group_info(group_id=event.group_id)
|
||||
await GroupInfo.add_group_info(
|
||||
group_info["group_id"],
|
||||
group_info["group_name"],
|
||||
group_info["max_member_count"],
|
||||
group_info["member_count"],
|
||||
1,
|
||||
)
|
||||
logger.debug(
|
||||
f"超级用户自动同意加入群聊", "群聊请求", event.user_id, target=event.group_id
|
||||
)
|
||||
await bot.set_group_add_request(
|
||||
flag=event.flag, sub_type="invite", approve=True
|
||||
)
|
||||
except ActionFailed:
|
||||
pass
|
||||
group_info = await bot.get_group_info(group_id=event.group_id)
|
||||
await GroupInfo.update_or_create(
|
||||
group_id=group_info["group_id"],
|
||||
defaults={
|
||||
"group_name": group_info["group_name"],
|
||||
"max_member_count": group_info["max_member_count"],
|
||||
"member_count": group_info["member_count"],
|
||||
"group_flag": 1,
|
||||
},
|
||||
)
|
||||
except ActionFailed as e:
|
||||
logger.error(
|
||||
"超级用户自动同意加入群聊发生错误",
|
||||
"群聊请求",
|
||||
event.user_id,
|
||||
target=event.group_id,
|
||||
e=e,
|
||||
)
|
||||
else:
|
||||
user = await bot.get_stranger_info(user_id=event.user_id)
|
||||
sex = user["sex"]
|
||||
age = str(user["age"])
|
||||
if exists_data["group"].get(f"{event.user_id}:{event.group_id}"):
|
||||
if (
|
||||
time.time()
|
||||
- exists_data["group"][f"{event.user_id}:{event.group_id}"]
|
||||
< 60 * 5
|
||||
):
|
||||
return
|
||||
exists_data["group"][f"{event.user_id}:{event.group_id}"] = time.time()
|
||||
nickname = await FriendUser.get_user_name(event.user_id)
|
||||
await bot.send_private_msg(
|
||||
user_id=int(list(bot.config.superusers)[0]),
|
||||
message=f"*****一份入群申请*****\n"
|
||||
f"申请人:{nickname}({event.user_id})\n"
|
||||
f"群聊:{event.group_id}\n"
|
||||
f"邀请日期:{str(datetime.now()).split('.')[0]}",
|
||||
)
|
||||
await bot.send_private_msg(
|
||||
user_id=event.user_id,
|
||||
message=f"想要邀请我偷偷入群嘛~已经提醒{NICKNAME}的管理员大人了\n"
|
||||
"请确保已经群主或群管理沟通过!\n"
|
||||
"等待管理员处理吧!",
|
||||
)
|
||||
requests_manager.add_request(
|
||||
event.user_id,
|
||||
"group",
|
||||
event.flag,
|
||||
invite_group=event.group_id,
|
||||
nickname=nickname,
|
||||
sex=sex,
|
||||
age=age,
|
||||
)
|
||||
if time_manager.add_group_request(event.user_id, event.group_id):
|
||||
logger.debug(
|
||||
f"收录 用户[{event.user_id}] 群聊[{event.group_id}] 群聊请求", "群聊请求"
|
||||
)
|
||||
user = await bot.get_stranger_info(user_id=event.user_id)
|
||||
sex = user["sex"]
|
||||
age = str(user["age"])
|
||||
nickname = await FriendUser.get_user_name(event.user_id)
|
||||
await bot.send_private_msg(
|
||||
user_id=int(list(bot.config.superusers)[0]),
|
||||
message=f"*****一份入群申请*****\n"
|
||||
f"申请人:{nickname}({event.user_id})\n"
|
||||
f"群聊:{event.group_id}\n"
|
||||
f"邀请日期:{datetime.now().replace(microsecond=0)}",
|
||||
)
|
||||
await bot.send_private_msg(
|
||||
user_id=event.user_id,
|
||||
message=f"想要邀请我偷偷入群嘛~已经提醒{NICKNAME}的管理员大人了\n"
|
||||
"请确保已经群主或群管理沟通过!\n"
|
||||
"等待管理员处理吧!",
|
||||
)
|
||||
requests_manager.add_request(
|
||||
event.user_id,
|
||||
"group",
|
||||
event.flag,
|
||||
invite_group=event.group_id,
|
||||
nickname=nickname,
|
||||
sex=sex,
|
||||
age=age,
|
||||
)
|
||||
else:
|
||||
logger.debug(
|
||||
f"群聊请求五分钟内重复, 已忽略",
|
||||
"群聊请求",
|
||||
target=f"{event.user_id}:{event.group_id}",
|
||||
)
|
||||
|
||||
|
||||
@x.handle()
|
||||
@@ -145,5 +163,4 @@ async def _(event: MessageEvent):
|
||||
minutes=5,
|
||||
)
|
||||
async def _():
|
||||
global exists_data
|
||||
exists_data = {"private": {}, "group": {}}
|
||||
time_manager.clear()
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
import time
|
||||
from dataclasses import dataclass
|
||||
from typing import Dict
|
||||
|
||||
|
||||
@dataclass
|
||||
class PrivateRequest:
|
||||
|
||||
"""
|
||||
好友请求
|
||||
"""
|
||||
|
||||
user_id: int
|
||||
time: float = time.time()
|
||||
|
||||
|
||||
@dataclass
|
||||
class GroupRequest:
|
||||
|
||||
"""
|
||||
群聊请求
|
||||
"""
|
||||
|
||||
user_id: int
|
||||
group_id: int
|
||||
time: float = time.time()
|
||||
|
||||
|
||||
class RequestTimeManage:
|
||||
|
||||
"""
|
||||
过滤五分钟以内的重复请求
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
|
||||
self._group: Dict[str, GroupRequest] = {}
|
||||
self._user: Dict[int, PrivateRequest] = {}
|
||||
|
||||
def add_user_request(self, user_id: int) -> bool:
|
||||
"""
|
||||
添加请求时间
|
||||
|
||||
Args:
|
||||
user_id (int): 用户id
|
||||
|
||||
Returns:
|
||||
bool: 是否满足时间
|
||||
"""
|
||||
if user := self._user.get(user_id):
|
||||
if time.time() - user.time < 60 * 5:
|
||||
return False
|
||||
self._user[user_id] = PrivateRequest(user_id)
|
||||
return True
|
||||
|
||||
def add_group_request(self, user_id: int, group_id: int) -> bool:
|
||||
"""
|
||||
添加请求时间
|
||||
|
||||
Args:
|
||||
user_id (int): 用户id
|
||||
group_id (int): 邀请群聊
|
||||
|
||||
Returns:
|
||||
bool: 是否满足时间
|
||||
"""
|
||||
key = f"{user_id}:{group_id}"
|
||||
if group := self._group.get(key):
|
||||
if time.time() - group.time < 60 * 5:
|
||||
return False
|
||||
self._group[key] = GroupRequest(user_id=user_id, group_id=group_id)
|
||||
return True
|
||||
|
||||
def clear(self):
|
||||
"""
|
||||
清理过期五分钟请求
|
||||
"""
|
||||
now = time.time()
|
||||
for user_id in self._user:
|
||||
if now - self._user[user_id].time < 60 * 5:
|
||||
del self._user[user_id]
|
||||
for key in self._group:
|
||||
if now - self._group[key].time < 60 * 5:
|
||||
del self._group[key]
|
||||
|
||||
|
||||
time_manager = RequestTimeManage()
|
||||
+136
-129
@@ -1,114 +1,163 @@
|
||||
from nonebot.adapters.onebot.v11 import Bot, GroupMessageEvent, PrivateMessageEvent, Message
|
||||
from nonebot import on_command
|
||||
from nonebot.typing import T_State
|
||||
from nonebot.rule import to_me
|
||||
from models.group_member_info import GroupInfoUser
|
||||
from models.friend_user import FriendUser
|
||||
from models.ban_user import BanUser
|
||||
from services.log import logger
|
||||
from configs.config import NICKNAME, Config
|
||||
from nonebot.params import CommandArg
|
||||
import random
|
||||
from typing import Any, List, Tuple
|
||||
|
||||
from nonebot import on_command, on_regex
|
||||
from nonebot.adapters.onebot.v11 import Bot, GroupMessageEvent, Message, MessageEvent
|
||||
from nonebot.internal.matcher import Matcher
|
||||
from nonebot.internal.params import Depends
|
||||
from nonebot.params import CommandArg, RegexGroup
|
||||
from nonebot.rule import to_me
|
||||
|
||||
from configs.config import NICKNAME
|
||||
from models.ban_user import BanUser
|
||||
from models.friend_user import FriendUser
|
||||
from models.group_member_info import GroupInfoUser
|
||||
from services.log import logger
|
||||
from utils.depends import GetConfig
|
||||
|
||||
__zx_plugin_name__ = "昵称系统"
|
||||
__plugin_usage__ = f"""
|
||||
usage:
|
||||
个人昵称系统,群聊 与 私聊 昵称相互独立
|
||||
个人昵称,将替换真寻称呼你的名称,群聊 与 私聊 昵称相互独立,全局昵称设置将更改您目前所有群聊中及私聊的昵称
|
||||
指令:
|
||||
以后叫我 [昵称]
|
||||
以后叫我 [昵称]: 设置当前群聊/私聊的昵称
|
||||
全局昵称设置 [昵称]: 设置当前所有群聊和私聊的昵称
|
||||
{NICKNAME}我是谁
|
||||
""".strip()
|
||||
__plugin_des__ = "区区昵称,才不想叫呢!"
|
||||
__plugin_cmd__ = ["以后叫我 [昵称]", f"{NICKNAME}我是谁"]
|
||||
__plugin_cmd__ = ["以后叫我 [昵称]", f"{NICKNAME}我是谁", "全局昵称设置 [昵称]"]
|
||||
__plugin_version__ = 0.1
|
||||
__plugin_author__ = "HibiKier"
|
||||
__plugin_settings__ = {
|
||||
"level": 5,
|
||||
"default_status": True,
|
||||
"limit_superuser": False,
|
||||
"cmd": ["昵称"],
|
||||
}
|
||||
__plugin_configs__ = {
|
||||
"BLACK_WORD": {
|
||||
"value": ["爸", "爹", "爷"],
|
||||
"help": "昵称所屏蔽的关键词,会被替换为 *",
|
||||
"default_value": None
|
||||
"value": ["爸", "爹", "爷", "父"],
|
||||
"help": "昵称所屏蔽的关键词,已设置的昵称会被替换为 *,未设置的昵称会在设置时提示",
|
||||
"default_value": None,
|
||||
"type": List[str],
|
||||
}
|
||||
}
|
||||
|
||||
nickname = on_command(
|
||||
"nickname",
|
||||
aliases={"以后叫我", "以后请叫我", "称呼我", "以后请称呼我", "以后称呼我", "叫我", "请叫我"},
|
||||
nickname = on_regex(
|
||||
"(?:以后)?(?:叫我|请叫我|称呼我)(.*)",
|
||||
rule=to_me(),
|
||||
priority=5,
|
||||
block=True,
|
||||
)
|
||||
|
||||
my_nickname = on_command(
|
||||
"my_name", aliases={"我叫什么", "我是谁", "我的名字"}, rule=to_me(), priority=5, block=True
|
||||
"我叫什么", aliases={"我是谁", "我的名字"}, rule=to_me(), priority=5, block=True
|
||||
)
|
||||
|
||||
global_nickname = on_regex("设置全局昵称(.*)", rule=to_me(), priority=5, block=True)
|
||||
|
||||
|
||||
cancel_nickname = on_command("取消昵称", rule=to_me(), priority=5, block=True)
|
||||
|
||||
|
||||
@nickname.handle()
|
||||
async def _(bot: Bot, event: GroupMessageEvent, arg: Message = CommandArg()):
|
||||
msg = arg.extract_plain_text().strip()
|
||||
if not msg:
|
||||
await nickname.finish("叫你空白?叫你虚空?叫你无名??", at_sender=True)
|
||||
if len(msg) > 10:
|
||||
await nickname.finish("昵称可不能超过10个字!", at_sender=True)
|
||||
if msg in bot.config.superusers:
|
||||
await nickname.finish("笨蛋!休想占用我的名字!#", at_sender=True)
|
||||
_tmp = ""
|
||||
black_word = Config.get_config("nickname", "BLACK_WORD")
|
||||
for x in msg:
|
||||
_tmp += "*" if x in black_word else x
|
||||
msg = _tmp
|
||||
def CheckNickname():
|
||||
"""
|
||||
说明:
|
||||
检查名称是否合法
|
||||
"""
|
||||
|
||||
async def dependency(
|
||||
bot: Bot,
|
||||
matcher: Matcher,
|
||||
event: MessageEvent,
|
||||
reg_group: Tuple[Any, ...] = RegexGroup(),
|
||||
black_word: Any = GetConfig(config="BLACK_WORD"),
|
||||
):
|
||||
(msg,) = reg_group
|
||||
logger.debug(f"昵称检查: {msg}", "昵称设置", event.user_id)
|
||||
if not msg:
|
||||
await matcher.finish("叫你空白?叫你虚空?叫你无名??", at_sender=True)
|
||||
if str(event.user_id) in bot.config.superusers:
|
||||
logger.debug(f"超级用户设置昵称, 跳过合法检测: {msg}", "昵称设置", event.user_id)
|
||||
return
|
||||
if len(msg) > 20:
|
||||
await nickname.finish("昵称可不能超过20个字!", at_sender=True)
|
||||
if msg in bot.config.nickname:
|
||||
await nickname.finish("笨蛋!休想占用我的名字!#", at_sender=True)
|
||||
if black_word:
|
||||
for x in msg:
|
||||
if x in black_word:
|
||||
logger.debug("昵称设置禁止字符: [{x}]", "昵称设置", event.user_id)
|
||||
await nickname.finish(f"字符 [{x}] 为禁止字符!", at_sender=True)
|
||||
for word in black_word:
|
||||
if word in msg:
|
||||
logger.debug("昵称设置禁止字符: [{word}]", "昵称设置", event.user_id)
|
||||
await nickname.finish(f"字符 [{word}] 为禁止字符!", at_sender=True)
|
||||
|
||||
return Depends(dependency)
|
||||
|
||||
|
||||
@global_nickname.handle(parameterless=[CheckNickname()])
|
||||
async def _(
|
||||
event: MessageEvent,
|
||||
reg_group: Tuple[Any, ...] = RegexGroup(),
|
||||
):
|
||||
(msg,) = reg_group
|
||||
await FriendUser.set_user_nickname(event.user_id, msg)
|
||||
await GroupInfoUser.filter(user_qq=event.user_id).update(nickname=msg)
|
||||
logger.info(f"设置全局昵称成功: {msg}", "设置全局昵称", event.user_id)
|
||||
await global_nickname.send(f"设置全局昵称成功!亲爱的{msg}")
|
||||
|
||||
|
||||
@nickname.handle(parameterless=[CheckNickname()])
|
||||
async def _(
|
||||
event: MessageEvent,
|
||||
reg_group: Tuple[Any, ...] = RegexGroup(),
|
||||
):
|
||||
(msg,) = reg_group
|
||||
if isinstance(event, GroupMessageEvent):
|
||||
if await GroupInfoUser.set_group_member_nickname(
|
||||
event.user_id, event.group_id, msg
|
||||
):
|
||||
if len(msg) < 5:
|
||||
if random.random() < 0.3:
|
||||
msg = "~".join(msg)
|
||||
await nickname.send(
|
||||
random.choice(
|
||||
[
|
||||
f"好啦好啦,我知道啦,{msg},以后就这么叫你吧",
|
||||
f"嗯嗯,{NICKNAME}记住你的昵称了哦,{msg}",
|
||||
f"好突然,突然要叫你昵称什么的...{msg}..",
|
||||
f"{NICKNAME}会好好记住的{msg}的,放心吧",
|
||||
f"好..好.,那窝以后就叫你{msg}了.",
|
||||
]
|
||||
)
|
||||
await GroupInfoUser.set_user_nickname(event.user_id, event.group_id, msg)
|
||||
if len(msg) < 5:
|
||||
if random.random() < 0.3:
|
||||
msg = "~".join(msg)
|
||||
await nickname.send(
|
||||
random.choice(
|
||||
[
|
||||
f"好啦好啦,我知道啦,{msg},以后就这么叫你吧",
|
||||
f"嗯嗯,{NICKNAME}记住你的昵称了哦,{msg}",
|
||||
f"好突然,突然要叫你昵称什么的...{msg}..",
|
||||
f"{NICKNAME}会好好记住{msg}的,放心吧",
|
||||
f"好..好.,那窝以后就叫你{msg}了.",
|
||||
]
|
||||
)
|
||||
logger.info(f"USER {event.user_id} GROUP {event.group_id} 设置群昵称 {msg}")
|
||||
else:
|
||||
await nickname.send("设置昵称失败,请更新群组成员信息!", at_sender=True)
|
||||
logger.warning(f"USER {event.user_id} GROUP {event.group_id} 设置群昵称 {msg} 失败")
|
||||
)
|
||||
logger.info(f"设置群昵称成功: {msg}", "昵称设置", event.user_id, event.group_id)
|
||||
else:
|
||||
if await FriendUser.set_friend_nickname(event.user_id, msg):
|
||||
await nickname.send(
|
||||
random.choice(
|
||||
[
|
||||
f"好啦好啦,我知道啦,{msg},以后就这么叫你吧",
|
||||
f"嗯嗯,{NICKNAME}记住你的昵称了哦,{msg}",
|
||||
f"好突然,突然要叫你昵称什么的...{msg}..",
|
||||
f"{NICKNAME}会好好记住的{msg}的,放心吧",
|
||||
f"好..好.,那窝以后就叫你{msg}了.",
|
||||
]
|
||||
)
|
||||
await FriendUser.set_user_nickname(event.user_id, msg)
|
||||
await nickname.send(
|
||||
random.choice(
|
||||
[
|
||||
f"好啦好啦,我知道啦,{msg},以后就这么叫你吧",
|
||||
f"嗯嗯,{NICKNAME}记住你的昵称了哦,{msg}",
|
||||
f"好突然,突然要叫你昵称什么的...{msg}..",
|
||||
f"{NICKNAME}会好好记住{msg}的,放心吧",
|
||||
f"好..好.,那窝以后就叫你{msg}了.",
|
||||
]
|
||||
)
|
||||
logger.info(f"USER {event.user_id} 设置昵称 {msg}")
|
||||
else:
|
||||
await nickname.send("设置昵称失败了,明天再来试一试!或联系管理员更新好友!", at_sender=True)
|
||||
logger.warning(f"USER {event.user_id} 设置昵称 {msg} 失败")
|
||||
)
|
||||
logger.info(f"设置私聊昵称成功: {msg}", "昵称设置", event.user_id)
|
||||
|
||||
|
||||
@my_nickname.handle()
|
||||
async def _(event: GroupMessageEvent):
|
||||
try:
|
||||
nickname_ = await GroupInfoUser.get_group_member_nickname(
|
||||
event.user_id, event.group_id
|
||||
)
|
||||
except AttributeError:
|
||||
nickname_ = ""
|
||||
async def _(event: MessageEvent):
|
||||
nickname_ = None
|
||||
card = None
|
||||
if isinstance(event, GroupMessageEvent):
|
||||
nickname_ = await GroupInfoUser.get_user_nickname(event.user_id, event.group_id)
|
||||
card = event.sender.card or event.sender.nickname
|
||||
else:
|
||||
nickname_ = await FriendUser.get_user_nickname(event.user_id)
|
||||
card = event.sender.nickname
|
||||
if nickname_:
|
||||
await my_nickname.send(
|
||||
random.choice(
|
||||
@@ -123,44 +172,20 @@ async def _(event: GroupMessageEvent):
|
||||
)
|
||||
)
|
||||
else:
|
||||
nickname_ = event.sender.card or event.sender.nickname
|
||||
await my_nickname.send(
|
||||
random.choice(
|
||||
["没..没有昵称嘛,{}", "啊,你是{}啊,我想叫你的昵称!", "是{}啊,有什么事吗?", "你是{}?"]
|
||||
).format(nickname_)
|
||||
)
|
||||
|
||||
|
||||
@my_nickname.handle()
|
||||
async def _(bot: Bot, event: PrivateMessageEvent, state: T_State):
|
||||
nickname_ = await FriendUser.get_friend_nickname(event.user_id)
|
||||
if nickname_:
|
||||
await my_nickname.send(
|
||||
random.choice(
|
||||
[
|
||||
f"我肯定记得你啊,你是{nickname_}啊",
|
||||
f"我不会忘记你的,你也不要忘记我!{nickname_}",
|
||||
f"哼哼,{NICKNAME}记忆力可是很好的,{nickname_}",
|
||||
f"嗯?你是失忆了嘛...{nickname_}..",
|
||||
f"不要小看{NICKNAME}的记忆力啊!笨蛋{nickname_}!QAQ",
|
||||
f"哎?{nickname_}..怎么了吗..突然这样问..",
|
||||
]
|
||||
)
|
||||
)
|
||||
else:
|
||||
nickname_ = (await bot.get_stranger_info(user_id=event.user_id))["nickname"]
|
||||
await my_nickname.send(
|
||||
random.choice(
|
||||
["没..没有昵称嘛,{}", "啊,你是{}啊,我想叫你的昵称!", "是{}啊,有什么事吗?", "你是{}?"]
|
||||
).format(nickname_)
|
||||
).format(card)
|
||||
)
|
||||
|
||||
|
||||
@cancel_nickname.handle()
|
||||
async def _(event: GroupMessageEvent):
|
||||
nickname_ = await GroupInfoUser.get_group_member_nickname(
|
||||
event.user_id, event.group_id
|
||||
)
|
||||
async def _(event: MessageEvent):
|
||||
nickname_ = None
|
||||
if isinstance(event, GroupMessageEvent):
|
||||
nickname_ = await GroupInfoUser.get_user_nickname(event.user_id, event.group_id)
|
||||
else:
|
||||
nickname_ = await FriendUser.get_user_nickname(event.user_id)
|
||||
if nickname_:
|
||||
await cancel_nickname.send(
|
||||
random.choice(
|
||||
@@ -173,28 +198,10 @@ async def _(event: GroupMessageEvent):
|
||||
]
|
||||
)
|
||||
)
|
||||
await GroupInfoUser.set_group_member_nickname(event.user_id, event.group_id, "")
|
||||
await BanUser.ban(event.user_id, 9, 60)
|
||||
else:
|
||||
await cancel_nickname.send("你在做梦吗?你没有昵称啊", at_sender=True)
|
||||
|
||||
|
||||
@cancel_nickname.handle()
|
||||
async def _(event: PrivateMessageEvent):
|
||||
nickname_ = await FriendUser.get_friend_nickname(event.user_id)
|
||||
if nickname_:
|
||||
await cancel_nickname.send(
|
||||
random.choice(
|
||||
[
|
||||
f"呜..{NICKNAME}睡一觉就会忘记的..和梦一样..{nickname_}",
|
||||
f"窝知道了..{nickname_}..",
|
||||
f"是{NICKNAME}哪里做的不好嘛..好吧..晚安{nickname_}",
|
||||
f"呃,{nickname_},下次我绝对绝对绝对不会再忘记你!",
|
||||
f"可..可恶!{nickname_}!太可恶了!呜",
|
||||
]
|
||||
)
|
||||
)
|
||||
await FriendUser.get_user_name(event.user_id)
|
||||
if isinstance(event, GroupMessageEvent):
|
||||
await GroupInfoUser.set_user_nickname(event.user_id, event.group_id, "")
|
||||
else:
|
||||
await FriendUser.set_user_nickname(event.user_id, "")
|
||||
await BanUser.ban(event.user_id, 9, 60)
|
||||
else:
|
||||
await cancel_nickname.send("你在做梦吗?你没有昵称啊", at_sender=True)
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
from nonebot import on_command, on_regex
|
||||
from nonebot.adapters.onebot.v11 import Bot, GroupMessageEvent, Message, MessageEvent
|
||||
from nonebot.params import CommandArg
|
||||
from nonebot.permission import SUPERUSER
|
||||
|
||||
from services.log import logger
|
||||
from utils.message_builder import image
|
||||
|
||||
from .data_source import (
|
||||
download_json,
|
||||
install_plugin,
|
||||
show_plugin_repo,
|
||||
uninstall_plugin,
|
||||
)
|
||||
|
||||
__zx_plugin_name__ = "插件商店 [Superuser]"
|
||||
__plugin_usage__ = """
|
||||
usage:
|
||||
下载安装插件
|
||||
指令:
|
||||
查看插件仓库
|
||||
更新插件仓库
|
||||
安装插件 [name/id] (重新安装等同于更新)
|
||||
卸载插件 [name/id]
|
||||
""".strip()
|
||||
__plugin_des__ = "从真寻适配仓库中下载插件"
|
||||
__plugin_cmd__ = ["查看插件仓库", "更新插件仓库", "安装插件 [name/id]", "卸载插件 [name/id]"]
|
||||
__plugin_version__ = 0.1
|
||||
__plugin_author__ = "HibiKier"
|
||||
|
||||
show_repo = on_regex("^查看插件仓库$", priority=1, block=True, permission=SUPERUSER)
|
||||
|
||||
update_repo = on_regex("^更新插件仓库$", priority=1, block=True, permission=SUPERUSER)
|
||||
|
||||
install_plugin_matcher = on_command(
|
||||
"安装插件", priority=1, block=True, permission=SUPERUSER
|
||||
)
|
||||
|
||||
uninstall_plugin_matcher = on_command(
|
||||
"卸载插件", priority=1, block=True, permission=SUPERUSER
|
||||
)
|
||||
|
||||
|
||||
@install_plugin_matcher.handle()
|
||||
async def _(bot: Bot, event: MessageEvent, arg: Message = CommandArg()):
|
||||
name = arg.extract_plain_text().strip()
|
||||
msg = await install_plugin(name)
|
||||
await install_plugin_matcher.send(msg)
|
||||
logger.info(f"安装插件: {name}", "安装插件", event.user_id)
|
||||
|
||||
|
||||
@uninstall_plugin_matcher.handle()
|
||||
async def _(bot: Bot, event: MessageEvent, arg: Message = CommandArg()):
|
||||
name = arg.extract_plain_text().strip()
|
||||
msg = await uninstall_plugin(name)
|
||||
await install_plugin_matcher.send(msg)
|
||||
logger.info(f"卸载插件: {name}", "卸载插件", event.user_id)
|
||||
|
||||
|
||||
@update_repo.handle()
|
||||
async def _(bot: Bot, event: MessageEvent):
|
||||
code = await download_json()
|
||||
if code == 200:
|
||||
await update_repo.finish("更新插件仓库信息成功!")
|
||||
await update_repo.send("更新插件仓库信息失败!")
|
||||
logger.info("更新插件仓库信息", "更新插件仓库信息", event.user_id)
|
||||
|
||||
|
||||
@show_repo.handle()
|
||||
async def _(bot: Bot, event: MessageEvent):
|
||||
msg = await show_plugin_repo()
|
||||
if isinstance(msg, int):
|
||||
await show_repo.finish("文件下载失败或解压失败..")
|
||||
await show_repo.send(image(msg))
|
||||
logger.info("查看插件仓库", "查看插件仓库", event.user_id)
|
||||
@@ -0,0 +1,201 @@
|
||||
import os
|
||||
import shutil
|
||||
import zipfile
|
||||
from pathlib import Path
|
||||
from typing import Tuple, Union
|
||||
|
||||
import ujson as json
|
||||
|
||||
from configs.path_config import DATA_PATH, TEMP_PATH
|
||||
from services import logger
|
||||
from utils.http_utils import AsyncHttpx
|
||||
from utils.image_utils import BuildImage, text2image
|
||||
from utils.manager import plugins_manager
|
||||
from utils.utils import is_number
|
||||
|
||||
path = DATA_PATH / "plugin_shop"
|
||||
if not path.exists():
|
||||
path.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
repo_json_url = "https://github.com/zhenxun-org/nonebot_plugins_zhenxun_bot/archive/refs/heads/index.zip"
|
||||
repo_zip_path = TEMP_PATH / "plugin_repo_json.zip"
|
||||
plugin_json = path / "zhenxun_plugins.json"
|
||||
|
||||
extensive_plugin_path = Path() / "extensive_plugin"
|
||||
|
||||
path = DATA_PATH / "plugin_shop"
|
||||
if not path.exists():
|
||||
path.mkdir(parents=True, exist_ok=True)
|
||||
if not extensive_plugin_path.exists():
|
||||
extensive_plugin_path.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
data = {}
|
||||
|
||||
|
||||
async def install_plugin(name: str) -> str:
|
||||
"""
|
||||
安装插件
|
||||
:param name: 插件名或下标
|
||||
"""
|
||||
try:
|
||||
if is_number(name):
|
||||
name, code = await get_plugin_name(int(name))
|
||||
if code != 200:
|
||||
return name
|
||||
plugin_url = data[name]["download_url"]
|
||||
url = (await AsyncHttpx.get(plugin_url)).headers.get("Location")
|
||||
zip_file = TEMP_PATH / f"{name}.zip"
|
||||
if zip_file.exists():
|
||||
zip_file.unlink()
|
||||
if await AsyncHttpx.download_file(url, zip_file):
|
||||
logger.debug("开始解压插件压缩包...", "安装插件", target=name)
|
||||
# 解压
|
||||
zf = zipfile.ZipFile(zip_file, "r")
|
||||
extract_path = TEMP_PATH / f"{name}"
|
||||
if extract_path.exists():
|
||||
shutil.rmtree(extract_path.absolute(), ignore_errors=True)
|
||||
extract_path.mkdir(exist_ok=True, parents=True)
|
||||
for file in zf.namelist():
|
||||
zf.extract(file, extract_path)
|
||||
zf.close()
|
||||
logger.debug("解压插件压缩包完成...", "安装插件", target=name)
|
||||
logger.debug("开始移动插件文件夹...", "安装插件", target=name)
|
||||
if (extensive_plugin_path / f"{name}").exists():
|
||||
logger.debug(
|
||||
"extensive_plugin目录下文件夹已存在,删除该目录插件文件夹...", "安装插件", target=name
|
||||
)
|
||||
shutil.rmtree(
|
||||
(extensive_plugin_path / f"{name}").absolute(), ignore_errors=True
|
||||
)
|
||||
extract_path.rename(extensive_plugin_path / f"{name}")
|
||||
prompt = ""
|
||||
if "pyproject.toml" in os.listdir(extensive_plugin_path / f"{name}"):
|
||||
prompt = "检测到该插件含有额外依赖,当前安装无法保证依赖完全安装成功。"
|
||||
os.system(
|
||||
f"poetry run pip install -r {(extensive_plugin_path / f'{name}' / 'pyproject.toml').absolute()}"
|
||||
)
|
||||
elif "requirements.txt" in os.listdir(extensive_plugin_path / f"{name}"):
|
||||
prompt = "检测到该插件含有额外依赖,当前安装无法保证依赖完全安装成功。"
|
||||
os.system(
|
||||
f"poetry run pip install -r {(extensive_plugin_path / f'{name}' / 'requirements.txt').absolute()}"
|
||||
)
|
||||
with open(extensive_plugin_path / f"{name}" / "plugin_info.json", "w") as f:
|
||||
json.dump(data[name], f, ensure_ascii=False, indent=4)
|
||||
logger.debug("移动插件文件夹完成...", "安装插件", target=name)
|
||||
logger.info(f"成功安装插件 {name} 成功!\n{prompt}", "安装插件", target=name)
|
||||
return f"成功安装插件 {name},请重启真寻!"
|
||||
except Exception as e:
|
||||
logger.error(f"安装插失败", "安装插件", target=name, e=e)
|
||||
return f"安装插件 {name} 失败 {type(e)}:{e}"
|
||||
|
||||
|
||||
async def uninstall_plugin(name: str) -> str:
|
||||
"""
|
||||
删除插件
|
||||
:param name: 插件名或下标
|
||||
"""
|
||||
try:
|
||||
if is_number(name):
|
||||
name, code = await get_plugin_name(int(name))
|
||||
if code != 200:
|
||||
return name
|
||||
if name not in os.listdir(extensive_plugin_path):
|
||||
return f"未安装 {name} 插件!"
|
||||
shutil.rmtree((extensive_plugin_path / name).absolute(), ignore_errors=True)
|
||||
logger.info(f"插件 {name} 删除成功!")
|
||||
return f"插件 {name} 删除成功!"
|
||||
except Exception as e:
|
||||
logger.error(f"删除插件 {name} 失败 {type(e)}:{e}")
|
||||
return f"删除插件 {name} 失败 {type(e)}:{e}"
|
||||
|
||||
|
||||
async def show_plugin_repo() -> Union[int, str]:
|
||||
"""
|
||||
获取插件仓库数据并格式化
|
||||
"""
|
||||
if not plugin_json.exists():
|
||||
code = await download_json()
|
||||
if code != 200:
|
||||
return code
|
||||
plugin_info = json.load(open(plugin_json, "r", encoding="utf8"))
|
||||
plugins_data = plugins_manager.get_data()
|
||||
load_plugin_list = plugins_data.keys()
|
||||
image_list = []
|
||||
w, h = 0, 0
|
||||
line_height = 10
|
||||
for i, key in enumerate(plugin_info.keys()):
|
||||
data[key] = {
|
||||
"名称": plugin_info[key]["plugin_name"],
|
||||
"模块": key,
|
||||
"作者": plugin_info[key]["author"],
|
||||
"版本": plugin_info[key]["version"],
|
||||
"简介": plugin_info[key]["introduction"],
|
||||
"download_url": plugin_info[key]["download_url"],
|
||||
"github_url": plugin_info[key]["github_url"],
|
||||
}
|
||||
status = ""
|
||||
version = ""
|
||||
if key in load_plugin_list:
|
||||
status = "<f font_color=#1a7e30>[已安装]</f>"
|
||||
version = f"<f font_color=#1a7e30>[{plugins_data[key].version}]</f>"
|
||||
s = (
|
||||
f'id:{i+1}\n名称:{plugin_info[key]["plugin_name"]}'
|
||||
f" \t\t{status}\n"
|
||||
f"模块:{key}\n"
|
||||
f'作者:{plugin_info[key]["author"]}\n'
|
||||
f'版本:{plugin_info[key]["version"]} \t\t{version}\n'
|
||||
f'简介:{plugin_info[key]["introduction"]}\n'
|
||||
f"-------------------"
|
||||
)
|
||||
img = await text2image(s, font_size=20, color="#f9f6f2")
|
||||
w = w if w > img.w else img.w
|
||||
h += img.h + line_height
|
||||
image_list.append(img)
|
||||
A = BuildImage(w + 50, h + 50, color="#f9f6f2")
|
||||
cur_h = 25
|
||||
for img in image_list:
|
||||
await A.apaste(img, (25, cur_h))
|
||||
cur_h += img.h + line_height
|
||||
return A.pic2bs4()
|
||||
|
||||
|
||||
async def download_json() -> int:
|
||||
"""
|
||||
下载插件库json文件
|
||||
"""
|
||||
try:
|
||||
url = (await AsyncHttpx.get(repo_json_url)).headers.get("Location")
|
||||
if repo_zip_path.exists():
|
||||
repo_zip_path.unlink()
|
||||
if await AsyncHttpx.download_file(url, repo_zip_path):
|
||||
zf = zipfile.ZipFile(repo_zip_path, "r")
|
||||
extract_path = path / "temp"
|
||||
for file in zf.namelist():
|
||||
zf.extract(file, extract_path)
|
||||
zf.close()
|
||||
if plugin_json.exists():
|
||||
plugin_json.unlink()
|
||||
(
|
||||
extract_path
|
||||
/ "nonebot_plugins_zhenxun_bot-index"
|
||||
/ "zhenxun_plugins.json"
|
||||
).rename(plugin_json)
|
||||
shutil.rmtree(extract_path.absolute(), ignore_errors=True)
|
||||
return 200
|
||||
except Exception as e:
|
||||
logger.error(f"下载插件库压缩包失败或解压失败 {type(e)}:{e}")
|
||||
return 999
|
||||
|
||||
|
||||
async def get_plugin_name(name: int) -> Tuple[str, int]:
|
||||
"""
|
||||
通过下标获取插件名
|
||||
:param name: 下标
|
||||
"""
|
||||
name = int(name)
|
||||
if not data:
|
||||
await show_plugin_repo()
|
||||
if name < 1 or name > len(data.keys()):
|
||||
return "下标超过上下限!", 999
|
||||
name = list(data.keys())[name - 1]
|
||||
return name, 200
|
||||
+139
-137
@@ -1,20 +1,16 @@
|
||||
from asyncpg.exceptions import (
|
||||
DuplicateColumnError,
|
||||
UndefinedColumnError,
|
||||
PostgresSyntaxError,
|
||||
)
|
||||
from nonebot import Driver
|
||||
from services.db_context import db
|
||||
from models.group_info import GroupInfo
|
||||
from models.bag_user import BagUser
|
||||
from nonebot.adapters.onebot.v11 import Bot
|
||||
from services.log import logger
|
||||
from configs.path_config import TEXT_PATH
|
||||
import random
|
||||
from asyncio.exceptions import TimeoutError
|
||||
from typing import List
|
||||
from utils.http_utils import AsyncHttpx
|
||||
from utils.utils import scheduler
|
||||
|
||||
import nonebot
|
||||
from nonebot.adapters.onebot.v11 import Bot
|
||||
from nonebot.drivers import Driver
|
||||
|
||||
from configs.path_config import TEXT_PATH
|
||||
from models.bag_user import BagUser
|
||||
from models.group_info import GroupInfo
|
||||
from services.log import logger
|
||||
from utils.http_utils import AsyncHttpx
|
||||
from utils.utils import GDict, scheduler
|
||||
|
||||
try:
|
||||
import ujson as json
|
||||
@@ -35,6 +31,7 @@ async def update_city():
|
||||
data = {}
|
||||
if not china_city.exists():
|
||||
try:
|
||||
logger.debug("开始更新城市列表...")
|
||||
res = await AsyncHttpx.get(
|
||||
"http://www.weather.com.cn/data/city3jdata/china.html", timeout=5
|
||||
)
|
||||
@@ -53,99 +50,97 @@ async def update_city():
|
||||
with open(china_city, "w", encoding="utf8") as f:
|
||||
json.dump(data, f, indent=4, ensure_ascii=False)
|
||||
logger.info("自动更新城市列表完成.....")
|
||||
except TimeoutError:
|
||||
logger.warning("自动更新城市列表超时.....")
|
||||
except ValueError:
|
||||
logger.warning("自动城市列表失败.....")
|
||||
except TimeoutError as e:
|
||||
logger.warning("自动更新城市列表超时...", e=e)
|
||||
except ValueError as e:
|
||||
logger.warning("自动城市列表失败.....", e=e)
|
||||
except Exception as e:
|
||||
logger.error(f"自动城市列表未知错误 {type(e)}:{e}")
|
||||
logger.error(f"自动城市列表未知错误", e=e)
|
||||
|
||||
|
||||
@driver.on_startup
|
||||
async def _():
|
||||
"""
|
||||
数据库表结构变换
|
||||
"""
|
||||
_flag = []
|
||||
sql_str = [
|
||||
(
|
||||
"ALTER TABLE group_info ADD group_flag Integer NOT NULL DEFAULT 0;",
|
||||
"group_info",
|
||||
), # group_info表添加一个group_flag
|
||||
(
|
||||
"ALTER TABLE bag_users rename belonging_group To group_id;",
|
||||
"bag_users",
|
||||
), # 将 bag_users 的 belonging_group 改为 group_id
|
||||
(
|
||||
"ALTER TABLE group_info_users rename belonging_group To group_id;",
|
||||
"group_info_users",
|
||||
),
|
||||
(
|
||||
"ALTER TABLE sign_group_users rename belonging_group To group_id;",
|
||||
"sign_group_users",
|
||||
),
|
||||
(
|
||||
"ALTER TABLE open_cases_users rename belonging_group To group_id;",
|
||||
"open_cases_users",
|
||||
),
|
||||
(
|
||||
"ALTER TABLE bag_users ADD property json NOT NULL DEFAULT '{}';",
|
||||
"bag_users",
|
||||
), # bag_users 新增字段 property 替代 props
|
||||
(
|
||||
"ALTER TABLE genshin ADD auto_sign_time timestamp with time zone;",
|
||||
"genshin"
|
||||
), # 新增原神自动签到字段
|
||||
(
|
||||
"ALTER TABLE genshin ADD resin_remind boolean DEFAULT False;",
|
||||
"genshin"
|
||||
), # 新增原神自动签到字段
|
||||
(
|
||||
"ALTER TABLE genshin ADD resin_recovery_time timestamp with time zone;",
|
||||
"genshin"
|
||||
), # 新增原神自动签到字段
|
||||
(
|
||||
"ALTER TABLE genshin ADD bind_group Integer;",
|
||||
"genshin"
|
||||
), # 新增原神群号绑定字段
|
||||
]
|
||||
for sql in sql_str:
|
||||
try:
|
||||
flag = sql[1]
|
||||
sql = sql[0]
|
||||
query = db.text(sql)
|
||||
await db.first(query)
|
||||
logger.info(f"完成sql操作:{sql}")
|
||||
_flag.append(flag)
|
||||
except (DuplicateColumnError, UndefinedColumnError):
|
||||
pass
|
||||
except PostgresSyntaxError:
|
||||
logger.error(f"语法错误:执行sql失败:{sql}")
|
||||
# bag_user 将文本转为字典格式
|
||||
await __database_script(_flag)
|
||||
|
||||
# 完成后
|
||||
end_sql_str = [
|
||||
# "ALTER TABLE bag_users DROP COLUMN props;" # 删除 bag_users 的 props 字段(还不到时候)
|
||||
]
|
||||
for sql in end_sql_str:
|
||||
try:
|
||||
query = db.text(sql)
|
||||
await db.first(query)
|
||||
logger.info(f"完成执行sql操作:{sql}")
|
||||
except (DuplicateColumnError, UndefinedColumnError):
|
||||
pass
|
||||
except PostgresSyntaxError:
|
||||
logger.error(f"语法错误:执行sql失败:{sql}")
|
||||
|
||||
# str2json_sql = ["alter table bag_users alter COLUMN props type json USING props::json;"] # 字段类型替换
|
||||
# rename_sql = 'alter table {} rename {} to {};' # 字段更名
|
||||
# for sql in str2json_sql:
|
||||
# try:
|
||||
# query = db.text(sql)
|
||||
# await db.first(query)
|
||||
# except DuplicateColumnError:
|
||||
# pass
|
||||
# @driver.on_startup
|
||||
# async def _():
|
||||
# """
|
||||
# 数据库表结构变换
|
||||
# """
|
||||
# _flag = []
|
||||
# sql_str = [
|
||||
# (
|
||||
# "ALTER TABLE group_info ADD group_flag Integer NOT NULL DEFAULT 0;",
|
||||
# "group_info",
|
||||
# ), # group_info表添加一个group_flag
|
||||
# (
|
||||
# "ALTER TABLE bag_users rename belonging_group To group_id;",
|
||||
# "bag_users",
|
||||
# ), # 将 bag_users 的 belonging_group 改为 group_id
|
||||
# (
|
||||
# "ALTER TABLE group_info_users rename belonging_group To group_id;",
|
||||
# "group_info_users",
|
||||
# ),
|
||||
# (
|
||||
# "ALTER TABLE sign_group_users rename belonging_group To group_id;",
|
||||
# "sign_group_users",
|
||||
# ),
|
||||
# (
|
||||
# "ALTER TABLE open_cases_users rename belonging_group To group_id;",
|
||||
# "open_cases_users",
|
||||
# ),
|
||||
# (
|
||||
# "ALTER TABLE bag_users ADD property json NOT NULL DEFAULT '{}';",
|
||||
# "bag_users",
|
||||
# ), # bag_users 新增字段 property 替代 props
|
||||
# (
|
||||
# "ALTER TABLE genshin ADD auto_sign_time timestamp with time zone;",
|
||||
# "genshin",
|
||||
# ), # 新增原神自动签到字段
|
||||
# (
|
||||
# "ALTER TABLE genshin ADD resin_remind boolean DEFAULT False;",
|
||||
# "genshin",
|
||||
# ), # 新增原神自动签到字段
|
||||
# (
|
||||
# "ALTER TABLE genshin ADD resin_recovery_time timestamp with time zone;",
|
||||
# "genshin",
|
||||
# ), # 新增原神自动签到字段
|
||||
# ("ALTER TABLE genshin ADD bind_group Integer;", "genshin"), # 新增原神群号绑定字段
|
||||
# (
|
||||
# "ALTER TABLE genshin ADD login_ticket VARCHAR(255) DEFAULT '';",
|
||||
# "genshin",
|
||||
# ), # 新增米游社login_ticket绑定字段
|
||||
# (
|
||||
# "ALTER TABLE genshin ADD stuid VARCHAR(255) DEFAULT '';",
|
||||
# "genshin",
|
||||
# ), # 新增米游社stuid绑定字段
|
||||
# (
|
||||
# "ALTER TABLE genshin ADD stoken VARCHAR(255) DEFAULT '';",
|
||||
# "genshin",
|
||||
# ), # 新增米游社stoken绑定字段
|
||||
# ("ALTER TABLE chat_history ADD plain_text Text;", "chat_history"), # 新增纯文本
|
||||
# (
|
||||
# "ALTER TABLE goods_info ADD daily_limit Integer DEFAULT 0;",
|
||||
# "goods_info",
|
||||
# ), # 新增纯文本
|
||||
# (
|
||||
# "ALTER TABLE goods_info ADD daily_purchase_limit Json DEFAULT '{}';",
|
||||
# "goods_info",
|
||||
# ), # 新增纯文本
|
||||
# ]
|
||||
# for sql in sql_str + GDict.get("run_sql", []):
|
||||
# try:
|
||||
# if isinstance(sql, str):
|
||||
# flag = f"{random.randint(1, 10000)}"
|
||||
# else:
|
||||
# flag = sql[1]
|
||||
# sql = sql[0]
|
||||
# query = db.text(sql)
|
||||
# await db.first(query)
|
||||
# logger.info(f"完成sql操作:{sql}")
|
||||
# _flag.append(flag)
|
||||
# except (DuplicateColumnError, UndefinedColumnError):
|
||||
# pass
|
||||
# except PostgresSyntaxError:
|
||||
# logger.error(f"语法错误:执行sql失败:{sql}")
|
||||
# bag_user 将文本转为字典格式
|
||||
# await __database_script(_flag)
|
||||
|
||||
|
||||
@driver.on_bot_connect
|
||||
@@ -154,51 +149,58 @@ async def _(bot: Bot):
|
||||
版本某些需要的变换
|
||||
"""
|
||||
# 清空不存在的群聊信息,并将已所有已存在的群聊group_flag设置为1(认证所有已存在的群)
|
||||
if not await GroupInfo.get_group_info(114514):
|
||||
if not await GroupInfo.get_or_none(group_id=114514):
|
||||
# 标识符,该功能只需执行一次
|
||||
await GroupInfo.add_group_info(114514, "114514", 114514, 114514, 1)
|
||||
await GroupInfo.create(
|
||||
group_id=114514,
|
||||
group_name="114514",
|
||||
max_member_count=114514,
|
||||
member_count=114514,
|
||||
group_flag=1,
|
||||
)
|
||||
group_list = await bot.get_group_list()
|
||||
group_list = [g["group_id"] for g in group_list]
|
||||
_gl = [x.group_id for x in await GroupInfo.get_all_group()]
|
||||
_gl = [x.group_id for x in await GroupInfo.all()]
|
||||
if 114514 in _gl:
|
||||
_gl.remove(114514)
|
||||
for group_id in _gl:
|
||||
if group_id in group_list:
|
||||
if await GroupInfo.get_group_info(group_id):
|
||||
await GroupInfo.set_group_flag(group_id, 1)
|
||||
if group := await GroupInfo.get_or_none(group_id=group_id):
|
||||
group.group_flag = 1
|
||||
await group.save(update_fields=["group_flag"])
|
||||
else:
|
||||
group_info = await bot.get_group_info(group_id=group_id)
|
||||
await GroupInfo.add_group_info(
|
||||
group_info["group_id"],
|
||||
group_info["group_name"],
|
||||
group_info["max_member_count"],
|
||||
group_info["member_count"],
|
||||
1,
|
||||
await GroupInfo.create(
|
||||
group_id=group_info["group_id"],
|
||||
group_name=group_info["group_name"],
|
||||
max_member_count=group_info["max_member_count"],
|
||||
member_count=group_info["member_count"],
|
||||
group_flag=1,
|
||||
)
|
||||
logger.info(f"已将群聊 {group_id} 添加认证...")
|
||||
logger.info(f"已添加群认证...", group_id=group_id)
|
||||
else:
|
||||
await GroupInfo.delete_group_info(group_id)
|
||||
logger.info(f"移除不存在的群聊信息:{group_id}")
|
||||
await GroupInfo.filter(group_id=group_id).delete()
|
||||
logger.info(f"移除不存在的群聊信息", group_id=group_id)
|
||||
|
||||
|
||||
async def __database_script(_flag: List[str]):
|
||||
# bag_user 将文本转为字典格式
|
||||
if "bag_users" in _flag:
|
||||
for x in await BagUser.get_all_users():
|
||||
props = {}
|
||||
if x.props:
|
||||
for prop in [p for p in x.props.split(",") if p]:
|
||||
if props.get(prop):
|
||||
props[prop] += 1
|
||||
else:
|
||||
props[prop] = 1
|
||||
logger.info(
|
||||
f"__database_script USER {x.user_qq} GROUP {x.group_id} 更新数据 {props}"
|
||||
)
|
||||
await x.update(
|
||||
property=props,
|
||||
props="",
|
||||
).apply()
|
||||
# async def __database_script(_flag: List[str]):
|
||||
# # bag_user 将文本转为字典格式
|
||||
# if "bag_users" in _flag:
|
||||
# for x in await BagUser.get_all_users():
|
||||
# props = {}
|
||||
# if x.props:
|
||||
# for prop in [p for p in x.props.split(",") if p]:
|
||||
# if props.get(prop):
|
||||
# props[prop] += 1
|
||||
# else:
|
||||
# props[prop] = 1
|
||||
# logger.info(
|
||||
# f"__database_script USER {x.user_qq} GROUP {x.group_id} 更新数据 {props}"
|
||||
# )
|
||||
# await x.update(
|
||||
# property=props,
|
||||
# props="",
|
||||
# ).apply()
|
||||
|
||||
|
||||
# 自动更新城市列表
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
from pathlib import Path
|
||||
|
||||
import nonebot
|
||||
from nonebot.drivers import Driver
|
||||
|
||||
from configs.config import Config
|
||||
from utils.decorator.shop import shop_register
|
||||
|
||||
driver: Driver = nonebot.get_driver()
|
||||
|
||||
|
||||
Config.add_plugin_config(
|
||||
@@ -7,8 +14,14 @@ Config.add_plugin_config(
|
||||
"IMPORT_DEFAULT_SHOP_GOODS",
|
||||
True,
|
||||
help_="导入商店自带的三个商品",
|
||||
default_value=True
|
||||
default_value=True,
|
||||
type=bool,
|
||||
)
|
||||
|
||||
|
||||
nonebot.load_plugins("basic_plugins/shop")
|
||||
nonebot.load_plugins(str(Path(__file__).parent.resolve()))
|
||||
|
||||
|
||||
@driver.on_bot_connect
|
||||
async def _():
|
||||
await shop_register.load_register()
|
||||
|
||||
+48
-33
@@ -1,14 +1,15 @@
|
||||
from nonebot import on_command
|
||||
from services.log import logger
|
||||
from nonebot.adapters.onebot.v11 import GroupMessageEvent, Message
|
||||
from nonebot.params import CommandArg
|
||||
from utils.utils import is_number
|
||||
from models.bag_user import BagUser
|
||||
from services.db_context import db
|
||||
from nonebot.adapters.onebot.v11.permission import GROUP
|
||||
from models.goods_info import GoodsInfo
|
||||
import time
|
||||
|
||||
from nonebot import on_command
|
||||
from nonebot.adapters.onebot.v11 import GroupMessageEvent, Message
|
||||
from nonebot.adapters.onebot.v11.permission import GROUP
|
||||
from nonebot.params import CommandArg
|
||||
|
||||
from models.bag_user import BagUser
|
||||
from models.goods_info import GoodsInfo
|
||||
from models.user_shop_gold_log import UserShopGoldLog
|
||||
from services.log import logger
|
||||
from utils.utils import is_number
|
||||
|
||||
__zx_plugin_name__ = "商店 - 购买道具"
|
||||
__plugin_usage__ = """
|
||||
@@ -46,10 +47,7 @@ async def _(event: GroupMessageEvent, arg: Message = CommandArg()):
|
||||
for x in await GoodsInfo.get_all_goods()
|
||||
if x.goods_limit_time > time.time() or x.goods_limit_time == 0
|
||||
]
|
||||
goods_name_list = [
|
||||
x.goods_name
|
||||
for x in goods_list
|
||||
]
|
||||
goods_name_list = [x.goods_name for x in goods_list]
|
||||
msg = arg.extract_plain_text().strip().split()
|
||||
num = 1
|
||||
if len(msg) > 1:
|
||||
@@ -72,23 +70,40 @@ async def _(event: GroupMessageEvent, arg: Message = CommandArg()):
|
||||
await buy.finish("请输入正确的商品名称!")
|
||||
else:
|
||||
await buy.finish("请输入正确的商品名称!", at_sender=True)
|
||||
async with db.transaction():
|
||||
if (
|
||||
await BagUser.get_gold(event.user_id, event.group_id)
|
||||
) < goods.goods_price * num * goods.goods_discount:
|
||||
await buy.finish("您的金币好像不太够哦", at_sender=True)
|
||||
if await BagUser.buy_property(event.user_id, event.group_id, goods, num):
|
||||
await buy.send(
|
||||
f"花费 {goods.goods_price * num * goods.goods_discount} 金币购买 {goods.goods_name} ×{num} 成功!",
|
||||
at_sender=True,
|
||||
)
|
||||
logger.info(
|
||||
f"USER {event.user_id} GROUP {event.group_id} "
|
||||
f"花费 {goods.goods_price*num} 金币购买 {goods.goods_name} ×{num} 成功!"
|
||||
)
|
||||
else:
|
||||
await buy.send(f"{goods.goods_name} 购买失败!", at_sender=True)
|
||||
logger.info(
|
||||
f"USER {event.user_id} GROUP {event.group_id} "
|
||||
f"花费 {goods.goods_price * num * goods.goods_discount} 金币购买 {goods.goods_name} ×{num} 失败!"
|
||||
)
|
||||
if (
|
||||
await BagUser.get_gold(event.user_id, event.group_id)
|
||||
) < goods.goods_price * num * goods.goods_discount:
|
||||
await buy.finish("您的金币好像不太够哦", at_sender=True)
|
||||
flag, n = await GoodsInfo.check_user_daily_purchase(
|
||||
goods, event.user_id, event.group_id, num
|
||||
)
|
||||
if flag:
|
||||
await buy.finish(f"该次购买将超过每日次数限制,目前该道具还可以购买{n}次哦", at_sender=True)
|
||||
spend_gold = int(goods.goods_discount * goods.goods_price * num)
|
||||
await BagUser.spend_gold(event.user_id, event.group_id, spend_gold)
|
||||
await BagUser.add_property(event.user_id, event.group_id, goods.goods_name, num)
|
||||
await GoodsInfo.add_user_daily_purchase(goods, event.user_id, event.group_id, num)
|
||||
await buy.send(
|
||||
f"花费 {goods.goods_price * num * goods.goods_discount} 金币购买 {goods.goods_name} ×{num} 成功!",
|
||||
at_sender=True,
|
||||
)
|
||||
logger.info(
|
||||
f"花费 {goods.goods_price*num} 金币购买 {goods.goods_name} ×{num} 成功!",
|
||||
"购买道具",
|
||||
event.user_id,
|
||||
event.group_id,
|
||||
)
|
||||
await UserShopGoldLog.create(
|
||||
user_qq=event.user_id,
|
||||
group_id=event.group_id,
|
||||
type=0,
|
||||
name=goods.goods_name,
|
||||
num=num,
|
||||
spend_gold=goods.goods_price * num * goods.goods_discount,
|
||||
)
|
||||
# else:
|
||||
# await buy.send(f"{goods.goods_name} 购买失败!", at_sender=True)
|
||||
# logger.info(
|
||||
# f"USER {event.user_id} GROUP {event.group_id} "
|
||||
# f"花费 {goods.goods_price * num * goods.goods_discount} 金币购买 {goods.goods_name} ×{num} 失败!"
|
||||
# )
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
from nonebot import on_command
|
||||
from nonebot.adapters.onebot.v11 import GroupMessageEvent, Message
|
||||
from nonebot.params import CommandArg
|
||||
from nonebot.adapters.onebot.v11 import ActionFailed, GroupMessageEvent, Message
|
||||
from nonebot.adapters.onebot.v11.permission import GROUP
|
||||
from utils.data_utils import init_rank
|
||||
from nonebot.params import CommandArg
|
||||
|
||||
from models.bag_user import BagUser
|
||||
from utils.data_utils import init_rank
|
||||
from utils.image_utils import text2image
|
||||
from utils.message_builder import image
|
||||
from utils.utils import is_number
|
||||
|
||||
@@ -34,7 +36,13 @@ gold_rank = on_command("金币排行", priority=5, block=True, permission=GROUP)
|
||||
|
||||
@my_gold.handle()
|
||||
async def _(event: GroupMessageEvent):
|
||||
await my_gold.finish(await BagUser.get_user_total_gold(event.user_id, event.group_id))
|
||||
msg = await BagUser.get_user_total_gold(event.user_id, event.group_id)
|
||||
try:
|
||||
await my_gold.send(msg)
|
||||
except ActionFailed:
|
||||
await my_gold.send(
|
||||
image(b64=(await text2image(msg, color="#f9f6f2", padding=10)).pic2bs4())
|
||||
)
|
||||
|
||||
|
||||
@gold_rank.handle()
|
||||
@@ -44,9 +52,11 @@ async def _(event: GroupMessageEvent, arg: Message = CommandArg()):
|
||||
num = int(num)
|
||||
else:
|
||||
num = 10
|
||||
all_users = await BagUser.get_all_users(event.group_id)
|
||||
all_users = await BagUser.filter(group_id=event.group_id)
|
||||
all_user_id = [user.user_qq for user in all_users]
|
||||
all_user_data = [user.gold for user in all_users]
|
||||
rank_image = await init_rank("金币排行", all_user_id, all_user_data, event.group_id, num)
|
||||
rank_image = await init_rank(
|
||||
"金币排行", all_user_id, all_user_data, event.group_id, num
|
||||
)
|
||||
if rank_image:
|
||||
await gold_rank.finish(image(b64=rank_image.pic2bs4()))
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
from nonebot import on_command
|
||||
from services.log import logger
|
||||
from nonebot.adapters.onebot.v11 import GroupMessageEvent
|
||||
from models.bag_user import BagUser
|
||||
from nonebot.adapters.onebot.v11.permission import GROUP
|
||||
|
||||
from models.bag_user import BagUser
|
||||
from services.log import logger
|
||||
from utils.message_builder import image
|
||||
|
||||
from ._data_source import create_bag_image
|
||||
|
||||
__zx_plugin_name__ = "商店 - 我的道具"
|
||||
__plugin_usage__ = """
|
||||
@@ -14,7 +17,7 @@ usage:
|
||||
""".strip()
|
||||
__plugin_des__ = "商店 - 我的道具"
|
||||
__plugin_cmd__ = ["我的道具"]
|
||||
__plugin_type__ = ('商店',)
|
||||
__plugin_type__ = ("商店",)
|
||||
__plugin_version__ = 0.1
|
||||
__plugin_author__ = "HibiKier"
|
||||
__plugin_settings__ = {
|
||||
@@ -32,10 +35,7 @@ my_props = on_command("我的道具", priority=5, block=True, permission=GROUP)
|
||||
async def _(event: GroupMessageEvent):
|
||||
props = await BagUser.get_property(event.user_id, event.group_id)
|
||||
if props:
|
||||
rst = ""
|
||||
for i, p in enumerate(props.keys()):
|
||||
rst += f"{i+1}.{p}\t×{props[p]}\n"
|
||||
await my_props.send("\n" + rst[:-1], at_sender=True)
|
||||
await my_props.send(image(b64=await create_bag_image(props)))
|
||||
logger.info(f"USER {event.user_id} GROUP {event.group_id} 查看我的道具")
|
||||
else:
|
||||
await my_props.finish("您的背包里没有任何的道具噢~", at_sender=True)
|
||||
@@ -0,0 +1,121 @@
|
||||
from typing import Dict, List, Optional
|
||||
|
||||
from models.bag_user import BagUser
|
||||
from models.goods_info import GoodsInfo
|
||||
from utils.image_utils import BuildImage
|
||||
from configs.path_config import IMAGE_PATH
|
||||
|
||||
|
||||
icon_path = IMAGE_PATH / "shop_icon"
|
||||
|
||||
|
||||
async def create_bag_image(props: Dict[str, int]):
|
||||
"""
|
||||
说明:
|
||||
创建背包道具图片
|
||||
参数:
|
||||
:param props: 道具仓库字典
|
||||
"""
|
||||
goods_list = await GoodsInfo.get_all_goods()
|
||||
active_props = await _init_prop(props, [x for x in goods_list if not x.is_passive])
|
||||
passive_props = await _init_prop(props, [x for x in goods_list if x.is_passive])
|
||||
active_w = 0
|
||||
active_h = 0
|
||||
passive_w = 0
|
||||
passive_h = 0
|
||||
if active_props:
|
||||
img = BuildImage(
|
||||
active_props.w,
|
||||
active_props.h + 70,
|
||||
font="CJGaoDeGuo.otf",
|
||||
font_size=30,
|
||||
color="#f9f6f2",
|
||||
)
|
||||
await img.apaste(active_props, (0, 70))
|
||||
await img.atext((0, 30), "主动道具")
|
||||
active_props = img
|
||||
active_w = img.w
|
||||
active_h = img.h
|
||||
if passive_props:
|
||||
img = BuildImage(
|
||||
passive_props.w,
|
||||
passive_props.h + 70,
|
||||
font="CJGaoDeGuo.otf",
|
||||
font_size=30,
|
||||
color="#f9f6f2",
|
||||
)
|
||||
await img.apaste(passive_props, (0, 70))
|
||||
await img.atext((0, 30), "被动道具")
|
||||
passive_props = img
|
||||
passive_w = img.w
|
||||
passive_h = img.h
|
||||
A = BuildImage(
|
||||
active_w + passive_w + 100,
|
||||
max(active_h, passive_h) + 60,
|
||||
font="CJGaoDeGuo.otf",
|
||||
font_size=30,
|
||||
color="#f9f6f2",
|
||||
)
|
||||
curr_w = 50
|
||||
if active_props:
|
||||
await A.apaste(active_props, (curr_w, 0))
|
||||
curr_w += active_props.w + 10
|
||||
if passive_props:
|
||||
await A.apaste(passive_props, (curr_w, 0))
|
||||
if active_props and passive_props:
|
||||
await A.aline(
|
||||
(active_props.w + 45, 70, active_props.w + 45, A.h - 20), fill=(0, 0, 0)
|
||||
)
|
||||
return A.pic2bs4()
|
||||
|
||||
|
||||
async def _init_prop(
|
||||
props: Dict[str, int], _props: List[GoodsInfo]
|
||||
) -> Optional[BuildImage]:
|
||||
"""
|
||||
说明:
|
||||
构造道具列表图片
|
||||
参数:
|
||||
:param props: 道具仓库字典
|
||||
:param _props: 道具列表
|
||||
"""
|
||||
active_name = [x.goods_name for x in _props]
|
||||
name_list = [x for x in props.keys() if x in active_name]
|
||||
if not name_list:
|
||||
return None
|
||||
temp_img = BuildImage(0, 0, font_size=20)
|
||||
image_list = []
|
||||
num_list = []
|
||||
for i, name in enumerate(name_list):
|
||||
img = BuildImage(
|
||||
temp_img.getsize(name)[0] + 50,
|
||||
30,
|
||||
font="msyh.ttf",
|
||||
font_size=20,
|
||||
color="#f9f6f2",
|
||||
)
|
||||
await img.atext((30, 5), f"{i + 1}.{name}")
|
||||
goods = [x for x in _props if x.goods_name == name][0]
|
||||
if goods.icon and (icon_path / goods.icon).exists():
|
||||
icon = BuildImage(30, 30, background=icon_path / goods.icon)
|
||||
await img.apaste(icon, alpha=True)
|
||||
image_list.append(img)
|
||||
num_list.append(
|
||||
BuildImage(
|
||||
30, 30, font_size=20, font="msyh.ttf", plain_text=f"×{props[name]}"
|
||||
)
|
||||
)
|
||||
max_w = 0
|
||||
num_max_w = 0
|
||||
h = 0
|
||||
for img, num in zip(image_list, num_list):
|
||||
h += img.h
|
||||
max_w = max_w if max_w > img.w else img.w
|
||||
num_max_w = num_max_w if num_max_w > num.w else num.w
|
||||
A = BuildImage(max_w + num_max_w + 30, h, color="#f9f6f2")
|
||||
curr_h = 0
|
||||
for img, num in zip(image_list, num_list):
|
||||
await A.apaste(img, (0, curr_h))
|
||||
await A.apaste(num, (max_w + 20, curr_h + 5), True)
|
||||
curr_h += img.h
|
||||
return A
|
||||
@@ -1,26 +0,0 @@
|
||||
from utils.utils import scheduler
|
||||
from models.bag_user import BagUser
|
||||
from services.log import logger
|
||||
|
||||
|
||||
__zx_plugin_name__ = "每日金币重置 [Hidden]"
|
||||
__plugin_version__ = 0.1
|
||||
__plugin_author__ = "HibiKier"
|
||||
|
||||
|
||||
# 重置每日金币
|
||||
@scheduler.scheduled_job(
|
||||
"cron",
|
||||
hour=0,
|
||||
minute=1,
|
||||
)
|
||||
async def _():
|
||||
try:
|
||||
user_list = await BagUser.get_all_users()
|
||||
for user in user_list:
|
||||
await user.update(
|
||||
get_today_gold=0,
|
||||
spend_today_gold=0,
|
||||
).apply()
|
||||
except Exception as e:
|
||||
logger.error(f"重置每日金币错误 e:{e}")
|
||||
@@ -1,15 +1,24 @@
|
||||
from .data_source import create_shop_help, delete_goods, update_goods, register_goods, parse_goods_info
|
||||
from nonebot.adapters.onebot.v11 import MessageEvent, Message
|
||||
from nonebot import on_command
|
||||
from configs.path_config import IMAGE_PATH
|
||||
from utils.message_builder import image
|
||||
from nonebot.permission import SUPERUSER
|
||||
from utils.utils import is_number
|
||||
from nonebot.params import CommandArg
|
||||
from nonebot.plugin import export
|
||||
from services.log import logger
|
||||
import os
|
||||
|
||||
from nonebot import on_command
|
||||
from nonebot.adapters.onebot.v11 import Message, MessageEvent
|
||||
from nonebot.params import CommandArg
|
||||
from nonebot.permission import SUPERUSER
|
||||
|
||||
from configs.path_config import IMAGE_PATH
|
||||
from models.bag_user import BagUser
|
||||
from services.log import logger
|
||||
from utils.message_builder import image
|
||||
from utils.utils import is_number, scheduler
|
||||
|
||||
from .data_source import (
|
||||
GoodsInfo,
|
||||
create_shop_help,
|
||||
delete_goods,
|
||||
parse_goods_info,
|
||||
register_goods,
|
||||
update_goods,
|
||||
)
|
||||
|
||||
__zx_plugin_name__ = "商店"
|
||||
__plugin_usage__ = """
|
||||
@@ -38,7 +47,7 @@ __plugin_cmd__ = [
|
||||
"删除商品 [名称或序号] [_superuser]",
|
||||
"修改商品 name:[名称或序号] price:[价格] des:[描述] discount:[折扣] limit_time:[限时] [_superuser]",
|
||||
]
|
||||
__plugin_type__ = ('商店',)
|
||||
__plugin_type__ = ("商店",)
|
||||
__plugin_version__ = 0.1
|
||||
__plugin_author__ = "HibiKier"
|
||||
__plugin_settings__ = {
|
||||
@@ -47,15 +56,8 @@ __plugin_settings__ = {
|
||||
"limit_superuser": False,
|
||||
"cmd": ["商店"],
|
||||
}
|
||||
__plugin_block_limit__ = {
|
||||
"limit_type": "group"
|
||||
}
|
||||
__plugin_block_limit__ = {"limit_type": "group"}
|
||||
|
||||
# 导出方法供其他插件使用
|
||||
export = export()
|
||||
export.register_goods = register_goods
|
||||
export.delete_goods = delete_goods
|
||||
export.update_goods = update_goods
|
||||
|
||||
shop_help = on_command("商店", priority=5, block=True)
|
||||
|
||||
@@ -81,12 +83,15 @@ async def _(event: MessageEvent, arg: Message = CommandArg()):
|
||||
if not data.get("name") or not data.get("price") or not data.get("des"):
|
||||
await shop_add_goods.finish("name:price:des 参数不可缺少!")
|
||||
if await register_goods(**data):
|
||||
await shop_add_goods.send(f"添加商品 {data['name']} 成功!\n"
|
||||
f"名称:{data['name']}\n"
|
||||
f"价格:{data['price']}金币\n"
|
||||
f"简介:{data['des']}\n"
|
||||
f"折扣:{data.get('discount')}\n"
|
||||
f"限时:{data.get('limit_time')}", at_sender=True)
|
||||
await shop_add_goods.send(
|
||||
f"添加商品 {data['name']} 成功!\n"
|
||||
f"名称:{data['name']}\n"
|
||||
f"价格:{data['price']}金币\n"
|
||||
f"简介:{data['des']}\n"
|
||||
f"折扣:{data.get('discount')}\n"
|
||||
f"限时:{data.get('limit_time')}",
|
||||
at_sender=True,
|
||||
)
|
||||
logger.info(f"USER {event.user_id} 添加商品 {msg} 成功")
|
||||
else:
|
||||
await shop_add_goods.send(f"添加商品 {msg} 失败了...", at_sender=True)
|
||||
@@ -128,6 +133,30 @@ async def _(event: MessageEvent, arg: Message = CommandArg()):
|
||||
await shop_update_goods.send(f"修改商品 {name} 成功了...\n{text}", at_sender=True)
|
||||
logger.info(f"USER {event.user_id} 修改商品 {name} 数据 {text} 成功")
|
||||
else:
|
||||
await shop_update_goods.send(f"修改商品 {name} 失败了...", at_sender=True)
|
||||
await shop_update_goods.send(name, at_sender=True)
|
||||
logger.info(f"USER {event.user_id} 修改商品 {name} 数据 {text} 失败")
|
||||
|
||||
|
||||
@scheduler.scheduled_job(
|
||||
"cron",
|
||||
hour=0,
|
||||
minute=0,
|
||||
)
|
||||
async def _():
|
||||
try:
|
||||
await GoodsInfo.all().update(daily_purchase_limit={})
|
||||
logger.info("商品每日限购次数重置成功...")
|
||||
except Exception as e:
|
||||
logger.error(f"商品每日限购次数重置出错 {type(e)}:{e}")
|
||||
|
||||
|
||||
@scheduler.scheduled_job(
|
||||
"cron",
|
||||
hour=0,
|
||||
minute=1,
|
||||
)
|
||||
async def _():
|
||||
try:
|
||||
await BagUser.all().update(get_today_gold=0, spend_today_gold=0)
|
||||
except Exception as e:
|
||||
logger.error(f"重置每日金币", "定时任务", e=e)
|
||||
|
||||
@@ -1,43 +1,18 @@
|
||||
from models.goods_info import GoodsInfo
|
||||
from utils.image_utils import BuildImage
|
||||
from models.sign_group_user import SignGroupUser
|
||||
from utils.utils import is_number
|
||||
from configs.path_config import IMAGE_PATH
|
||||
from typing import Optional, Union
|
||||
from configs.config import Config
|
||||
from nonebot import Driver
|
||||
from nonebot.plugin import require
|
||||
import nonebot
|
||||
import time
|
||||
from typing import Optional, Tuple, Union
|
||||
|
||||
driver: Driver = nonebot.get_driver()
|
||||
from PIL import Image
|
||||
|
||||
use = require("use")
|
||||
from configs.path_config import IMAGE_PATH
|
||||
from models.goods_info import GoodsInfo
|
||||
from utils.image_utils import BuildImage, text2image
|
||||
from utils.utils import GDict, is_number
|
||||
|
||||
icon_path = IMAGE_PATH / "shop_icon"
|
||||
|
||||
|
||||
@driver.on_startup
|
||||
async def init_default_shop_goods():
|
||||
"""
|
||||
导入内置的三个商品
|
||||
"""
|
||||
async def sign_card(**kwargs):
|
||||
user_id = kwargs['user_id']
|
||||
group_id = kwargs['group_id']
|
||||
prob = kwargs["prob"]
|
||||
user = await SignGroupUser.ensure(user_id, group_id)
|
||||
await user.update(add_probability=prob).apply()
|
||||
|
||||
if Config.get_config("shop", "IMPORT_DEFAULT_SHOP_GOODS"):
|
||||
await register_goods(
|
||||
"好感度双倍加持卡Ⅰ", 30, "下次签到双倍好感度概率 + 10%(谁才是真命天子?)(同类商品将覆盖)"
|
||||
)
|
||||
use.register_use("好感度双倍加持卡Ⅰ", sign_card, **{"prob": 0.1})
|
||||
await register_goods("好感度双倍加持卡Ⅱ", 150, "下次签到双倍好感度概率 + 20%(平平庸庸)(同类商品将覆盖)")
|
||||
use.register_use("好感度双倍加持卡Ⅱ", sign_card, **{"prob": 0.2})
|
||||
await register_goods(
|
||||
"好感度双倍加持卡Ⅲ", 250, "下次签到双倍好感度概率 + 30%(金币才是真命天子!)(同类商品将覆盖)"
|
||||
)
|
||||
use.register_use("好感度双倍加持卡Ⅲ", sign_card, **{"prob": 0.3})
|
||||
GDict["run_sql"].append("ALTER TABLE goods_info ADD is_passive boolean DEFAULT False;")
|
||||
GDict["run_sql"].append("ALTER TABLE goods_info ADD icon VARCHAR(255);")
|
||||
|
||||
|
||||
# 创建商店界面
|
||||
@@ -47,91 +22,230 @@ async def create_shop_help() -> str:
|
||||
:return: 图片base64
|
||||
"""
|
||||
goods_lst = await GoodsInfo.get_all_goods()
|
||||
idx = 1
|
||||
_dc = {}
|
||||
font_h = BuildImage(0, 0).getsize("正")[1]
|
||||
h = 10
|
||||
_list = []
|
||||
for goods in goods_lst:
|
||||
if goods.goods_limit_time == 0 or time.time() < goods.goods_limit_time:
|
||||
h += len(goods.goods_description.strip().split("\n")) * font_h + 80
|
||||
_list.append(goods)
|
||||
A = BuildImage(1000, h, color="#f9f6f2")
|
||||
current_h = 0
|
||||
for goods in _list:
|
||||
bk = BuildImage(
|
||||
700, 80, font_size=15, color="#f9f6f2", font="CJGaoDeGuo.otf"
|
||||
)
|
||||
goods_image = BuildImage(
|
||||
600, 80, font_size=20, color="#a29ad6", font="CJGaoDeGuo.otf"
|
||||
)
|
||||
# A = BuildImage(1100, h, color="#f9f6f2")
|
||||
total_n = 0
|
||||
image_list = []
|
||||
for idx, goods in enumerate(_list):
|
||||
name_image = BuildImage(
|
||||
580, 40, font_size=25, color="#e67b6b", font="CJGaoDeGuo.otf"
|
||||
)
|
||||
await name_image.atext(
|
||||
(15, 0), f"{idx}.{goods.goods_name}", center_type="by_height"
|
||||
(15, 0), f"{idx + 1}.{goods.goods_name}", center_type="by_height"
|
||||
)
|
||||
await name_image.aline((380, -5, 280, 45), "#a29ad6", 5)
|
||||
await name_image.atext((390, 0), "售价:", center_type="by_height")
|
||||
await name_image.atext(
|
||||
(440, 0), str(goods.goods_price), (255, 255, 255), center_type="by_height"
|
||||
)
|
||||
if goods.goods_discount != 1:
|
||||
discount_price = int(goods.goods_discount * goods.goods_price)
|
||||
old_price_image = BuildImage(
|
||||
0,
|
||||
0,
|
||||
plain_text=str(goods.goods_price),
|
||||
font_color=(194, 194, 194),
|
||||
font="CJGaoDeGuo.otf",
|
||||
font_size=15,
|
||||
)
|
||||
await old_price_image.aline(
|
||||
(
|
||||
0,
|
||||
int(old_price_image.h / 2),
|
||||
old_price_image.w + 1,
|
||||
int(old_price_image.h / 2),
|
||||
),
|
||||
(0, 0, 0),
|
||||
)
|
||||
await name_image.apaste(old_price_image, (440, 0), True)
|
||||
await name_image.atext((440, 15), str(discount_price), (255, 255, 255))
|
||||
else:
|
||||
await name_image.atext(
|
||||
(440, 0),
|
||||
str(goods.goods_price),
|
||||
(255, 255, 255),
|
||||
center_type="by_height",
|
||||
)
|
||||
await name_image.atext(
|
||||
(
|
||||
440
|
||||
+ BuildImage(0, 0, plain_text=str(goods.goods_price), font_size=25).w,
|
||||
0,
|
||||
),
|
||||
" 金币",
|
||||
f" 金币",
|
||||
center_type="by_height",
|
||||
)
|
||||
des_image = None
|
||||
font_img = BuildImage(
|
||||
600, 80, font_size=20, color="#a29ad6", font="CJGaoDeGuo.otf"
|
||||
)
|
||||
p = font_img.getsize("简介:")[0] + 20
|
||||
if goods.goods_description:
|
||||
des_list = goods.goods_description.split("\n")
|
||||
desc = ""
|
||||
for des in des_list:
|
||||
if font_img.getsize(des)[0] > font_img.w - p - 20:
|
||||
msg = ""
|
||||
tmp = ""
|
||||
for i in range(len(des)):
|
||||
if font_img.getsize(tmp)[0] < font_img.w - p - 20:
|
||||
tmp += des[i]
|
||||
else:
|
||||
msg += tmp + "\n"
|
||||
tmp = des[i]
|
||||
desc += msg
|
||||
if tmp:
|
||||
desc += tmp
|
||||
else:
|
||||
desc += des + "\n"
|
||||
if desc[-1] == "\n":
|
||||
desc = desc[:-1]
|
||||
des_image = await text2image(desc, color="#a29ad6")
|
||||
goods_image = BuildImage(
|
||||
600,
|
||||
(50 + des_image.h) if des_image else 50,
|
||||
font_size=20,
|
||||
color="#a29ad6",
|
||||
font="CJGaoDeGuo.otf",
|
||||
)
|
||||
if des_image:
|
||||
await goods_image.atext((15, 50), "简介:")
|
||||
await goods_image.apaste(des_image, (p, 50))
|
||||
await name_image.acircle_corner(5)
|
||||
await goods_image.apaste(name_image, (0, 5), True, center_type="by_width")
|
||||
await goods_image.atext((15, 50), f"简介:{goods.goods_description}")
|
||||
await goods_image.acircle_corner(20)
|
||||
await bk.apaste(goods_image, alpha=True)
|
||||
bk = BuildImage(
|
||||
1180,
|
||||
(50 + des_image.h) if des_image else 50,
|
||||
font_size=15,
|
||||
color="#f9f6f2",
|
||||
font="CJGaoDeGuo.otf",
|
||||
)
|
||||
if goods.icon and (icon_path / goods.icon).exists():
|
||||
icon = BuildImage(70, 70, background=icon_path / goods.icon)
|
||||
await bk.apaste(icon)
|
||||
await bk.apaste(goods_image, (70, 0), alpha=True)
|
||||
n = 0
|
||||
_w = 650
|
||||
# 添加限时图标和时间
|
||||
if goods.goods_limit_time > 0:
|
||||
_limit_time_logo = BuildImage(40, 40, background=f"{IMAGE_PATH}/other/time.png")
|
||||
await bk.apaste(_limit_time_logo, (600, 0), True)
|
||||
await bk.apaste(BuildImage(0, 0, plain_text="限时!", font_size=23, font="CJGaoDeGuo.otf"), (640, 10), True)
|
||||
limit_time = time.strftime("%Y-%m-%d %H:%M", time.localtime(goods.goods_limit_time)).split()
|
||||
n += 140
|
||||
_limit_time_logo = BuildImage(
|
||||
40, 40, background=f"{IMAGE_PATH}/other/time.png"
|
||||
)
|
||||
await bk.apaste(_limit_time_logo, (_w + 50, 0), True)
|
||||
await bk.apaste(
|
||||
BuildImage(0, 0, plain_text="限时!", font_size=23, font="CJGaoDeGuo.otf"),
|
||||
(_w + 90, 10),
|
||||
True,
|
||||
)
|
||||
limit_time = time.strftime(
|
||||
"%Y-%m-%d %H:%M", time.localtime(goods.goods_limit_time)
|
||||
).split()
|
||||
y_m_d = limit_time[0]
|
||||
_h_m = limit_time[1].split(":")
|
||||
h_m = _h_m[0] + "时 " + _h_m[1] + "分"
|
||||
await bk.atext((605, 38), str(y_m_d))
|
||||
await bk.atext((615, 57), str(h_m))
|
||||
await bk.aline((550, -1, 710, -1), "#a29ad6", 5)
|
||||
await bk.aline((550, 80, 710, 80), "#a29ad6", 5)
|
||||
idx += 1
|
||||
await A.apaste(bk, (0, current_h), True)
|
||||
current_h += 90
|
||||
w = 1000
|
||||
await bk.atext((_w + 55, 38), str(y_m_d))
|
||||
await bk.atext((_w + 65, 57), str(h_m))
|
||||
_w += 140
|
||||
if goods.goods_discount != 1:
|
||||
n += 140
|
||||
_discount_logo = BuildImage(
|
||||
30, 30, background=f"{IMAGE_PATH}/other/discount.png"
|
||||
)
|
||||
await bk.apaste(_discount_logo, (_w + 50, 10), True)
|
||||
await bk.apaste(
|
||||
BuildImage(0, 0, plain_text="折扣!", font_size=23, font="CJGaoDeGuo.otf"),
|
||||
(_w + 90, 15),
|
||||
True,
|
||||
)
|
||||
await bk.apaste(
|
||||
BuildImage(
|
||||
0,
|
||||
0,
|
||||
plain_text=f"{10 * goods.goods_discount:.1f} 折",
|
||||
font_size=30,
|
||||
font="CJGaoDeGuo.otf",
|
||||
font_color=(85, 156, 75),
|
||||
),
|
||||
(_w + 50, 44),
|
||||
True,
|
||||
)
|
||||
_w += 140
|
||||
if goods.daily_limit != 0:
|
||||
n += 140
|
||||
_daily_limit_logo = BuildImage(
|
||||
35, 35, background=f"{IMAGE_PATH}/other/daily_limit.png"
|
||||
)
|
||||
await bk.apaste(_daily_limit_logo, (_w + 50, 10), True)
|
||||
await bk.apaste(
|
||||
BuildImage(0, 0, plain_text="限购!", font_size=23, font="CJGaoDeGuo.otf"),
|
||||
(_w + 90, 20),
|
||||
True,
|
||||
)
|
||||
await bk.apaste(
|
||||
BuildImage(
|
||||
0,
|
||||
0,
|
||||
plain_text=f"{goods.daily_limit}",
|
||||
font_size=30,
|
||||
font="CJGaoDeGuo.otf",
|
||||
),
|
||||
(_w + 72, 45),
|
||||
True,
|
||||
)
|
||||
if total_n < n:
|
||||
total_n = n
|
||||
if n:
|
||||
await bk.aline((650, -1, 650 + n, -1), "#a29ad6", 5)
|
||||
# await bk.aline((650, 80, 650 + n, 80), "#a29ad6", 5)
|
||||
|
||||
# 添加限时图标和时间
|
||||
image_list.append(bk)
|
||||
# await A.apaste(bk, (0, current_h), True)
|
||||
# current_h += 90
|
||||
h = 0
|
||||
current_h = 0
|
||||
for img in image_list:
|
||||
h += img.h + 10
|
||||
A = BuildImage(1100, h, color="#f9f6f2")
|
||||
for img in image_list:
|
||||
await A.apaste(img, (0, current_h), True)
|
||||
current_h += img.h + 10
|
||||
w = 950
|
||||
if total_n:
|
||||
w += total_n
|
||||
h = A.h + 230 + 100
|
||||
h = 1000 if h < 1000 else h
|
||||
shop_logo = BuildImage(100, 100, background=f"{IMAGE_PATH}/other/shop_text.png")
|
||||
shop = BuildImage(w, h, font_size=20, color="#f9f6f2")
|
||||
shop.paste(A, (20, 230))
|
||||
zx_img = BuildImage(0, 0, background=f"{IMAGE_PATH}/zhenxun/toukan.png")
|
||||
zx_img = BuildImage(0, 0, background=f"{IMAGE_PATH}/zhenxun/toukan_3.png")
|
||||
zx_img.transpose(Image.FLIP_LEFT_RIGHT)
|
||||
zx_img.replace_color_tran(((240, 240, 240), (255, 255, 255)), (249, 246, 242))
|
||||
await shop.apaste(zx_img, (780, 100))
|
||||
await shop.apaste(zx_img, (0, 100))
|
||||
shop.paste(A, (20 + zx_img.w, 230))
|
||||
await shop.apaste(shop_logo, (450, 30), True)
|
||||
shop.text(
|
||||
(int((1000 - shop.getsize("注【通过 序号 或者 商品名称 购买】")[0]) / 2), 170),
|
||||
"注【通过 序号 或者 商品名称 购买】",
|
||||
)
|
||||
shop.text((20, h - 100), "神秘药水\t\t售价:9999999金币\n\t\t鬼知道会有什么效果~")
|
||||
shop.text((20 + zx_img.w, h - 100), "神秘药水\t\t售价:9999999金币\n\t\t鬼知道会有什么效果~")
|
||||
return shop.pic2bs4()
|
||||
|
||||
|
||||
async def register_goods(
|
||||
name: str,
|
||||
price: int,
|
||||
des: str,
|
||||
discount: Optional[float] = 1,
|
||||
limit_time: Optional[int] = 0,
|
||||
**kwargs,
|
||||
):
|
||||
name: str,
|
||||
price: int,
|
||||
des: str,
|
||||
discount: Optional[float] = 1,
|
||||
limit_time: Optional[int] = 0,
|
||||
daily_limit: Optional[int] = 0,
|
||||
is_passive: Optional[bool] = False,
|
||||
icon: Optional[str] = None,
|
||||
) -> bool:
|
||||
"""
|
||||
添加商品
|
||||
例如: 折扣:可选参数↓ 限时时间:可选,单位为小时
|
||||
@@ -142,25 +256,35 @@ async def register_goods(
|
||||
:param des: 商品简介
|
||||
:param discount: 商品折扣
|
||||
:param limit_time: 商品限时销售时间,单位为小时
|
||||
:param kwargs: kwargs
|
||||
:param daily_limit: 每日购买次数限制
|
||||
:param is_passive: 是否为被动
|
||||
:param icon: 图标
|
||||
:return: 是否添加成功
|
||||
"""
|
||||
if kwargs:
|
||||
name = kwargs.get("name")
|
||||
price = kwargs.get("price")
|
||||
des = kwargs.get("des")
|
||||
discount = kwargs.get("discount")
|
||||
limit_time = kwargs.get("time_limit")
|
||||
limit_time = float(limit_time) if limit_time else limit_time
|
||||
discount = discount if discount is None else 1
|
||||
limit_time = int(time.time() + limit_time * 60 * 60) if limit_time is not None and limit_time != 0 else 0
|
||||
return await GoodsInfo.add_goods(
|
||||
name, int(price), des, float(discount), limit_time
|
||||
)
|
||||
if not await GoodsInfo.get_or_none(goods_name=name):
|
||||
limit_time_ = float(limit_time) if limit_time else limit_time
|
||||
discount = discount if discount is not None else 1
|
||||
limit_time_ = (
|
||||
int(time.time() + limit_time_ * 60 * 60)
|
||||
if limit_time_ is not None and limit_time_ != 0
|
||||
else 0
|
||||
)
|
||||
await GoodsInfo.create(
|
||||
goods_name=name,
|
||||
goods_price=int(price),
|
||||
goods_description=des,
|
||||
goods_discount=float(discount),
|
||||
goods_limit_time=limit_time_,
|
||||
daily_limit=daily_limit,
|
||||
is_passive=is_passive,
|
||||
icon=icon,
|
||||
)
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
# 删除商品
|
||||
async def delete_goods(name: str, id_: int) -> "str, str, int":
|
||||
async def delete_goods(name: str, id_: int) -> Tuple[str, str, int]:
|
||||
"""
|
||||
删除商品
|
||||
:param name: 商品名称
|
||||
@@ -181,10 +305,11 @@ async def delete_goods(name: str, id_: int) -> "str, str, int":
|
||||
return f"删除商品 {name} 成功!", name, 200
|
||||
else:
|
||||
return f"删除商品 {name} 失败!", name, 999
|
||||
return "获取商品失败", "", 999
|
||||
|
||||
|
||||
# 更新商品信息
|
||||
async def update_goods(**kwargs) -> "str, str, int":
|
||||
async def update_goods(**kwargs) -> Tuple[bool, str, str]:
|
||||
"""
|
||||
更新商品信息
|
||||
:param kwargs: kwargs
|
||||
@@ -194,17 +319,19 @@ async def update_goods(**kwargs) -> "str, str, int":
|
||||
goods_lst = await GoodsInfo.get_all_goods()
|
||||
if is_number(kwargs["name"]):
|
||||
if int(kwargs["name"]) < 1 or int(kwargs["name"]) > len(goods_lst):
|
||||
return "序号错误,没有该序号的商品...", "", 999
|
||||
return False, "序号错误,没有该序号的商品...", ""
|
||||
goods = goods_lst[int(kwargs["name"]) - 1]
|
||||
else:
|
||||
goods = await GoodsInfo.get_goods_info(kwargs["name"])
|
||||
goods = await GoodsInfo.filter(goods_name=kwargs["name"]).first()
|
||||
if not goods:
|
||||
return "名称错误,没有该名称的商品...", "", 999
|
||||
name = goods.goods_name
|
||||
return False, "名称错误,没有该名称的商品...", ""
|
||||
name: str = goods.goods_name
|
||||
price = goods.goods_price
|
||||
des = goods.goods_description
|
||||
discount = goods.goods_discount
|
||||
limit_time = goods.goods_limit_time
|
||||
daily_limit = goods.daily_limit
|
||||
is_passive = goods.is_passive
|
||||
new_time = 0
|
||||
tmp = ""
|
||||
if kwargs.get("price"):
|
||||
@@ -218,20 +345,41 @@ async def update_goods(**kwargs) -> "str, str, int":
|
||||
discount = kwargs["discount"]
|
||||
if kwargs.get("limit_time"):
|
||||
kwargs["limit_time"] = float(kwargs["limit_time"])
|
||||
new_time = time.strftime(
|
||||
"%Y-%m-%d %H:%M:%S",
|
||||
time.localtime(time.time() + kwargs["limit_time"] * 60 * 60),
|
||||
new_time = (
|
||||
time.strftime(
|
||||
"%Y-%m-%d %H:%M:%S",
|
||||
time.localtime(time.time() + kwargs["limit_time"] * 60 * 60),
|
||||
)
|
||||
if kwargs["limit_time"] != 0
|
||||
else 0
|
||||
)
|
||||
tmp += f"限时至: {new_time}\n"
|
||||
tmp += f"限时至: {new_time}\n" if new_time else "取消了限时\n"
|
||||
limit_time = kwargs["limit_time"]
|
||||
return (
|
||||
await GoodsInfo.update_goods(
|
||||
name,
|
||||
int(price),
|
||||
des,
|
||||
float(discount),
|
||||
int(time.time() + limit_time * 60 * 60 if limit_time != 0 and new_time else 0),
|
||||
if kwargs.get("daily_limit"):
|
||||
tmp += (
|
||||
f'每日购买限制:{daily_limit} --> {kwargs["daily_limit"]}\n'
|
||||
if daily_limit
|
||||
else "取消了购买限制\n"
|
||||
)
|
||||
daily_limit = int(kwargs["daily_limit"])
|
||||
if kwargs.get("is_passive"):
|
||||
tmp += f'被动道具:{is_passive} --> {kwargs["is_passive"]}\n'
|
||||
des = kwargs["is_passive"]
|
||||
await GoodsInfo.update_goods(
|
||||
name,
|
||||
int(price),
|
||||
des,
|
||||
float(discount),
|
||||
int(
|
||||
time.time() + limit_time * 60 * 60
|
||||
if limit_time != 0 and new_time
|
||||
else 0
|
||||
),
|
||||
daily_limit,
|
||||
is_passive,
|
||||
)
|
||||
return (
|
||||
True,
|
||||
name,
|
||||
tmp[:-1],
|
||||
)
|
||||
@@ -264,6 +412,10 @@ def parse_goods_info(msg: str) -> Union[dict, str]:
|
||||
data["discount"] = sp[1]
|
||||
elif sp[0] == "limit_time":
|
||||
if not is_number(sp[1]) or float(sp[1]) < 0:
|
||||
return "limit_time参数不合法,必须大于0!"
|
||||
return "limit_time参数不合法,必须为数字且大于0!"
|
||||
data["limit_time"] = sp[1]
|
||||
elif sp[0] == "daily_limit":
|
||||
if not is_number(sp[1]) or float(sp[1]) < 0:
|
||||
return "daily_limit参数不合法,必须为数字且大于0!"
|
||||
data["daily_limit"] = sp[1]
|
||||
return data
|
||||
|
||||
@@ -1,26 +1,33 @@
|
||||
from nonebot import on_command
|
||||
from services.log import logger
|
||||
from nonebot.adapters.onebot.v11 import Bot, GroupMessageEvent, Message
|
||||
from nonebot.params import CommandArg
|
||||
from utils.utils import is_number
|
||||
from models.bag_user import BagUser
|
||||
from nonebot.adapters.onebot.v11.permission import GROUP
|
||||
from services.db_context import db
|
||||
from nonebot.plugin import export
|
||||
from .data_source import effect, register_use, func_manager
|
||||
from typing import Any, Tuple
|
||||
|
||||
from nonebot import on_command, on_regex
|
||||
from nonebot.adapters.onebot.v11 import Bot, GroupMessageEvent, Message
|
||||
from nonebot.adapters.onebot.v11.permission import GROUP
|
||||
from nonebot.params import CommandArg, RegexGroup
|
||||
|
||||
from models.bag_user import BagUser
|
||||
from models.user_shop_gold_log import UserShopGoldLog
|
||||
from services.log import logger
|
||||
from utils.decorator.shop import NotMeetUseConditionsException
|
||||
from utils.utils import is_number
|
||||
|
||||
from .data_source import build_params, effect, func_manager, register_use
|
||||
|
||||
__zx_plugin_name__ = "商店 - 使用道具"
|
||||
__plugin_usage__ = """
|
||||
usage:
|
||||
普通的使用道具
|
||||
指令:
|
||||
使用道具 [序号或道具名称] ?[数量]=1
|
||||
使用道具 [序号或道具名称] ?[数量]=1 ?[其他信息]
|
||||
示例:使用道具好感度双倍加持卡 使用道具好感度双倍加持卡
|
||||
示例:使用道具1 使用第一个道具
|
||||
示例:使用道具1 10 使用10个第一个道具
|
||||
示例:使用道具1 1 来点色图 使用第一个道具并附带信息
|
||||
* 序号以 ”我的道具“ 为准 *
|
||||
""".strip()
|
||||
__plugin_des__ = "商店 - 使用道具"
|
||||
__plugin_cmd__ = ["使用道具 [序号或道具名称]"]
|
||||
__plugin_type__ = ('商店',)
|
||||
__plugin_type__ = ("商店",)
|
||||
__plugin_version__ = 0.1
|
||||
__plugin_author__ = "HibiKier"
|
||||
__plugin_settings__ = {
|
||||
@@ -30,52 +37,81 @@ __plugin_settings__ = {
|
||||
"cmd": ["商店", "使用道具"],
|
||||
}
|
||||
|
||||
# 导出方法供其他插件使用
|
||||
export = export()
|
||||
export.register_use = register_use
|
||||
|
||||
use_props = on_command("使用道具", priority=5, block=True, permission=GROUP)
|
||||
use_props = on_command(r"使用道具", priority=5, block=True, permission=GROUP)
|
||||
|
||||
|
||||
@use_props.handle()
|
||||
async def _(bot: Bot, event: GroupMessageEvent, arg: Message = CommandArg()):
|
||||
msg = arg.extract_plain_text().strip()
|
||||
msg = arg.extract_plain_text()
|
||||
num = 1
|
||||
msg_sp = msg.split()
|
||||
if len(msg_sp) > 1 and is_number(msg_sp[-1]) and int(msg_sp[-1]) > 0:
|
||||
num = int(msg.split()[-1])
|
||||
msg = " ".join(msg.split()[:-1])
|
||||
property_ = await BagUser.get_property(event.user_id, event.group_id)
|
||||
text = ""
|
||||
prop_n = None
|
||||
index = None
|
||||
split = msg.split()
|
||||
if size := len(split):
|
||||
if size == 1:
|
||||
prop_n = split[0].strip()
|
||||
index = 1
|
||||
if size > 1 and is_number(split[1].strip()):
|
||||
prop_n = split[0].strip()
|
||||
num = int(split[1].strip())
|
||||
index = 2
|
||||
else:
|
||||
await use_props.finish("缺少参数,请查看帮助", at_sender=True)
|
||||
if index:
|
||||
text = " ".join(split[index:])
|
||||
property_ = await BagUser.get_property(event.user_id, event.group_id, True)
|
||||
if property_:
|
||||
async with db.transaction():
|
||||
if is_number(msg):
|
||||
if 0 < int(msg) <= len(property_):
|
||||
name = list(property_.keys())[int(msg) - 1]
|
||||
else:
|
||||
await use_props.finish("仔细看看自己的道具仓库有没有这个道具?", at_sender=True)
|
||||
name = None
|
||||
if prop_n and is_number(prop_n):
|
||||
if 0 < int(prop_n) <= len(property_):
|
||||
name = list(property_.keys())[int(prop_n) - 1]
|
||||
else:
|
||||
if msg not in property_.keys():
|
||||
await use_props.finish("道具名称错误!", at_sender=True)
|
||||
name = msg
|
||||
_user_prop_count = property_[name]
|
||||
if num > _user_prop_count:
|
||||
await use_props.finish(f"道具数量不足,无法使用{num}次!")
|
||||
if num > (n := func_manager.get_max_num_limit(name)):
|
||||
await use_props.finish(f"该道具单次只能使用 {n} 个!")
|
||||
if await BagUser.delete_property(
|
||||
event.user_id, event.group_id, name, num
|
||||
):
|
||||
await use_props.finish("仔细看看自己的道具仓库有没有这个道具?", at_sender=True)
|
||||
else:
|
||||
if prop_n not in property_.keys():
|
||||
await use_props.finish("道具名称错误!", at_sender=True)
|
||||
name = prop_n
|
||||
if not name:
|
||||
await use_props.finish("未获取到道具名称", at_sender=True)
|
||||
_user_prop_count = property_[name]
|
||||
if num > _user_prop_count:
|
||||
await use_props.finish(f"道具数量不足,无法使用{num}次!")
|
||||
if num > (n := func_manager.get_max_num_limit(name)):
|
||||
await use_props.finish(f"该道具单次只能使用 {n} 个!")
|
||||
try:
|
||||
model, kwargs = build_params(bot, event, name, num, text)
|
||||
except KeyError:
|
||||
logger.warning(f"{name} 未注册使用函数")
|
||||
await use_props.finish(f"{name} 未注册使用方法")
|
||||
else:
|
||||
try:
|
||||
await func_manager.run_handle(
|
||||
type_="before_handle", param=model, **kwargs
|
||||
)
|
||||
except NotMeetUseConditionsException as e:
|
||||
await use_props.finish(e.get_info(), at_sender=True)
|
||||
if await BagUser.delete_property(event.user_id, event.group_id, name, num):
|
||||
if func_manager.check_send_success_message(name):
|
||||
await use_props.send(f"使用道具 {name} {num} 次成功!", at_sender=True)
|
||||
if msg := await effect(bot, event, name, num):
|
||||
if msg := await effect(bot, event, name, num, text, event.message):
|
||||
await use_props.send(msg, at_sender=True)
|
||||
logger.info(
|
||||
f"USER {event.user_id} GROUP {event.group_id} 使用道具 {name} {num} 次成功"
|
||||
)
|
||||
await UserShopGoldLog.create(
|
||||
user_qq=event.user_id,
|
||||
group_id=event.group_id,
|
||||
type=1,
|
||||
name=name,
|
||||
num=num,
|
||||
)
|
||||
else:
|
||||
await use_props.send(f"使用道具 {name} {num} 次失败!", at_sender=True)
|
||||
logger.info(
|
||||
f"USER {event.user_id} GROUP {event.group_id} 使用道具 {name} {num} 次失败"
|
||||
)
|
||||
await func_manager.run_handle(type_="after_handle", param=model, **kwargs)
|
||||
else:
|
||||
await use_props.send("您的背包里没有任何的道具噢", at_sender=True)
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
from nonebot.adapters.onebot.v11 import GroupMessageEvent, MessageSegment
|
||||
from nonebot.adapters.onebot.v11 import GroupMessageEvent, MessageSegment, Message
|
||||
from services.log import logger
|
||||
from nonebot.adapters.onebot.v11 import Bot
|
||||
from typing import Optional, Union
|
||||
from pydantic import create_model
|
||||
from utils.models import ShopParam
|
||||
from typing import Optional, Union, Callable, List, Tuple, Dict, Any
|
||||
from types import MappingProxyType
|
||||
import inspect
|
||||
import asyncio
|
||||
|
||||
|
||||
@@ -9,6 +13,30 @@ class GoodsUseFuncManager:
|
||||
def __init__(self):
|
||||
self._data = {}
|
||||
|
||||
def register_use_before_handle(self, goods_name: str, fun_list: List[Callable]):
|
||||
"""
|
||||
说明:
|
||||
注册商品使用前函数
|
||||
参数:
|
||||
:param goods_name: 商品名称
|
||||
:param fun_list: 函数列表
|
||||
"""
|
||||
if fun_list:
|
||||
self._data[goods_name]["before_handle"] = fun_list
|
||||
logger.info(f"register_use_before_handle 成功注册商品:{goods_name} 的{len(fun_list)}个使用前函数")
|
||||
|
||||
def register_use_after_handle(self, goods_name: str, fun_list: List[Callable]):
|
||||
"""
|
||||
说明:
|
||||
注册商品使用后函数
|
||||
参数:
|
||||
:param goods_name: 商品名称
|
||||
:param fun_list: 函数列表
|
||||
"""
|
||||
if fun_list:
|
||||
self._data[goods_name]["after_handle"] = fun_list
|
||||
logger.info(f"register_use_after_handle 成功注册商品:{goods_name} 的{len(fun_list)}个使用后函数")
|
||||
|
||||
def register_use(self, goods_name: str, **kwargs):
|
||||
"""
|
||||
注册商品使用方法
|
||||
@@ -30,24 +58,69 @@ class GoodsUseFuncManager:
|
||||
:param goods_name: 商品名称
|
||||
"""
|
||||
if self.exists(goods_name):
|
||||
return self._data[goods_name]["kwargs"]["_max_num_limit"]
|
||||
return self._data[goods_name]["kwargs"]["max_num_limit"]
|
||||
return 1
|
||||
|
||||
async def use(self, **kwargs) -> Optional[Union[str, MessageSegment]]:
|
||||
def _parse_args(self, args_: MappingProxyType, param: ShopParam, **kwargs):
|
||||
param_list_ = []
|
||||
_bot = param.bot
|
||||
param.bot = None
|
||||
param_json = param.dict()
|
||||
param_json["bot"] = _bot
|
||||
for par in args_.keys():
|
||||
if par in ["shop_param"]:
|
||||
param_list_.append(param)
|
||||
elif par not in ["args", "kwargs"]:
|
||||
param_list_.append(param_json.get(par))
|
||||
if kwargs.get(par) is not None:
|
||||
del kwargs[par]
|
||||
return param_list_
|
||||
|
||||
async def use(
|
||||
self, param: ShopParam, **kwargs
|
||||
) -> Optional[Union[str, MessageSegment]]:
|
||||
"""
|
||||
使用道具
|
||||
:param param: BaseModel
|
||||
:param kwargs: kwargs
|
||||
"""
|
||||
goods_name = kwargs.get("goods_name")
|
||||
goods_name = param.goods_name
|
||||
if self.exists(goods_name):
|
||||
if asyncio.iscoroutinefunction(self._data[goods_name]["func"]):
|
||||
return await self._data[goods_name]["func"](
|
||||
**kwargs,
|
||||
)
|
||||
# 使用方法
|
||||
args = inspect.signature(self._data[goods_name]["func"]).parameters
|
||||
if args and list(args.keys())[0] != "kwargs":
|
||||
if asyncio.iscoroutinefunction(self._data[goods_name]["func"]):
|
||||
return await self._data[goods_name]["func"](
|
||||
*self._parse_args(args, param, **kwargs)
|
||||
)
|
||||
else:
|
||||
return self._data[goods_name]["func"](
|
||||
*self._parse_args(args, param, **kwargs)
|
||||
)
|
||||
else:
|
||||
return self._data[goods_name]["func"](
|
||||
**kwargs,
|
||||
)
|
||||
if asyncio.iscoroutinefunction(self._data[goods_name]["func"]):
|
||||
return await self._data[goods_name]["func"](
|
||||
**kwargs,
|
||||
)
|
||||
else:
|
||||
return self._data[goods_name]["func"](
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
async def run_handle(self, goods_name: str, type_: str, param: ShopParam, **kwargs):
|
||||
if self._data.get(goods_name) and self._data[goods_name].get(type_):
|
||||
for func in self._data[goods_name].get(type_):
|
||||
args = inspect.signature(func).parameters
|
||||
if args and list(args.keys())[0] != "kwargs":
|
||||
if asyncio.iscoroutinefunction(func):
|
||||
await func(*self._parse_args(args, param, **kwargs))
|
||||
else:
|
||||
func(*self._parse_args(args, param, **kwargs))
|
||||
else:
|
||||
if asyncio.iscoroutinefunction(func):
|
||||
await func(**kwargs)
|
||||
else:
|
||||
func(**kwargs)
|
||||
|
||||
def check_send_success_message(self, goods_name: str) -> bool:
|
||||
"""
|
||||
@@ -67,12 +140,56 @@ class GoodsUseFuncManager:
|
||||
return self._data[goods_name]["kwargs"]
|
||||
return {}
|
||||
|
||||
def init_model(self, goods_name: str, bot: Bot, event: GroupMessageEvent, num: int, text: str):
|
||||
return self._data[goods_name]["model"](
|
||||
**{
|
||||
"goods_name": goods_name,
|
||||
"bot": bot,
|
||||
"event": event,
|
||||
"user_id": event.user_id,
|
||||
"group_id": event.group_id,
|
||||
"num": num,
|
||||
"message": event.message,
|
||||
"text": text
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
func_manager = GoodsUseFuncManager()
|
||||
|
||||
|
||||
def build_params(
|
||||
bot: Bot, event: GroupMessageEvent, goods_name: str, num: int, text: str
|
||||
) -> Tuple[ShopParam, Dict[str, Any]]:
|
||||
"""
|
||||
说明:
|
||||
构造参数
|
||||
参数:
|
||||
:param bot: bot
|
||||
:param event: event
|
||||
:param goods_name: 商品名称
|
||||
:param num: 数量
|
||||
:param text: 其他信息
|
||||
"""
|
||||
_kwargs = func_manager.get_kwargs(goods_name)
|
||||
return (
|
||||
func_manager.init_model(goods_name, bot, event, num, text),
|
||||
{
|
||||
**_kwargs,
|
||||
"_bot": bot,
|
||||
"event": event,
|
||||
"group_id": event.group_id,
|
||||
"user_id": event.user_id,
|
||||
"num": num,
|
||||
"text": text,
|
||||
"message": event.message,
|
||||
"goods_name": goods_name,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
async def effect(
|
||||
bot: Bot, event: GroupMessageEvent, goods_name: str, num: int
|
||||
bot: Bot, event: GroupMessageEvent, goods_name: str, num: int, text: str, message: Message
|
||||
) -> Optional[Union[str, MessageSegment]]:
|
||||
"""
|
||||
商品生效
|
||||
@@ -80,29 +197,22 @@ async def effect(
|
||||
:param event: GroupMessageEvent
|
||||
:param goods_name: 商品名称
|
||||
:param num: 使用数量
|
||||
:param text: 其他信息
|
||||
:param message: Message
|
||||
:return: 使用是否成功
|
||||
"""
|
||||
# 优先使用注册的商品插件
|
||||
try:
|
||||
if func_manager.exists(goods_name):
|
||||
_kwargs = func_manager.get_kwargs(goods_name)
|
||||
return await func_manager.use(
|
||||
**{
|
||||
**_kwargs,
|
||||
"_bot": bot,
|
||||
"event": event,
|
||||
"group_id": event.group_id,
|
||||
"user_id": event.user_id,
|
||||
"num": num,
|
||||
"goods_name": goods_name,
|
||||
}
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f"use 商品生效函数effect 发生错误 {type(e)}:{e}")
|
||||
# try:
|
||||
if func_manager.exists(goods_name):
|
||||
_kwargs = func_manager.get_kwargs(goods_name)
|
||||
model, kwargs = build_params(bot, event, goods_name, num, text)
|
||||
return await func_manager.use(model, **kwargs)
|
||||
# except Exception as e:
|
||||
# logger.error(f"use 商品生效函数effect 发生错误 {type(e)}:{e}")
|
||||
return None
|
||||
|
||||
|
||||
def register_use(goods_name: str, func, **kwargs):
|
||||
def register_use(goods_name: str, func: Callable, **kwargs):
|
||||
"""
|
||||
注册商品使用方法
|
||||
:param goods_name: 商品名称
|
||||
@@ -112,10 +222,14 @@ def register_use(goods_name: str, func, **kwargs):
|
||||
if func_manager.exists(goods_name):
|
||||
raise ValueError("该商品使用函数已被注册!")
|
||||
# 发送使用成功信息
|
||||
if kwargs.get("send_success_msg") is None:
|
||||
kwargs["send_success_msg"] = True
|
||||
kwargs["_max_num_limit"] = (
|
||||
kwargs.get("_max_num_limit") if kwargs.get("_max_num_limit") else 1
|
||||
kwargs["send_success_msg"] = kwargs.get("send_success_msg", True)
|
||||
kwargs["max_num_limit"] = kwargs.get("max_num_limit", 1)
|
||||
func_manager.register_use(
|
||||
goods_name,
|
||||
**{
|
||||
"func": func,
|
||||
"model": create_model(f"{goods_name}_model", __base__=ShopParam, **kwargs),
|
||||
"kwargs": kwargs,
|
||||
},
|
||||
)
|
||||
func_manager.register_use(goods_name, **{"func": func, "kwargs": kwargs})
|
||||
logger.info(f"register_use 成功注册商品:{goods_name} 的使用函数")
|
||||
|
||||
@@ -1,10 +1,5 @@
|
||||
from pathlib import Path
|
||||
|
||||
import nonebot
|
||||
|
||||
|
||||
nonebot.load_plugins('basic_plugins/super_cmd')
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
nonebot.load_plugins(str(Path(__file__).parent.resolve()))
|
||||
|
||||
@@ -1,14 +1,17 @@
|
||||
from nonebot import on_command
|
||||
from nonebot.permission import SUPERUSER
|
||||
from nonebot.adapters.onebot.v11 import Bot, Message
|
||||
from nonebot.params import Command, CommandArg
|
||||
from typing import Tuple
|
||||
|
||||
from nonebot import on_command
|
||||
from nonebot.adapters.onebot.v11 import Bot, Message, MessageEvent
|
||||
from nonebot.params import Command, CommandArg
|
||||
from nonebot.permission import SUPERUSER
|
||||
from nonebot.rule import to_me
|
||||
from utils.utils import is_number
|
||||
|
||||
from models.group_info import GroupInfo
|
||||
from services.log import logger
|
||||
from utils.depends import OneCommand
|
||||
from utils.manager import requests_manager
|
||||
from utils.message_builder import image
|
||||
from models.group_info import GroupInfo
|
||||
|
||||
from utils.utils import is_number
|
||||
|
||||
__zx_plugin_name__ = "显示所有好友群组 [Superuser]"
|
||||
__plugin_usage__ = """
|
||||
@@ -76,8 +79,7 @@ async def _(bot: Bot):
|
||||
|
||||
|
||||
@friend_handle.handle()
|
||||
async def _(bot: Bot, cmd: Tuple[str, ...] = Command(), arg: Message = CommandArg()):
|
||||
cmd = cmd[0]
|
||||
async def _(bot: Bot, cmd: str = OneCommand(), arg: Message = CommandArg()):
|
||||
id_ = arg.extract_plain_text().strip()
|
||||
if is_number(id_):
|
||||
id_ = int(id_)
|
||||
@@ -97,8 +99,9 @@ async def _(bot: Bot, cmd: Tuple[str, ...] = Command(), arg: Message = CommandAr
|
||||
|
||||
|
||||
@group_handle.handle()
|
||||
async def _(bot: Bot, cmd: Tuple[str, ...] = Command(), arg: Message = CommandArg()):
|
||||
cmd = cmd[0]
|
||||
async def _(
|
||||
bot: Bot, event: MessageEvent, cmd: str = OneCommand(), arg: Message = CommandArg()
|
||||
):
|
||||
id_ = arg.extract_plain_text().strip()
|
||||
flag = None
|
||||
if is_number(id_):
|
||||
@@ -106,31 +109,33 @@ async def _(bot: Bot, cmd: Tuple[str, ...] = Command(), arg: Message = CommandAr
|
||||
if cmd[:2] == "同意":
|
||||
rid = requests_manager.get_group_id(id_)
|
||||
if rid:
|
||||
if await GroupInfo.get_group_info(rid):
|
||||
await GroupInfo.set_group_flag(rid, 1)
|
||||
if group := await GroupInfo.get_or_none(group_id=rid):
|
||||
group.group_flag = 1
|
||||
await group.save(update_fields=["group_flag"])
|
||||
else:
|
||||
group_info = await bot.get_group_info(group_id=rid)
|
||||
await GroupInfo.add_group_info(
|
||||
rid,
|
||||
group_info["group_name"],
|
||||
group_info["max_member_count"],
|
||||
group_info["member_count"],
|
||||
1
|
||||
await GroupInfo.create(
|
||||
group_id=rid,
|
||||
group_name=group_info["group_name"],
|
||||
max_member_count=group_info["max_member_count"],
|
||||
member_count=group_info["member_count"],
|
||||
group_flag=1,
|
||||
)
|
||||
flag = await requests_manager.approve(bot, id_, "group")
|
||||
else:
|
||||
await friend_handle.send("同意群聊请求失败,未找到此id的请求..")
|
||||
await group_handle.send("同意群聊请求失败,未找到此id的请求..")
|
||||
logger.info("同意群聊请求失败,未找到此id的请求..", cmd, event.user_id)
|
||||
else:
|
||||
flag = await requests_manager.refused(bot, id_, "group")
|
||||
if flag == 1:
|
||||
await friend_handle.send(f"{cmd[:2]}群聊请求失败,该请求已失效..")
|
||||
await group_handle.send(f"{cmd[:2]}群聊请求失败,该请求已失效..")
|
||||
requests_manager.delete_request(id_, "group")
|
||||
elif flag == 2:
|
||||
await friend_handle.send(f"{cmd[:2]}群聊请求失败,未找到此id的请求..")
|
||||
await group_handle.send(f"{cmd[:2]}群聊请求失败,未找到此id的请求..")
|
||||
else:
|
||||
await friend_handle.send(f"{cmd[:2]}群聊请求成功!")
|
||||
await group_handle.send(f"{cmd[:2]}群聊请求成功!")
|
||||
else:
|
||||
await friend_handle.send("id必须为纯数字!")
|
||||
await group_handle.send("id必须为纯数字!")
|
||||
|
||||
|
||||
@cls_request.handle()
|
||||
@@ -139,15 +144,15 @@ async def _():
|
||||
for type_ in ["private", "group"]:
|
||||
msg = await requests_manager.show(type_)
|
||||
if msg:
|
||||
_str += image(b64=msg)
|
||||
_str += image(msg)
|
||||
else:
|
||||
_str += "没有任何好友请求.." if type_ == "private" else "没有任何群聊请求.."
|
||||
if type_ == "private":
|
||||
_str += '\n--------------------\n'
|
||||
_str += "\n--------------------\n"
|
||||
await cls_request.send(Message(_str))
|
||||
|
||||
|
||||
@clear_request.handle()
|
||||
async def _():
|
||||
requests_manager.clear()
|
||||
await cls_request.send("已清空所有好友/群聊请求..")
|
||||
await clear_request.send("已清空所有好友/群聊请求..")
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
import asyncio
|
||||
import os
|
||||
import time
|
||||
|
||||
from nonebot import on_command
|
||||
from nonebot.permission import SUPERUSER
|
||||
from configs.path_config import TEMP_PATH
|
||||
from nonebot.rule import to_me
|
||||
from utils.utils import scheduler
|
||||
|
||||
from configs.path_config import TEMP_PATH
|
||||
from services.log import logger
|
||||
from utils.manager import resources_manager
|
||||
import asyncio
|
||||
import time
|
||||
import os
|
||||
from utils.utils import scheduler
|
||||
|
||||
__zx_plugin_name__ = "清理临时数据 [Superuser]"
|
||||
__plugin_usage__ = """
|
||||
@@ -37,22 +39,29 @@ async def _():
|
||||
await clear_data.send("开始清理临时数据....")
|
||||
size = await asyncio.get_event_loop().run_in_executor(None, _clear_data)
|
||||
await clear_data.send("共清理了 {:.2f}MB 的数据...".format(size / 1024 / 1024))
|
||||
logger.info("清理临时数据完成," + "共清理了 {:.2f}MB 的数据...".format(size / 1024 / 1024))
|
||||
|
||||
|
||||
def _clear_data() -> float:
|
||||
logger.debug("开始清理临时文件...")
|
||||
size = 0
|
||||
for dir_ in resources_manager.get_temp_data_dir():
|
||||
if dir_.exists():
|
||||
for file in os.listdir(dir_):
|
||||
file = dir_ / file
|
||||
if file.is_file():
|
||||
try:
|
||||
if time.time() - os.path.getatime(file) > 300:
|
||||
file_size = os.path.getsize(file)
|
||||
file.unlink()
|
||||
size += file_size
|
||||
except Exception as e:
|
||||
logger.error(f"清理临时数据错误...{type(e)}:{e}")
|
||||
dir_list = [dir_ for dir_ in resources_manager.get_temp_data_dir() if dir_.exists()]
|
||||
for dir_ in dir_list:
|
||||
logger.debug(f"尝试清理文件夹: {dir_.absolute()}", "清理临时数据")
|
||||
dir_size = 0
|
||||
for file in os.listdir(dir_):
|
||||
file = dir_ / file
|
||||
if file.is_file():
|
||||
try:
|
||||
if time.time() - os.path.getatime(file) > 10:
|
||||
file_size = os.path.getsize(file)
|
||||
file.unlink()
|
||||
size += file_size
|
||||
dir_size += file_size
|
||||
logger.debug(f"移除临时文件: {file.absolute()}", "清理临时数据")
|
||||
except Exception as e:
|
||||
logger.error(f"清理临时数据错误,临时文件夹: {dir_.absolute()}...", "清理临时数据", e=e)
|
||||
logger.debug("清理临时文件夹大小: {:.2f}MB".format(size / 1024 / 1024), "清理临时数据")
|
||||
return float(size)
|
||||
|
||||
|
||||
|
||||
@@ -1,37 +1,92 @@
|
||||
from nonebot.adapters.onebot.v11 import Message
|
||||
import asyncio
|
||||
|
||||
from nonebot import on_command
|
||||
from nonebot.adapters.onebot.v11 import Bot, GroupMessageEvent, Message, MessageEvent
|
||||
from nonebot.params import CommandArg
|
||||
from nonebot.permission import SUPERUSER
|
||||
from nonebot.rule import to_me
|
||||
from services.db_context import db
|
||||
from nonebot.params import CommandArg
|
||||
from tortoise import Tortoise
|
||||
|
||||
from services.db_context import TestSQL
|
||||
from services.log import logger
|
||||
from utils.message_builder import custom_forward_msg
|
||||
|
||||
__zx_plugin_name__ = "执行sql [Superuser]"
|
||||
__plugin_usage__ = """
|
||||
usage:
|
||||
执行一段sql语句
|
||||
指令:
|
||||
exec [sql语句]
|
||||
exec [sql语句] ([查询页数,19条/页])
|
||||
查看所有表
|
||||
""".strip()
|
||||
__plugin_des__ = "执行一段sql语句"
|
||||
__plugin_cmd__ = [
|
||||
"exec [sql语句]",
|
||||
]
|
||||
__plugin_version__ = 0.1
|
||||
__plugin_cmd__ = ["exec [sql语句]", "查看所有表"]
|
||||
__plugin_version__ = 0.2
|
||||
__plugin_author__ = "HibiKier"
|
||||
|
||||
|
||||
exec_ = on_command("exec", rule=to_me(), permission=SUPERUSER, priority=1, block=True)
|
||||
tables = on_command("查看所有表", rule=to_me(), permission=SUPERUSER, priority=1, block=True)
|
||||
|
||||
|
||||
@exec_.handle()
|
||||
async def _(arg: Message = CommandArg()):
|
||||
async def _(bot: Bot, event: MessageEvent, arg: Message = CommandArg()):
|
||||
sql = arg.extract_plain_text().strip()
|
||||
async with db.transaction():
|
||||
if not sql:
|
||||
await exec_.finish("未接受到sql语句")
|
||||
db = Tortoise.get_connection("default")
|
||||
# try:
|
||||
# 判断是否为SELECT语句
|
||||
if sql.lower().startswith("select"):
|
||||
pass
|
||||
# # 分割语句
|
||||
try:
|
||||
query = db.text(sql)
|
||||
await db.first(query)
|
||||
await exec_.send("执行 sql 语句成功.")
|
||||
except Exception as e:
|
||||
await exec_.send(f"执行 sql 语句失败 {type(e)}:{e}")
|
||||
logger.error(f"执行 sql 语句失败 {type(e)}:{e}")
|
||||
page = int(sql.split(" ")[-1]) - 1
|
||||
sql_list = sql.split(" ")[:-1]
|
||||
except ValueError:
|
||||
page = 0
|
||||
sql_list = sql.split(" ")
|
||||
# 拼接语句
|
||||
sql = " ".join(sql_list)
|
||||
res = await db.execute_query_dict(sql)
|
||||
msg_list = [f"第{page+1}页查询结果:"]
|
||||
# logger.info(res)
|
||||
# 获取所有字段
|
||||
keys = res[0].keys()
|
||||
# 每页10条
|
||||
for i in res[page * 10 : (page + 1) * 10]:
|
||||
msg = ""
|
||||
for key in keys:
|
||||
msg += f"{key}: {i[key]}\n"
|
||||
msg += f"第{page+1}页第{res.index(i)+1}条"
|
||||
msg_list.append(msg)
|
||||
# 检查是私聊还是群聊
|
||||
if isinstance(event, GroupMessageEvent):
|
||||
forward_msg_list = custom_forward_msg(msg_list, bot.self_id)
|
||||
await bot.send_group_forward_msg(
|
||||
group_id=event.group_id, messages=forward_msg_list
|
||||
)
|
||||
else:
|
||||
for msg in msg_list:
|
||||
await exec_.send(msg)
|
||||
await asyncio.sleep(0.2)
|
||||
return
|
||||
else:
|
||||
await TestSQL.raw(sql)
|
||||
await exec_.send("执行 sql 语句成功.")
|
||||
# except Exception as e:
|
||||
# await exec_.send(f"执行 sql 语句失败 {type(e)}:{e}")
|
||||
# logger.error(f"执行 sql 语句失败 {type(e)}:{e}")
|
||||
|
||||
|
||||
@tables.handle()
|
||||
async def _(bot: Bot, event: MessageEvent):
|
||||
# 获取所有表
|
||||
db = Tortoise.get_connection("default")
|
||||
query = await db.execute_query_dict(
|
||||
"select tablename from pg_tables where schemaname = 'public'"
|
||||
)
|
||||
msg = "数据库中的所有表名:\n"
|
||||
for tablename in query:
|
||||
msg += str(tablename["tablename"]) + "\n"
|
||||
await tables.finish(msg)
|
||||
|
||||
@@ -1,29 +1,39 @@
|
||||
from nonebot.adapters.onebot.v11 import Bot, MessageEvent, GROUP, GroupMessageEvent, Message
|
||||
from nonebot import on_command, on_regex
|
||||
from nonebot.permission import SUPERUSER
|
||||
from nonebot.typing import T_State
|
||||
from nonebot.rule import to_me
|
||||
from utils.utils import is_number
|
||||
from utils.manager import group_manager, plugins2settings_manager
|
||||
from models.group_info import GroupInfo
|
||||
from services.log import logger
|
||||
from configs.config import NICKNAME
|
||||
from nonebot.adapters.onebot.v11.exception import ActionFailed
|
||||
from nonebot.params import Command, CommandArg
|
||||
from typing import Tuple
|
||||
|
||||
from nonebot import on_command, on_regex
|
||||
from nonebot.adapters.onebot.v11 import (
|
||||
GROUP,
|
||||
Bot,
|
||||
GroupMessageEvent,
|
||||
Message,
|
||||
MessageEvent,
|
||||
)
|
||||
from nonebot.params import Command, CommandArg
|
||||
from nonebot.permission import SUPERUSER
|
||||
from nonebot.rule import to_me
|
||||
|
||||
from configs.config import NICKNAME
|
||||
from models.group_info import GroupInfo
|
||||
from services.log import logger
|
||||
from utils.depends import OneCommand
|
||||
from utils.image_utils import text2image
|
||||
from utils.manager import group_manager, plugins2settings_manager
|
||||
from utils.message_builder import image
|
||||
from utils.utils import is_number
|
||||
|
||||
__zx_plugin_name__ = "管理群操作 [Superuser]"
|
||||
__plugin_usage__ = """
|
||||
usage:
|
||||
群权限 | 群白名单 | 退出群 操作
|
||||
指令:
|
||||
退群 [group_id]
|
||||
退群,添加/删除群白名单,添加/删除群认证,当在群聊中这五个命令且没有指定群号时,默认指定当前群聊
|
||||
指令:
|
||||
退群 ?[group_id]
|
||||
修改群权限 [group_id] [等级]
|
||||
添加群白名单 *[group_id]
|
||||
删除群白名单 *[group_id]
|
||||
添加群认证 *[group_id]
|
||||
删除群认证 *[group_id]
|
||||
修改群权限 [等级]: 该命令仅在群聊时生效,默认修改当前群聊
|
||||
添加群白名单 ?*[group_id]
|
||||
删除群白名单 ?*[group_id]
|
||||
添加群认证 ?*[group_id]
|
||||
删除群认证 ?*[group_id]
|
||||
查看群白名单
|
||||
""".strip()
|
||||
__plugin_des__ = "管理群操作"
|
||||
@@ -47,7 +57,7 @@ my_group_level = on_command(
|
||||
"查看群权限", aliases={"群权限"}, priority=5, permission=GROUP, block=True
|
||||
)
|
||||
what_up_group_level = on_regex(
|
||||
".*?(提高|提升|升高|增加|加上)(.*?)群权限.*?",
|
||||
"(:?提高|提升|升高|增加|加上).*?群权限",
|
||||
rule=to_me(),
|
||||
priority=5,
|
||||
permission=GROUP,
|
||||
@@ -67,47 +77,57 @@ group_auth = on_command(
|
||||
|
||||
|
||||
@del_group.handle()
|
||||
async def _(bot: Bot, arg: Message = CommandArg()):
|
||||
async def _(bot: Bot, event: MessageEvent, arg: Message = CommandArg()):
|
||||
group_id = arg.extract_plain_text().strip()
|
||||
if not group_id and isinstance(event, GroupMessageEvent):
|
||||
group_id = event.group_id
|
||||
if group_id:
|
||||
if is_number(group_id):
|
||||
group_list = [x["group_id"] for x in await bot.get_group_list()]
|
||||
group_id = int(group_id)
|
||||
if group_id not in group_list:
|
||||
logger.debug("群聊不存在", "退群", event.user_id, target=group_id)
|
||||
await del_group.finish(f"{NICKNAME}未在该群聊中...")
|
||||
try:
|
||||
await bot.set_group_leave(group_id=int(group_id))
|
||||
logger.info(f"退出群聊 {group_id} 成功")
|
||||
await bot.set_group_leave(group_id=group_id)
|
||||
logger.info(f"{NICKNAME}退出群聊成功", "退群", event.user_id, target=group_id)
|
||||
await del_group.send(f"退出群聊 {group_id} 成功", at_sender=True)
|
||||
group_manager.delete_group(int(group_id))
|
||||
await GroupInfo.delete_group_info(int(group_id))
|
||||
group_manager.delete_group(group_id)
|
||||
await GroupInfo.filter(group_id=group_id).delete()
|
||||
except Exception as e:
|
||||
logger.info(f"退出群聊 {group_id} 失败 e:{e}")
|
||||
logger.error(f"退出群聊失败", "退群", event.user_id, target=group_id, e=e)
|
||||
await del_group.send(f"退出群聊 {group_id} 失败", at_sender=True)
|
||||
else:
|
||||
await del_group.finish(f"请输入正确的群号", at_sender=True)
|
||||
await del_group.send(f"请输入正确的群号", at_sender=True)
|
||||
else:
|
||||
await del_group.finish(f"请输入群号", at_sender=True)
|
||||
await del_group.send(f"请输入群号", at_sender=True)
|
||||
|
||||
|
||||
@add_group_level.handle()
|
||||
async def _(bot: Bot, event: MessageEvent, arg: Message = CommandArg()):
|
||||
msg = arg.extract_plain_text().strip()
|
||||
msg = msg.split()
|
||||
group_id = 0
|
||||
level = 0
|
||||
if isinstance(event, GroupMessageEvent) and len(msg) == 1:
|
||||
msg = [event.group_id, msg[0]]
|
||||
if not msg:
|
||||
await add_group_level.finish("用法:修改群权限 [group] [level]")
|
||||
msg = msg.split(" ")
|
||||
await add_group_level.finish("缺失参数...")
|
||||
if len(msg) < 2:
|
||||
await add_group_level.finish("参数不完全..[group] [level]")
|
||||
await add_group_level.finish("缺失参数...")
|
||||
if is_number(msg[0]) and is_number(msg[1]):
|
||||
group_id = msg[0]
|
||||
group_id = int(msg[0])
|
||||
level = int(msg[1])
|
||||
else:
|
||||
await add_group_level.finish("参数错误...group和level必须是数字..")
|
||||
await add_group_level.finish("参数错误...群号和等级必须是数字..")
|
||||
old_level = group_manager.get_group_level(group_id)
|
||||
group_manager.set_group_level(group_id, level)
|
||||
await add_group_level.send("修改成功...", at_sender=True)
|
||||
if level > -1:
|
||||
await bot.send_group_msg(
|
||||
group_id=int(group_id), message=f"管理员修改了此群权限:{old_level} -> {level}"
|
||||
group_id=group_id, message=f"管理员修改了此群权限:{old_level} -> {level}"
|
||||
)
|
||||
logger.info(f"{event.user_id} 修改了 {group_id} 的权限:{level}")
|
||||
logger.info(f"修改群权限:{level}", "修改群权限", event.user_id, target=group_id)
|
||||
|
||||
|
||||
@my_group_level.handle()
|
||||
@@ -116,89 +136,109 @@ async def _(event: GroupMessageEvent):
|
||||
tmp = ""
|
||||
data = plugins2settings_manager.get_data()
|
||||
for module in data:
|
||||
if data[module]["level"] > level:
|
||||
plugin_name = data[module]["cmd"][0]
|
||||
if data[module].level > level:
|
||||
plugin_name = data[module].cmd[0]
|
||||
if plugin_name == "pixiv":
|
||||
plugin_name = "搜图 p站排行"
|
||||
tmp += f"{plugin_name}\n"
|
||||
if tmp:
|
||||
tmp = "\n目前无法使用的功能:\n" + tmp
|
||||
await my_group_level.finish(f"当前群权限:{level}{tmp}")
|
||||
if not tmp:
|
||||
await my_group_level.finish(f"当前群权限:{level}")
|
||||
await my_group_level.finish(
|
||||
f"当前群权限:{level}\n目前无法使用的功能:\n"
|
||||
+ image(await text2image(tmp, padding=10, color="#f9f6f2"))
|
||||
)
|
||||
|
||||
|
||||
@what_up_group_level.handle()
|
||||
async def _():
|
||||
await what_up_group_level.finish(
|
||||
f"[此功能用于防止内鬼,如果引起不便那真是抱歉了]\n" f"目前提高群权限的方法:\n" f"\t1.管理员修改权限"
|
||||
f"[此功能用于防止内鬼,如果引起不便那真是抱歉了]\n" f"目前提高群权限的方法:\n" f"\t1.超级管理员修改权限"
|
||||
)
|
||||
|
||||
|
||||
@manager_group_whitelist.handle()
|
||||
async def _(bot: Bot, cmd: Tuple[str, ...] = Command(), arg: Message = CommandArg()):
|
||||
cmd = cmd[0]
|
||||
async def _(
|
||||
bot: Bot, event: MessageEvent, cmd: str = OneCommand(), arg: Message = CommandArg()
|
||||
):
|
||||
msg = arg.extract_plain_text().strip().split()
|
||||
all_group = [
|
||||
g["group_id"] for g in await bot.get_group_list()
|
||||
]
|
||||
if not msg and isinstance(event, GroupMessageEvent):
|
||||
msg = [event.group_id]
|
||||
if not msg:
|
||||
await manager_group_whitelist.finish("请输入群号")
|
||||
all_group = [g["group_id"] for g in await bot.get_group_list()]
|
||||
error_group = []
|
||||
group_list = []
|
||||
for group in msg:
|
||||
if is_number(group) and int(group) in all_group:
|
||||
group_list.append(int(group))
|
||||
for group_id in msg:
|
||||
if is_number(group_id) and int(group_id) in all_group:
|
||||
group_list.append(int(group_id))
|
||||
else:
|
||||
logger.debug(f"群号不合法或不存在", cmd, target=group_id)
|
||||
error_group.append(group_id)
|
||||
if group_list:
|
||||
for group in group_list:
|
||||
for group_id in group_list:
|
||||
if cmd in ["添加群白名单"]:
|
||||
group_manager.add_group_white_list(group)
|
||||
group_manager.add_group_white_list(group_id)
|
||||
else:
|
||||
group_manager.delete_group_white_list(group)
|
||||
group_manager.delete_group_white_list(group_id)
|
||||
group_list = [str(x) for x in group_list]
|
||||
await manager_group_whitelist.send(
|
||||
"已成功将 " + "\n".join(group_list) + " " + cmd
|
||||
)
|
||||
else:
|
||||
await manager_group_whitelist.send(f"添加失败,请检查{NICKNAME}是否已加入这些群聊或重复添加/删除群白单名")
|
||||
await manager_group_whitelist.send("已成功将 " + "\n".join(group_list) + " " + cmd)
|
||||
group_manager.save()
|
||||
if error_group:
|
||||
await manager_group_whitelist.send("以下群聊不合法或不存在:\n" + "\n".join(error_group))
|
||||
|
||||
|
||||
@show_group_whitelist.handle()
|
||||
async def _():
|
||||
x = group_manager.get_group_white_list()
|
||||
x = [str(g) for g in x]
|
||||
if x:
|
||||
await show_group_whitelist.send("目前的群白名单:\n" + "\n".join(x))
|
||||
else:
|
||||
await show_group_whitelist.send("没有任何群在群白名单...")
|
||||
group = [str(g) for g in group_manager.get_group_white_list()]
|
||||
if not group:
|
||||
await show_group_whitelist.finish("没有任何群在群白名单...")
|
||||
await show_group_whitelist.send("目前的群白名单:\n" + "\n".join(group))
|
||||
|
||||
|
||||
@group_auth.handle()
|
||||
async def _(bot: Bot, cmd: Tuple[str, ...] = Command(), arg: Message = CommandArg()):
|
||||
cmd = cmd[0]
|
||||
async def _(
|
||||
bot: Bot, event: MessageEvent, cmd: str = OneCommand(), arg: Message = CommandArg()
|
||||
):
|
||||
msg = arg.extract_plain_text().strip().split()
|
||||
if isinstance(event, GroupMessageEvent) and not msg:
|
||||
msg = [event.group_id]
|
||||
if not msg:
|
||||
await manager_group_whitelist.finish("请输入群号")
|
||||
error_group = []
|
||||
all_group = [g["group_id"] for g in await bot.get_group_list()]
|
||||
for group_id in msg:
|
||||
if not is_number(group_id):
|
||||
await group_auth.send(f"{group_id}非纯数字,已跳过该项..")
|
||||
group_id = int(group_id)
|
||||
if cmd[:2] == "添加":
|
||||
if await GroupInfo.get_group_info(group_id):
|
||||
await GroupInfo.set_group_flag(group_id, 1)
|
||||
else:
|
||||
if is_number(group_id) and group_id in all_group:
|
||||
if cmd[:2] == "添加":
|
||||
try:
|
||||
group_info = await bot.get_group_info(group_id=group_id)
|
||||
except ActionFailed:
|
||||
group_info = {
|
||||
"group_id": group_id,
|
||||
"group_name": "_",
|
||||
"max_member_count": -1,
|
||||
"member_count": -1,
|
||||
}
|
||||
await GroupInfo.add_group_info(
|
||||
group_info["group_id"],
|
||||
group_info["group_name"],
|
||||
group_info["max_member_count"],
|
||||
group_info["member_count"],
|
||||
1,
|
||||
)
|
||||
await GroupInfo.update_or_create(
|
||||
group_id=group_info["group_id"],
|
||||
defaults={
|
||||
"group_flag": 1,
|
||||
"group_name": group_info["group_name"],
|
||||
"max_member_count": group_info["max_member_count"],
|
||||
"member_count": group_info["member_count"],
|
||||
},
|
||||
)
|
||||
except Exception as e:
|
||||
await group_auth.send(f"添加群认证 {group_id} 发生错误!")
|
||||
logger.error(f"添加群认证发生错误", cmd, target=group_id, e=e)
|
||||
else:
|
||||
await group_auth.send(f"已为 {group_id} {cmd[:2]}群认证..")
|
||||
logger.info(f"添加群认证成功", cmd, target=group_id)
|
||||
else:
|
||||
if group := await GroupInfo.filter(group_id=group_id).first():
|
||||
await group.update_or_create(
|
||||
group_id=group_id, defaults={"group_flag": 0}
|
||||
)
|
||||
await group_auth.send(f"已删除 {group_id} 群认证..")
|
||||
logger.info(f"删除群认证成功", cmd, target=group_id)
|
||||
else:
|
||||
await group_auth.send(f"未查找到群聊: {group_id}")
|
||||
logger.info(f"未找到群聊", cmd, target=group_id)
|
||||
else:
|
||||
if await GroupInfo.get_group_info(group_id):
|
||||
await GroupInfo.set_group_flag(group_id, 0)
|
||||
await group_auth.send(
|
||||
f'已为 {group_id} {cmd[:2]}群认证..'
|
||||
)
|
||||
logger.debug(f"群号不合法或不存在", cmd, target=group_id)
|
||||
error_group.append(str(group_id))
|
||||
if error_group:
|
||||
await manager_group_whitelist.send("以下群聊不合法或不存在:\n" + "\n".join(error_group))
|
||||
|
||||
@@ -1,45 +1,37 @@
|
||||
from nonebot import on_command
|
||||
from nonebot.permission import SUPERUSER
|
||||
from nonebot.rule import to_me
|
||||
from utils.manager import (
|
||||
plugins2cd_manager,
|
||||
plugins2settings_manager,
|
||||
plugins2block_manager,
|
||||
group_manager,
|
||||
)
|
||||
|
||||
from configs.config import Config
|
||||
from services.log import logger
|
||||
from utils.manager import (
|
||||
group_manager,
|
||||
plugins2block_manager,
|
||||
plugins2cd_manager,
|
||||
plugins2settings_manager,
|
||||
)
|
||||
from utils.utils import scheduler
|
||||
|
||||
|
||||
__zx_plugin_name__ = "重载插件配置 [Superuser]"
|
||||
__zx_plugin_name__ = "重载配置 [Superuser]"
|
||||
__plugin_usage__ = """
|
||||
usage:
|
||||
重载插件配置
|
||||
重载配置
|
||||
plugins2settings,
|
||||
plugins2cd
|
||||
plugins2block
|
||||
group_manager
|
||||
指令:
|
||||
重载插件配置
|
||||
重载配置
|
||||
""".strip()
|
||||
__plugin_des__ = "重载插件配置"
|
||||
__plugin_des__ = "重载配置"
|
||||
__plugin_cmd__ = [
|
||||
"重载插件配置",
|
||||
"重载配置",
|
||||
]
|
||||
__plugin_version__ = 0.1
|
||||
__plugin_version__ = 0.2
|
||||
__plugin_author__ = "HibiKier"
|
||||
__plugin_configs__ = {
|
||||
"AUTO_RELOAD": {
|
||||
"value": False,
|
||||
"help": "自动重载配置文件",
|
||||
"default_value": False
|
||||
},
|
||||
"AUTO_RELOAD_TIME": {
|
||||
"value": 180,
|
||||
"help": "控制自动重载配置文件时长",
|
||||
"default_value": 180
|
||||
}
|
||||
"AUTO_RELOAD": {"value": False, "help": "自动重载配置文件", "default_value": False, "type": bool},
|
||||
"AUTO_RELOAD_TIME": {"value": 180, "help": "控制自动重载配置文件时长", "default_value": 180, "type": int},
|
||||
}
|
||||
|
||||
|
||||
@@ -59,7 +51,7 @@ async def _():
|
||||
|
||||
|
||||
@scheduler.scheduled_job(
|
||||
'interval',
|
||||
"interval",
|
||||
seconds=Config.get_config("reload_setting", "AUTO_RELOAD_TIME", 180),
|
||||
)
|
||||
async def _():
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
from typing import List, Tuple
|
||||
|
||||
from nonebot import on_command
|
||||
from nonebot.adapters.onebot.v11 import Bot, GroupMessageEvent, Message, MessageEvent
|
||||
from nonebot.exception import ActionFailed
|
||||
from nonebot.params import CommandArg
|
||||
from nonebot.permission import SUPERUSER
|
||||
|
||||
from models.level_user import LevelUser
|
||||
from nonebot.adapters.onebot.v11 import Bot, MessageEvent, Message, GroupMessageEvent
|
||||
from utils.utils import get_message_at, is_number
|
||||
from services.log import logger
|
||||
from utils.depends import AtList, OneCommand
|
||||
from utils.message_builder import at
|
||||
from nonebot.params import Command, CommandArg
|
||||
from typing import Tuple
|
||||
from utils.utils import is_number
|
||||
|
||||
__zx_plugin_name__ = "用户权限管理 [Superuser]"
|
||||
__plugin_usage__ = """
|
||||
@@ -16,12 +20,14 @@ usage:
|
||||
添加权限 [at] [权限]
|
||||
添加权限 [qq] [group_id] [权限]
|
||||
删除权限 [at]
|
||||
删除权限 [qq] [group_id]
|
||||
""".strip()
|
||||
__plugin_des__ = "增删改用户的权限"
|
||||
__plugin_cmd__ = [
|
||||
"添加权限 [at] [权限]",
|
||||
"添加权限 [qq] [group_id] [权限]",
|
||||
"删除权限 [at]",
|
||||
"删除权限 [qq] [group_id]",
|
||||
]
|
||||
__plugin_version__ = 0.1
|
||||
__plugin_author__ = "HibiKier"
|
||||
@@ -40,21 +46,22 @@ super_cmd = on_command(
|
||||
async def _(
|
||||
bot: Bot,
|
||||
event: MessageEvent,
|
||||
cmd: Tuple[str, ...] = Command(),
|
||||
cmd: str = OneCommand(),
|
||||
arg: Message = CommandArg(),
|
||||
at_list: List[int] = AtList(),
|
||||
):
|
||||
group_id = event.group_id if isinstance(event, GroupMessageEvent) else -1
|
||||
level = None
|
||||
args = arg.extract_plain_text().strip().split()
|
||||
qq = get_message_at(event.json())
|
||||
flag = 2
|
||||
qq = None
|
||||
try:
|
||||
if qq:
|
||||
qq = qq[0]
|
||||
if cmd[0][:2] == "添加" and args and is_number(args[0]):
|
||||
if at_list:
|
||||
qq = at_list[0]
|
||||
if cmd[:2] == "添加" and args and is_number(args[0]):
|
||||
level = int(args[0])
|
||||
else:
|
||||
if cmd[0][:2] == "添加":
|
||||
if cmd[:2] == "添加":
|
||||
if (
|
||||
len(args) > 2
|
||||
and is_number(args[0])
|
||||
@@ -69,33 +76,39 @@ async def _(
|
||||
qq = int(args[0])
|
||||
group_id = int(args[1])
|
||||
flag = 1
|
||||
level = -1 if cmd[0][:2] == "删除" else level
|
||||
level = -1 if cmd[:2] == "删除" else level
|
||||
if group_id == -1 or not level or not qq:
|
||||
raise IndexError()
|
||||
except IndexError:
|
||||
await super_cmd.finish(__plugin_usage__)
|
||||
if not qq:
|
||||
await super_cmd.finish("未指定对象...")
|
||||
try:
|
||||
if cmd[0][:2] == "添加":
|
||||
if await LevelUser.set_level(qq, group_id, level, 1):
|
||||
result = f"添加管理成功, 权限: {level}"
|
||||
else:
|
||||
result = f"管理已存在, 更新权限: {level}"
|
||||
if cmd[:2] == "添加":
|
||||
await LevelUser.set_level(qq, group_id, level, 1)
|
||||
result = f"设置权限成功, 权限: {level}"
|
||||
else:
|
||||
if await LevelUser.delete_level(qq, event.group_id):
|
||||
if await LevelUser.delete_level(qq, group_id):
|
||||
result = "删除管理成功!"
|
||||
else:
|
||||
result = "该账号无管理权限!"
|
||||
if flag == 2:
|
||||
await super_cmd.send(result)
|
||||
elif flag == 1:
|
||||
await bot.send_group_msg(
|
||||
group_id=group_id,
|
||||
message=Message(
|
||||
f"{at(qq)}管理员修改了你的权限"
|
||||
f"\n--------\n你当前的权限等级:{level if level != -1 else 0}"
|
||||
),
|
||||
)
|
||||
try:
|
||||
await bot.send_group_msg(
|
||||
group_id=group_id,
|
||||
message=Message(
|
||||
f"{at(qq)}管理员修改了你的权限"
|
||||
f"\n--------\n你当前的权限等级:{level if level != -1 else 0}"
|
||||
),
|
||||
)
|
||||
except ActionFailed:
|
||||
pass
|
||||
await super_cmd.send("修改成功")
|
||||
logger.info(
|
||||
f"修改权限: {level if level != -1 else 0}", cmd, event.user_id, group_id, qq
|
||||
)
|
||||
except Exception as e:
|
||||
await super_cmd.send("执行指令失败!")
|
||||
logger.error(f"执行指令失败 e:{e}")
|
||||
logger.error(f"执行指令失败", cmd, event.user_id, group_id, qq, e=e)
|
||||
|
||||
@@ -1,58 +0,0 @@
|
||||
from nonebot import on_command
|
||||
from nonebot.permission import SUPERUSER
|
||||
from nonebot.adapters.onebot.v11 import Bot, MessageEvent, Message
|
||||
from nonebot.rule import to_me
|
||||
from utils.utils import is_number
|
||||
from services.log import logger
|
||||
from utils.manager import group_manager
|
||||
from nonebot.params import Command, CommandArg
|
||||
from typing import Tuple
|
||||
|
||||
|
||||
__zx_plugin_name__ = "超级用户被动开关 [Superuser]"
|
||||
__plugin_usage__ = """
|
||||
usage:
|
||||
超级用户被动开关
|
||||
指令:
|
||||
开启/关闭广播通知
|
||||
""".strip()
|
||||
__plugin_des__ = "超级用户被动开关"
|
||||
__plugin_cmd__ = [
|
||||
"开启/关闭广播通知",
|
||||
]
|
||||
__plugin_version__ = 0.1
|
||||
__plugin_author__ = "HibiKier"
|
||||
|
||||
|
||||
oc_gb = on_command(
|
||||
"开启广播通知",
|
||||
aliases={"关闭广播通知"},
|
||||
rule=to_me(),
|
||||
permission=SUPERUSER,
|
||||
priority=1,
|
||||
block=True,
|
||||
)
|
||||
|
||||
|
||||
@oc_gb.handle()
|
||||
async def _(bot: Bot, event: MessageEvent, cmd: Tuple[str, ...] = Command(), arg: Message = CommandArg()):
|
||||
cmd = cmd[0]
|
||||
group = arg.extract_plain_text().strip()
|
||||
if group:
|
||||
if is_number(group):
|
||||
group = int(group)
|
||||
for g in await bot.get_group_list():
|
||||
if g["group_id"] == group:
|
||||
break
|
||||
else:
|
||||
await oc_gb.finish("没有加入这个群...", at_sender=True)
|
||||
if cmd == "开启广播通知":
|
||||
logger.info(f"USER {event.user_id} 开启了 GROUP {group} 的广播")
|
||||
await oc_gb.finish(await group_manager.open_group_task(group, "broadcast",), at_sender=True)
|
||||
else:
|
||||
logger.info(f"USER {event.user_id} 关闭了 GROUP {group} 的广播")
|
||||
await oc_gb.finish(await group_manager.close_group_task(group, "broadcast"), at_sender=True)
|
||||
else:
|
||||
await oc_gb.finish("请输入正确的群号", at_sender=True)
|
||||
else:
|
||||
await oc_gb.finish("请输入要关闭广播的群号", at_sender=True)
|
||||
@@ -1,11 +1,11 @@
|
||||
from nonebot import on_command
|
||||
from nonebot.adapters.onebot.v11 import Bot, MessageEvent
|
||||
from nonebot.permission import SUPERUSER
|
||||
from nonebot.rule import to_me
|
||||
from utils.utils import get_bot
|
||||
from services.log import logger
|
||||
from models.group_info import GroupInfo
|
||||
from models.friend_user import FriendUser
|
||||
|
||||
from models.friend_user import FriendUser
|
||||
from models.group_info import GroupInfo
|
||||
from services.log import logger
|
||||
|
||||
__zx_plugin_name__ = "更新群/好友信息 [Superuser]"
|
||||
__plugin_usage__ = """
|
||||
@@ -32,38 +32,46 @@ update_friend_info = on_command(
|
||||
|
||||
|
||||
@update_group_info.handle()
|
||||
async def _():
|
||||
bot = get_bot()
|
||||
async def _(bot: Bot, event: MessageEvent):
|
||||
gl = await bot.get_group_list()
|
||||
gl = [g["group_id"] for g in gl]
|
||||
num = 0
|
||||
rst = ""
|
||||
for g in gl:
|
||||
group_info = await bot.get_group_info(group_id=g)
|
||||
if await GroupInfo.add_group_info(
|
||||
group_info["group_id"],
|
||||
group_info["group_name"],
|
||||
group_info["max_member_count"],
|
||||
group_info["member_count"],
|
||||
):
|
||||
try:
|
||||
group_info = await bot.get_group_info(group_id=g)
|
||||
await GroupInfo.update_or_create(
|
||||
group_id=group_info["group_id"],
|
||||
defaults={
|
||||
"group_name": group_info["group_name"],
|
||||
"max_member_count": group_info["max_member_count"],
|
||||
"member_count": group_info["member_count"],
|
||||
},
|
||||
)
|
||||
num += 1
|
||||
logger.info(f"自动更新群组 {g} 信息成功")
|
||||
else:
|
||||
logger.info(f"自动更新群组 {g} 信息失败")
|
||||
rst += f"{g} 更新失败\n"
|
||||
await update_group_info.send(f"成功更新了 {num} 个群的信息\n{rst[:-1]}")
|
||||
logger.debug(
|
||||
"群聊信息更新成功", "更新群信息", event.user_id, target=group_info["group_id"]
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f"更新群聊信息失败", "更新群信息", event.user_id, target=g, e=e)
|
||||
await update_group_info.send(f"成功更新了 {len(gl)} 个群的信息")
|
||||
logger.info(f"更新群聊信息完成,共更新了 {len(gl)} 个群的信息", "更新群信息", event.user_id)
|
||||
|
||||
|
||||
@update_friend_info.handle()
|
||||
async def _():
|
||||
async def _(bot: Bot, event: MessageEvent):
|
||||
num = 0
|
||||
rst = ""
|
||||
fl = await get_bot().get_friend_list()
|
||||
error_list = []
|
||||
fl = await bot.get_friend_list()
|
||||
for f in fl:
|
||||
if await FriendUser.add_friend_info(f["user_id"], f["nickname"]):
|
||||
logger.info(f'自动更新好友 {f["user_id"]} 信息成功')
|
||||
try:
|
||||
await FriendUser.update_or_create(
|
||||
user_id=f["user_id"], defaults={"nickname": f["nickname"]}
|
||||
)
|
||||
logger.debug(f"更新好友信息成功", "更新好友信息", event.user_id, target=f["user_id"])
|
||||
num += 1
|
||||
else:
|
||||
logger.warning(f'自动更新好友 {f["user_id"]} 信息失败')
|
||||
rst += f'{f["user_id"]} 更新失败\n'
|
||||
await update_friend_info.send(f"成功更新了 {num} 个好友的信息\n{rst[:-1]}")
|
||||
except Exception as e:
|
||||
logger.error(f"更新好友信息失败", "更新好友信息", event.user_id, target=f["user_id"], e=e)
|
||||
await update_friend_info.send(f"成功更新了 {num} 个好友的信息")
|
||||
if error_list:
|
||||
await update_friend_info.send(f"以下好友更新失败:\n" + "\n".join(error_list))
|
||||
logger.info(f"更新好友信息完成,共更新了 {num} 个群的信息", "更新好友信息", event.user_id)
|
||||
|
||||
@@ -1,18 +1,16 @@
|
||||
from nonebot import on_command
|
||||
from nonebot.permission import SUPERUSER
|
||||
from nonebot.rule import to_me
|
||||
from configs.path_config import IMAGE_PATH
|
||||
|
||||
from utils.message_builder import image
|
||||
from .data_source import create_help_image
|
||||
|
||||
from .data_source import SUPERUSER_HELP_IMAGE, create_help_image
|
||||
|
||||
__zx_plugin_name__ = "超级用户帮助 [Superuser]"
|
||||
|
||||
|
||||
__zx_plugin_name__ = '超级用户帮助 [Superuser]'
|
||||
|
||||
|
||||
superuser_help_image = IMAGE_PATH / 'superuser_help.png'
|
||||
|
||||
if superuser_help_image.exists():
|
||||
superuser_help_image.unlink()
|
||||
if SUPERUSER_HELP_IMAGE.exists():
|
||||
SUPERUSER_HELP_IMAGE.unlink()
|
||||
|
||||
super_help = on_command(
|
||||
"超级用户帮助", rule=to_me(), priority=1, permission=SUPERUSER, block=True
|
||||
@@ -21,7 +19,6 @@ super_help = on_command(
|
||||
|
||||
@super_help.handle()
|
||||
async def _():
|
||||
if not superuser_help_image.exists():
|
||||
if not SUPERUSER_HELP_IMAGE.exists():
|
||||
await create_help_image()
|
||||
x = image(superuser_help_image)
|
||||
await super_help.finish(x)
|
||||
await super_help.finish(image(SUPERUSER_HELP_IMAGE))
|
||||
|
||||
@@ -1,79 +1,84 @@
|
||||
from utils.image_utils import BuildImage
|
||||
from configs.path_config import IMAGE_PATH
|
||||
from services.log import logger
|
||||
from utils.utils import get_matchers
|
||||
from nonebot.adapters.onebot.v11 import Bot
|
||||
from nonebot import Driver
|
||||
import asyncio
|
||||
import nonebot
|
||||
|
||||
import nonebot
|
||||
from configs.path_config import IMAGE_PATH
|
||||
from nonebot import Driver
|
||||
from services.log import logger
|
||||
from utils.image_template import help_template
|
||||
from utils.image_utils import (BuildImage, build_sort_image, group_image,
|
||||
text2image)
|
||||
from utils.manager import plugin_data_manager
|
||||
from utils.manager.models import PluginType
|
||||
|
||||
driver: Driver = nonebot.get_driver()
|
||||
|
||||
background = IMAGE_PATH / "background" / "0.png"
|
||||
|
||||
superuser_help_image = IMAGE_PATH / "superuser_help.png"
|
||||
SUPERUSER_HELP_IMAGE = IMAGE_PATH / "superuser_help.png"
|
||||
|
||||
|
||||
@driver.on_bot_connect
|
||||
async def create_help_image(bot: Bot = None):
|
||||
async def create_help_image():
|
||||
"""
|
||||
创建超级用户帮助图片
|
||||
"""
|
||||
await asyncio.get_event_loop().run_in_executor(None, _create_help_image)
|
||||
|
||||
|
||||
def _create_help_image():
|
||||
"""
|
||||
创建管理员帮助图片
|
||||
"""
|
||||
_matchers = get_matchers()
|
||||
_plugin_name_list = []
|
||||
width = 0
|
||||
help_str = "超级用户帮助\n\n* 注: ‘*’ 代表可有多个相同参数 ‘?’ 代表可省略该参数 *\n\n"
|
||||
tmp_img = BuildImage(0, 0, plain_text='1', font_size=24)
|
||||
for matcher in _matchers:
|
||||
plugin_name = ""
|
||||
if SUPERUSER_HELP_IMAGE.exists():
|
||||
return
|
||||
plugin_data_ = plugin_data_manager.get_data()
|
||||
image_list = []
|
||||
task_list = []
|
||||
for plugin_data in [
|
||||
plugin_data_[x]
|
||||
for x in plugin_data_
|
||||
if plugin_data_[x].name != "超级用户帮助 [Superuser]"
|
||||
]:
|
||||
try:
|
||||
_plugin = nonebot.plugin.get_plugin(matcher.plugin_name)
|
||||
_module = _plugin.module
|
||||
try:
|
||||
plugin_name = _module.__getattribute__("__zx_plugin_name__")
|
||||
except AttributeError:
|
||||
continue
|
||||
is_superuser_usage = False
|
||||
try:
|
||||
_ = _module.__getattribute__("__plugin_superuser_usage__")
|
||||
is_superuser_usage = True
|
||||
except AttributeError:
|
||||
pass
|
||||
if (
|
||||
("[superuser]" in plugin_name.lower() or is_superuser_usage)
|
||||
and plugin_name != "超级用户帮助 [Superuser]"
|
||||
and plugin_name not in _plugin_name_list
|
||||
and "[hidden]" not in plugin_name.lower()
|
||||
):
|
||||
_plugin_name_list.append(plugin_name)
|
||||
try:
|
||||
plugin_des = _module.__getattribute__("__plugin_des__")
|
||||
except AttributeError:
|
||||
plugin_des = '_'
|
||||
plugin_cmd = _module.__getattribute__("__plugin_cmd__")
|
||||
if is_superuser_usage:
|
||||
plugin_cmd = [x for x in plugin_cmd if "[_superuser]" in x]
|
||||
plugin_cmd = " / ".join(plugin_cmd).replace('[_superuser]', '').strip()
|
||||
help_str += f"{plugin_des} -> {plugin_cmd}\n\n"
|
||||
x = tmp_img.getsize(f"{plugin_des} -> {plugin_cmd}")[0]
|
||||
width = width if width > x else x
|
||||
if plugin_data.plugin_type in [PluginType.SUPERUSER, PluginType.ADMIN]:
|
||||
usage = None
|
||||
if (
|
||||
plugin_data.plugin_type == PluginType.SUPERUSER
|
||||
and plugin_data.usage
|
||||
):
|
||||
usage = await text2image(
|
||||
plugin_data.usage, padding=5, color=(204, 196, 151)
|
||||
)
|
||||
if plugin_data.superuser_usage:
|
||||
usage = await text2image(
|
||||
plugin_data.superuser_usage, padding=5, color=(204, 196, 151)
|
||||
)
|
||||
if usage:
|
||||
await usage.acircle_corner()
|
||||
image = await help_template(plugin_data.name, usage)
|
||||
image_list.append(image)
|
||||
if plugin_data.task:
|
||||
for x in plugin_data.task.keys():
|
||||
task_list.append(plugin_data.task[x])
|
||||
except Exception as e:
|
||||
logger.warning(
|
||||
f"获取超级用户插件 {matcher.plugin_name}: {plugin_name} 设置失败... {type(e)}:{e}"
|
||||
f"获取超级用户插件 {plugin_data.model}: {plugin_data.name} 设置失败... {type(e)}:{e}"
|
||||
)
|
||||
height = len(help_str.split("\n")) * 33
|
||||
width += 500
|
||||
A = BuildImage(width, height, font_size=24)
|
||||
_background = BuildImage(width, height, background=background)
|
||||
A.text((300, 140), help_str)
|
||||
A.paste(_background, alpha=True)
|
||||
A.save(superuser_help_image)
|
||||
logger.info(f"已成功加载 {len(_plugin_name_list)} 条超级用户命令")
|
||||
task_str = "\n".join(task_list)
|
||||
task_str = "通过私聊 开启被动/关闭被动 + [被动名称] 来控制全局被动\n----------\n" + task_str
|
||||
task_image = await text2image(
|
||||
task_str, padding=5, color=(204, 196, 151)
|
||||
)
|
||||
task_image = await help_template("被动任务", task_image)
|
||||
image_list.append(task_image)
|
||||
image_group, _ = group_image(image_list)
|
||||
A = await build_sort_image(image_group, color="#f9f6f2", padding_top=180)
|
||||
await A.apaste(
|
||||
BuildImage(0, 0, font="CJGaoDeGuo.otf", plain_text="超级用户帮助", font_size=50),
|
||||
(50, 30),
|
||||
True,
|
||||
)
|
||||
await A.apaste(
|
||||
BuildImage(
|
||||
0,
|
||||
0,
|
||||
font="CJGaoDeGuo.otf",
|
||||
plain_text="注: ‘*’ 代表可有多个相同参数 ‘?’ 代表可省略该参数",
|
||||
font_size=30,
|
||||
font_color="red",
|
||||
),
|
||||
(50, 90),
|
||||
True,
|
||||
)
|
||||
await A.asave(SUPERUSER_HELP_IMAGE)
|
||||
logger.info(f"已成功加载 {len(image_list)} 条超级用户命令")
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from nonebot import on_command
|
||||
from utils.message_builder import image
|
||||
|
||||
from utils.message_builder import image
|
||||
|
||||
__zx_plugin_name__ = "更新信息"
|
||||
__plugin_usage__ = """
|
||||
@@ -26,8 +26,7 @@ update_info = on_command("更新信息", aliases={"更新日志"}, priority=5, b
|
||||
|
||||
@update_info.handle()
|
||||
async def _():
|
||||
img = image("update_info.png")
|
||||
if img:
|
||||
if img := image("update_info.png"):
|
||||
await update_info.finish(image("update_info.png"))
|
||||
else:
|
||||
await update_info.finish("目前没有更新信息哦")
|
||||
|
||||
@@ -12,6 +12,7 @@ driver.on_shutdown(disconnect)
|
||||
nonebot.load_plugin("nonebot_plugin_apscheduler")
|
||||
nonebot.load_plugins("basic_plugins")
|
||||
nonebot.load_plugins("plugins")
|
||||
nonebot.load_plugins("extensive_plugin")
|
||||
# 最后加载权限控制
|
||||
nonebot.load_plugins("basic_plugins/hooks")
|
||||
|
||||
|
||||
+15
-3
@@ -1,16 +1,27 @@
|
||||
from typing import Optional
|
||||
import platform
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
|
||||
from .utils import ConfigsManager
|
||||
|
||||
if platform.system() == "Linux":
|
||||
import os
|
||||
|
||||
hostip = (
|
||||
os.popen("cat /etc/resolv.conf | grep nameserver | awk '{ print $2 }'")
|
||||
.read()
|
||||
.replace("\n", "")
|
||||
)
|
||||
|
||||
|
||||
# 回复消息名称
|
||||
NICKNAME: str = "小真寻"
|
||||
|
||||
# 数据库(必要)
|
||||
# 如果填写了bind就不需要再填写后面的字段了#)
|
||||
# 示例:"bind": "postgresql://user:password@127.0.0.1:5432/database"
|
||||
# 示例:"bind": "postgres://user:password@127.0.0.1:5432/database"
|
||||
bind: str = "" # 数据库连接链接
|
||||
sql_name: str = "postgresql"
|
||||
sql_name: str = "postgres"
|
||||
user: str = "" # 数据用户名
|
||||
password: str = "" # 数据库密码
|
||||
address: str = "" # 数据库地址
|
||||
@@ -18,6 +29,7 @@ port: str = "" # 数据库端口
|
||||
database: str = "" # 数据库名称
|
||||
|
||||
# 代理,例如 "http://127.0.0.1:7890"
|
||||
# 如果是WLS 可以 f"http://{hostip}:7890" 使用寄主机的代理
|
||||
SYSTEM_PROXY: Optional[str] = None # 全局代理
|
||||
|
||||
|
||||
|
||||
@@ -15,6 +15,8 @@ FONT_PATH = Path() / "resources" / "font"
|
||||
DATA_PATH = Path() / "data"
|
||||
# 临时数据路径
|
||||
TEMP_PATH = Path() / "resources" / "temp"
|
||||
# 网页模板路径
|
||||
TEMPLATE_PATH = Path() / "resources" / "template"
|
||||
|
||||
|
||||
def load_path():
|
||||
|
||||
+208
-90
@@ -1,7 +1,52 @@
|
||||
from typing import Optional, Any, Union
|
||||
import copy
|
||||
from pathlib import Path
|
||||
from ruamel.yaml import YAML
|
||||
from typing import Any, Callable, Dict, Optional, Type, Union
|
||||
|
||||
import cattrs
|
||||
from pydantic import BaseModel
|
||||
from ruamel import yaml
|
||||
from ruamel.yaml import YAML
|
||||
from ruamel.yaml.scanner import ScannerError
|
||||
|
||||
from services.log import logger
|
||||
|
||||
|
||||
class Config(BaseModel):
|
||||
|
||||
"""
|
||||
配置项
|
||||
"""
|
||||
|
||||
value: Any
|
||||
"""配置项值"""
|
||||
name: Optional[str]
|
||||
"""插件名称"""
|
||||
help: Optional[str]
|
||||
"""配置注解"""
|
||||
default_value: Optional[Any] = None
|
||||
"""默认值"""
|
||||
level_module: Optional[str]
|
||||
"""受权限模块"""
|
||||
type: Any = None
|
||||
"""参数类型"""
|
||||
arg_parser: Optional[Callable] = None
|
||||
"""参数解析"""
|
||||
|
||||
|
||||
class ConfigGroup(BaseModel):
|
||||
|
||||
"""
|
||||
配置组
|
||||
"""
|
||||
|
||||
module: str
|
||||
"""模块名"""
|
||||
configs: Dict[str, Config] = {}
|
||||
"""配置项列表"""
|
||||
|
||||
|
||||
class NoSuchConfig(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class ConfigsManager:
|
||||
@@ -10,20 +55,25 @@ class ConfigsManager:
|
||||
"""
|
||||
|
||||
def __init__(self, file: Path):
|
||||
self._data: dict = {}
|
||||
self._data: Dict[str, ConfigGroup] = {}
|
||||
self._simple_data: dict = {}
|
||||
self._admin_level_data = []
|
||||
self._simple_file = Path() / "configs" / "config.yaml"
|
||||
_yaml = YAML()
|
||||
if file:
|
||||
file.parent.mkdir(exist_ok=True, parents=True)
|
||||
self.file = file
|
||||
_yaml = YAML()
|
||||
if file.exists():
|
||||
with open(file, "r", encoding="utf8") as f:
|
||||
self._data = _yaml.load(f)
|
||||
if self._simple_file.exists():
|
||||
self.load_data()
|
||||
if self._simple_file.exists():
|
||||
try:
|
||||
with open(self._simple_file, "r", encoding="utf8") as f:
|
||||
self._simple_data = _yaml.load(f)
|
||||
except ScannerError as e:
|
||||
raise ScannerError(
|
||||
f"{e}\n**********************************************\n"
|
||||
f"****** 可能为config.yaml配置文件填写不规范 ******\n"
|
||||
f"**********************************************"
|
||||
)
|
||||
|
||||
def add_plugin_config(
|
||||
self,
|
||||
@@ -33,7 +83,9 @@ class ConfigsManager:
|
||||
*,
|
||||
name: Optional[str] = None,
|
||||
help_: Optional[str] = None,
|
||||
default_value: Optional[str] = None,
|
||||
default_value: Optional[Any] = None,
|
||||
type: Optional[Type] = None,
|
||||
arg_parser: Optional[Callable] = None,
|
||||
_override: bool = False,
|
||||
):
|
||||
"""
|
||||
@@ -44,115 +96,138 @@ class ConfigsManager:
|
||||
:param name: 插件名称
|
||||
:param help_: 配置注解
|
||||
:param default_value: 默认值
|
||||
:param _override: 覆盖前值
|
||||
:param _override: 强制覆盖值
|
||||
"""
|
||||
if (
|
||||
not (module in self._data.keys() and self._data[module].get(key))
|
||||
or _override
|
||||
):
|
||||
if not module or not key:
|
||||
raise ValueError("add_plugin_config: module和key不能为为空")
|
||||
if module in self._data and (config := self._data[module].configs.get(key)):
|
||||
config.help = help_
|
||||
config.arg_parser = arg_parser
|
||||
config.type = type
|
||||
if _override:
|
||||
config.value = value
|
||||
config.name = name
|
||||
config.default_value = default_value
|
||||
else:
|
||||
_module = None
|
||||
if ":" in module:
|
||||
module = module.split(":")
|
||||
_module = module[-1]
|
||||
module = module[0]
|
||||
module_split = module.split(":")
|
||||
if len(module_split) < 2:
|
||||
raise ValueError(f"module: {module} 填写错误")
|
||||
_module = module_split[-1]
|
||||
module = module_split[0]
|
||||
if "[LEVEL]" in key and _module:
|
||||
key = key.replace("[LEVEL]", "").strip()
|
||||
self._admin_level_data.append((_module, value))
|
||||
if self._data.get(module) is None:
|
||||
self._data[module] = {}
|
||||
key = key.upper()
|
||||
self._data[module][key] = {
|
||||
"value": value,
|
||||
"name": name.strip() if isinstance(name, str) else name,
|
||||
"help": help_.strip() if isinstance(help_, str) else help_,
|
||||
"default_value": default_value,
|
||||
"level_module": _module,
|
||||
}
|
||||
if not self._data.get(module):
|
||||
self._data[module] = ConfigGroup(module=module)
|
||||
self._data[module].configs[key] = Config(
|
||||
value=value,
|
||||
name=name,
|
||||
help=help_,
|
||||
default_value=default_value,
|
||||
level_module=_module,
|
||||
type=type,
|
||||
)
|
||||
|
||||
def remove_plugin_config(self, module: str):
|
||||
"""
|
||||
为插件删除一个配置
|
||||
:param module: 模块名
|
||||
"""
|
||||
if module in self._data.keys():
|
||||
del self._data[module]
|
||||
self.save()
|
||||
|
||||
def set_config(self, module: str, key: str, value: str):
|
||||
def set_config(
|
||||
self,
|
||||
module: str,
|
||||
key: str,
|
||||
value: Any,
|
||||
auto_save: bool = False,
|
||||
save_simple_data: bool = True,
|
||||
):
|
||||
"""
|
||||
设置配置值
|
||||
:param module: 模块名
|
||||
:param key: 配置名称
|
||||
:param value: 值
|
||||
:param auto_save: 自动保存
|
||||
:param save_simple_data: 保存至config.yaml
|
||||
"""
|
||||
if module in self._data.keys():
|
||||
if self._data[module].get(key) is not None and self._data[module][key] != value:
|
||||
self._data[module][key]["value"] = value
|
||||
if module in self._data:
|
||||
if (
|
||||
self._data[module].configs.get(key)
|
||||
and self._data[module].configs[key] != value
|
||||
):
|
||||
self._data[module].configs[key].value = value
|
||||
self._simple_data[module][key] = value
|
||||
self.save()
|
||||
if auto_save:
|
||||
self.save(save_simple_data=save_simple_data)
|
||||
|
||||
def set_help(self, module: str, key: str, help_: str):
|
||||
"""
|
||||
设置配置注释
|
||||
:param module: 模块名
|
||||
:param key: 配置名称
|
||||
:param help_: 注释文本
|
||||
"""
|
||||
if module in self._data.keys():
|
||||
if self._data[module].get(key) is not None:
|
||||
self._data[module][key]["help"] = help_
|
||||
self.save()
|
||||
|
||||
def set_default_value(self, module: str, key: str, value: str):
|
||||
"""
|
||||
设置配置默认值
|
||||
:param module: 模块名
|
||||
:param key: 配置名称
|
||||
:param value: 值
|
||||
"""
|
||||
if module in self._data.keys():
|
||||
if self._data[module].get(key) is not None:
|
||||
self._data[module][key]["default_value"] = value
|
||||
self.save()
|
||||
|
||||
def get_config(self, module: str, key: str, default: Optional[Any] = None) -> Optional[Any]:
|
||||
def get_config(
|
||||
self, module: str, key: str, default: Optional[Any] = None
|
||||
) -> Optional[Any]:
|
||||
"""
|
||||
获取指定配置值
|
||||
:param module: 模块名
|
||||
:param key: 配置名称
|
||||
:param default: 没有key值内容的默认返回值
|
||||
"""
|
||||
logger.debug(
|
||||
f"尝试获取配置 MODULE: [<u><y>{module}</y></u>] | KEY: [<u><y>{key}</y></u>]"
|
||||
)
|
||||
key = key.upper()
|
||||
value = None
|
||||
if module in self._data.keys():
|
||||
for key in [key, f"{key} [LEVEL]"]:
|
||||
if self._data[module].get(key) is not None:
|
||||
if self._data[module][key]["value"] is None:
|
||||
return self._data[module][key]["default_value"]
|
||||
return self._data[module][key]["value"]
|
||||
if default is not None:
|
||||
return default
|
||||
return None
|
||||
config = self._data[module].configs.get(key)
|
||||
if not config:
|
||||
config = self._data[module].configs.get(f"{key} [LEVEL]")
|
||||
if not config:
|
||||
raise NoSuchConfig(f"未查询到配置项 MODULE: [ {module} ] | KEY: [ {key} ]")
|
||||
if config.arg_parser:
|
||||
value = config.arg_parser(value or config.default_value)
|
||||
else:
|
||||
try:
|
||||
if config.value is not None:
|
||||
value = (
|
||||
cattrs.structure(config.value, config.type)
|
||||
if config.type
|
||||
else config.value
|
||||
)
|
||||
else:
|
||||
if config.default_value is not None:
|
||||
value = (
|
||||
cattrs.structure(config.default_value, config.type)
|
||||
if config.type
|
||||
else config.default_value
|
||||
)
|
||||
except Exception as e:
|
||||
logger.warning(
|
||||
f"配置项类型转换 MODULE: [<u><y>{module}</y></u>] | KEY: [<u><y>{key}</y></u>]",
|
||||
e=e,
|
||||
)
|
||||
value = config.value or config.default_value
|
||||
if value is None:
|
||||
value = default
|
||||
logger.debug(
|
||||
f"获取配置 MODULE: [<u><y>{module}</y></u>] | KEY: [<u><y>{key}</y></u>] -> [<u><c>{value}</c></u>]"
|
||||
)
|
||||
return value
|
||||
|
||||
def get_level2module(self, module: str, key: str) -> Optional[str]:
|
||||
"""
|
||||
获取指定key所绑定的module,一般为权限等级
|
||||
获取指定key所绑定的module,一般为权限等级
|
||||
:param module: 模块名
|
||||
:param key: 配置名称
|
||||
:return:
|
||||
"""
|
||||
if self._data.get(module) is not None:
|
||||
if self._data[module].get(key) is not None:
|
||||
return self._data[module][key].get("level_module")
|
||||
if config := self._data[module].configs.get(key):
|
||||
return config.level_module
|
||||
|
||||
def get(self, key: str):
|
||||
def get(self, key: str) -> Optional[ConfigGroup]:
|
||||
"""
|
||||
获取插件配置数据
|
||||
:param key: 名称
|
||||
"""
|
||||
if key in self._data.keys():
|
||||
return self._data[key]
|
||||
return self._data.get(key)
|
||||
|
||||
def save(self, path: Union[str, Path] = None, save_simple_data: bool = False):
|
||||
def save(
|
||||
self, path: Optional[Union[str, Path]] = None, save_simple_data: bool = False
|
||||
):
|
||||
"""
|
||||
保存数据
|
||||
:param path: 路径
|
||||
@@ -161,12 +236,23 @@ class ConfigsManager:
|
||||
if save_simple_data:
|
||||
with open(self._simple_file, "w", encoding="utf8") as f:
|
||||
yaml.dump(
|
||||
self._simple_data, f, indent=2, Dumper=yaml.RoundTripDumper, allow_unicode=True
|
||||
self._simple_data,
|
||||
f,
|
||||
indent=2,
|
||||
Dumper=yaml.RoundTripDumper,
|
||||
allow_unicode=True,
|
||||
)
|
||||
path = path if path else self.file
|
||||
path = path or self.file
|
||||
data = {}
|
||||
for module in self._data:
|
||||
data[module] = {}
|
||||
for config in self._data[module].configs:
|
||||
value = self._data[module].configs[config].dict()
|
||||
del value["type"]
|
||||
data[module][config] = value
|
||||
with open(path, "w", encoding="utf8") as f:
|
||||
yaml.dump(
|
||||
self._data, f, indent=2, Dumper=yaml.RoundTripDumper, allow_unicode=True
|
||||
data, f, indent=2, Dumper=yaml.RoundTripDumper, allow_unicode=True
|
||||
)
|
||||
|
||||
def reload(self):
|
||||
@@ -174,21 +260,53 @@ class ConfigsManager:
|
||||
重新加载配置文件
|
||||
"""
|
||||
_yaml = YAML()
|
||||
temp_file = Path() / "configs" / "config.yaml"
|
||||
if temp_file.exists():
|
||||
with open(temp_file, "r", encoding="utf8") as f:
|
||||
temp = _yaml.load(f)
|
||||
for key in temp.keys():
|
||||
for k in temp[key].keys():
|
||||
self._data[key][k]["value"] = temp[key][k]
|
||||
if self._simple_file.exists():
|
||||
with open(self._simple_file, "r", encoding="utf8") as f:
|
||||
self._simple_data = _yaml.load(f)
|
||||
for key in self._simple_data.keys():
|
||||
for k in self._simple_data[key].keys():
|
||||
self._data[key].configs[k].value = self._simple_data[key][k]
|
||||
self.save()
|
||||
|
||||
def load_data(self):
|
||||
"""
|
||||
加载数据
|
||||
|
||||
Raises:
|
||||
ValueError: _description_
|
||||
"""
|
||||
if self.file.exists():
|
||||
_yaml = YAML()
|
||||
with open(self.file, "r", encoding="utf8") as f:
|
||||
temp_data = _yaml.load(f)
|
||||
if not temp_data:
|
||||
self.file.unlink()
|
||||
raise ValueError(
|
||||
"配置文件为空!\n"
|
||||
"***********************************************************\n"
|
||||
"****** 配置文件 plugins2config.yaml 为空,已删除,请重启 ******\n"
|
||||
"***********************************************************"
|
||||
)
|
||||
count = 0
|
||||
for module in temp_data:
|
||||
config_group = ConfigGroup(module=module)
|
||||
for config in temp_data[module]:
|
||||
config_group.configs[config] = Config(**temp_data[module][config])
|
||||
count += 1
|
||||
self._data[module] = config_group
|
||||
logger.info(
|
||||
f"加载配置完成,共加载 <u><y>{len(temp_data)}</y></u> 个配置组及对应 <u><y>{count}</y></u> 个配置项"
|
||||
)
|
||||
|
||||
def get_admin_level_data(self):
|
||||
"""
|
||||
获取管理插件等级
|
||||
"""
|
||||
return self._admin_level_data
|
||||
|
||||
def get_data(self) -> Dict[str, ConfigGroup]:
|
||||
return copy.deepcopy(self._data)
|
||||
|
||||
def is_empty(self) -> bool:
|
||||
return not bool(self._data)
|
||||
|
||||
|
||||
+1450
-155
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,66 @@
|
||||
{
|
||||
"抽卡":{
|
||||
"buff": ["欧气满满,十连出金","出金不歪"],
|
||||
"debuff": ["武器大师","保底出金","金色会是痛苦大剑"]
|
||||
},
|
||||
"刷世界boss":{
|
||||
"buff": ["双攻双爆角斗士"],
|
||||
"debuff": ["只有保底材料","贪生怕死角斗士"]
|
||||
},
|
||||
"刷风本":{
|
||||
"buff": ["会有极品猎人套","会掉真正的少女心","治疗加成少女头"],
|
||||
"debuff": ["勇往直前少女心","少女飘摇的杀意","少女暴怒的容颜"]
|
||||
},
|
||||
"刷火本":{
|
||||
"buff": ["魔女帽子火伤杯","暴伤魔女帽!","火伤魔女心!"],
|
||||
"debuff": ["幡 然 醒 悟","这么阴间的地方真的会有魔女套吗?","不务正业火魔女","会匹配到3个卢姥爷"]
|
||||
},
|
||||
"刷岩本":{
|
||||
"buff": ["悠久的磐岩伴你左右","岩神的庇护常在"],
|
||||
"debuff": ["防御流星杯,你值得拥有"]
|
||||
},
|
||||
"刷宗室":{
|
||||
"buff": ["物理伤害骑士道,元素精通宗室套"],
|
||||
"debuff": ["贪生怕死骑士道,物理伤害宗室杯"]
|
||||
},
|
||||
"刷冰本":{
|
||||
"buff": ["双暴词条概率up"],
|
||||
"debuff": ["防御力船帽,无人可及"]
|
||||
},
|
||||
"刷雷本":{
|
||||
"buff": ["愿雷鸟伴你左右"],
|
||||
"debuff": ["来表演一个只掉平雷套的绝活","风神忽悠雷凶兆"]
|
||||
},
|
||||
"锄大地":{
|
||||
"buff": ["会掉一大堆紫色材料"],
|
||||
"debuff": ["深渊法师爱你哟","会被冰水法控到死"]
|
||||
},
|
||||
"挖矿":{
|
||||
"buff": ["开矿出双材料"],
|
||||
"debuff": ["去别人世界会被拒"]
|
||||
},
|
||||
"刷天赋本":{
|
||||
"buff": ["金色!我看到了金色的书!"],
|
||||
"debuff": ["2蓝2绿不会变"]
|
||||
},
|
||||
"刷突破材料":{
|
||||
"buff": ["金色!我看到了金色的材料!"],
|
||||
"debuff": ["2蓝2绿不会变"]
|
||||
},
|
||||
"升级圣遗物":{
|
||||
"buff": ["稀有词条跳跳跳","会双爆拉满"],
|
||||
"debuff": ["女 仆 狂 喜","无中生有防御力","生命拉满","完美避开双爆"]
|
||||
},
|
||||
"打风魔龙":{
|
||||
"buff": ["看我一箭一个风魔鸡","今天特瓦林可以给想要的突破材料","5金加原胚!"],
|
||||
"debuff": ["会不小心掉下平台","不小心被地板烫死了"]
|
||||
},
|
||||
"打狼王":{
|
||||
"buff": ["今天安德琉斯的心情不错,可以py一下","5金加原胚!"],
|
||||
"debuff": ["狼尾巴*1"]
|
||||
},
|
||||
"打公子":{
|
||||
"buff": ["今天可以和公子py想要的突破材料","5金加原胚!"],
|
||||
"debuff": ["要角没有!要命一条!"]
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 772 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 799 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 2.3 MiB |
Binary file not shown.
|
After Width: | Height: | Size: 694 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 694 KiB After Width: | Height: | Size: 326 KiB |
+96
-153
@@ -1,40 +1,48 @@
|
||||
from services.db_context import db
|
||||
from typing import Dict
|
||||
from typing import Optional, List
|
||||
from services.log import logger
|
||||
|
||||
from tortoise import fields
|
||||
|
||||
from services.db_context import Model
|
||||
|
||||
from .goods_info import GoodsInfo
|
||||
|
||||
|
||||
class BagUser(db.Model):
|
||||
__tablename__ = "bag_users"
|
||||
id = db.Column(db.Integer(), primary_key=True)
|
||||
user_qq = db.Column(db.BigInteger(), nullable=False)
|
||||
group_id = db.Column(db.BigInteger(), nullable=False)
|
||||
gold = db.Column(db.Integer(), default=100)
|
||||
props = db.Column(db.TEXT(), nullable=False, default="") # 旧道具字段(废弃)
|
||||
spend_total_gold = db.Column(db.Integer(), default=0)
|
||||
get_total_gold = db.Column(db.Integer(), default=0)
|
||||
get_today_gold = db.Column(db.Integer(), default=0)
|
||||
spend_today_gold = db.Column(db.Integer(), default=0)
|
||||
property = db.Column(db.JSON(), nullable=False, default={}) # 新道具字段
|
||||
class BagUser(Model):
|
||||
|
||||
_idx1 = db.Index("bag_group_users_idx1", "user_qq", "group_id", unique=True)
|
||||
id = fields.IntField(pk=True, generated=True, auto_increment=True)
|
||||
"""自增id"""
|
||||
user_qq = fields.BigIntField()
|
||||
"""用户id"""
|
||||
group_id = fields.BigIntField()
|
||||
"""群聊id"""
|
||||
gold = fields.IntField(default=100)
|
||||
"""金币数量"""
|
||||
spend_total_gold = fields.IntField(default=0)
|
||||
"""花费金币总数"""
|
||||
get_total_gold = fields.IntField(default=0)
|
||||
"""获取金币总数"""
|
||||
get_today_gold = fields.IntField(default=0)
|
||||
"""今日获取金币"""
|
||||
spend_today_gold = fields.IntField(default=0)
|
||||
"""今日获取金币"""
|
||||
property: Dict[str, int] = fields.JSONField(default={})
|
||||
"""道具"""
|
||||
|
||||
class Meta:
|
||||
table = "bag_users"
|
||||
table_description = "用户道具数据表"
|
||||
unique_together = ("user_qq", "group_id")
|
||||
|
||||
@classmethod
|
||||
async def get_user_total_gold(cls, user_qq: int, group_id: int) -> str:
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
获取金币概况
|
||||
参数:
|
||||
参数:
|
||||
:param user_qq: qq号
|
||||
:param group_id: 所在群号
|
||||
"""
|
||||
query = cls.query.where((cls.user_qq == user_qq) & (cls.group_id == group_id))
|
||||
user = await query.gino.first()
|
||||
if not user:
|
||||
user = await cls.create(
|
||||
user_qq=user_qq,
|
||||
group_id=group_id,
|
||||
)
|
||||
user, _ = await cls.get_or_create(user_qq=user_qq, group_id=group_id)
|
||||
return (
|
||||
f"当前金币:{user.gold}\n今日获取金币:{user.get_today_gold}\n今日花费金币:{user.spend_today_gold}"
|
||||
f"\n今日收益:{user.get_today_gold - user.spend_today_gold}"
|
||||
@@ -44,183 +52,118 @@ class BagUser(db.Model):
|
||||
@classmethod
|
||||
async def get_gold(cls, user_qq: int, group_id: int) -> int:
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
获取当前金币
|
||||
参数:
|
||||
参数:
|
||||
:param user_qq: qq号
|
||||
:param group_id: 所在群号
|
||||
"""
|
||||
query = cls.query.where((cls.user_qq == user_qq) & (cls.group_id == group_id))
|
||||
user = await query.gino.first()
|
||||
if user:
|
||||
return user.gold
|
||||
else:
|
||||
await cls.create(
|
||||
user_qq=user_qq,
|
||||
group_id=group_id,
|
||||
)
|
||||
return 100
|
||||
user, _ = await cls.get_or_create(user_qq=user_qq, group_id=group_id)
|
||||
return user.gold
|
||||
|
||||
@classmethod
|
||||
async def get_property(cls, user_qq: int, group_id: int) -> Dict[str, int]:
|
||||
async def get_property(
|
||||
cls, user_qq: int, group_id: int, only_active: bool = False
|
||||
) -> Dict[str, int]:
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
获取当前道具
|
||||
参数:
|
||||
参数:
|
||||
:param user_qq: qq号
|
||||
:param group_id: 所在群号
|
||||
:param only_active: 仅仅获取主动使用的道具
|
||||
"""
|
||||
query = cls.query.where((cls.user_qq == user_qq) & (cls.group_id == group_id))
|
||||
user = await query.gino.first()
|
||||
if user:
|
||||
return user.property
|
||||
else:
|
||||
await cls.create(
|
||||
user_qq=user_qq,
|
||||
group_id=group_id,
|
||||
)
|
||||
return {}
|
||||
user, _ = await cls.get_or_create(user_qq=user_qq, group_id=group_id)
|
||||
if only_active and user.property:
|
||||
data = {}
|
||||
name_list = [
|
||||
x.goods_name
|
||||
for x in await GoodsInfo.get_all_goods()
|
||||
if not x.is_passive
|
||||
]
|
||||
for key in [x for x in user.property if x in name_list]:
|
||||
data[key] = user.property[key]
|
||||
return data
|
||||
return user.property
|
||||
|
||||
@classmethod
|
||||
async def add_gold(cls, user_qq: int, group_id: int, num: int):
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
增加金币
|
||||
参数:
|
||||
参数:
|
||||
:param user_qq: qq号
|
||||
:param group_id: 所在群号
|
||||
:param num: 金币数量
|
||||
"""
|
||||
query = cls.query.where((cls.user_qq == user_qq) & (cls.group_id == group_id))
|
||||
query = query.with_for_update()
|
||||
user = await query.gino.first()
|
||||
if user:
|
||||
await user.update(
|
||||
gold=user.gold + num,
|
||||
get_total_gold=user.get_total_gold + num,
|
||||
get_today_gold=user.get_today_gold + num,
|
||||
).apply()
|
||||
else:
|
||||
await cls.create(
|
||||
user_qq=user_qq,
|
||||
group_id=group_id,
|
||||
gold=100 + num,
|
||||
get_total_gold=num,
|
||||
get_today_gold=num,
|
||||
)
|
||||
user, _ = await cls.get_or_create(user_qq=user_qq, group_id=group_id)
|
||||
user.gold = user.gold + num
|
||||
user.get_total_gold = user.get_total_gold + num
|
||||
user.get_today_gold = user.get_today_gold + num
|
||||
await user.save(update_fields=["gold", "get_today_gold", "get_total_gold"])
|
||||
|
||||
@classmethod
|
||||
async def spend_gold(cls, user_qq: int, group_id: int, num: int):
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
花费金币
|
||||
参数:
|
||||
参数:
|
||||
:param user_qq: qq号
|
||||
:param group_id: 所在群号
|
||||
:param num: 金币数量
|
||||
"""
|
||||
query = cls.query.where((cls.user_qq == user_qq) & (cls.group_id == group_id))
|
||||
query = query.with_for_update()
|
||||
user = await query.gino.first()
|
||||
if user:
|
||||
await user.update(
|
||||
gold=user.gold - num,
|
||||
spend_total_gold=user.spend_total_gold + num,
|
||||
spend_today_gold=user.spend_today_gold + num,
|
||||
).apply()
|
||||
else:
|
||||
await cls.create(
|
||||
user_qq=user_qq,
|
||||
group_id=group_id,
|
||||
gold=100 - num,
|
||||
spend_total_gold=num,
|
||||
spend_today_gold=num,
|
||||
)
|
||||
user, _ = await cls.get_or_create(user_qq=user_qq, group_id=group_id)
|
||||
user.gold = user.gold - num
|
||||
user.spend_total_gold = user.spend_total_gold + num
|
||||
user.spend_today_gold = user.spend_today_gold + num
|
||||
await user.save(update_fields=["gold", "spend_total_gold", "spend_today_gold"])
|
||||
|
||||
@classmethod
|
||||
async def add_property(cls, user_qq: int, group_id: int, name: str):
|
||||
async def add_property(cls, user_qq: int, group_id: int, name: str, num: int = 1):
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
增加道具
|
||||
参数:
|
||||
参数:
|
||||
:param user_qq: qq号
|
||||
:param group_id: 所在群号
|
||||
:param name: 道具名称
|
||||
:param num: 道具数量
|
||||
"""
|
||||
query = cls.query.where((cls.user_qq == user_qq) & (cls.group_id == group_id))
|
||||
query = query.with_for_update()
|
||||
user = await query.gino.first()
|
||||
if user:
|
||||
p = user.property
|
||||
if p.get(name) is None:
|
||||
p[name] = 1
|
||||
else:
|
||||
p[name] += 1
|
||||
await user.update(property=p).apply()
|
||||
else:
|
||||
await cls.create(user_qq=user_qq, group_id=group_id, property={name: 1})
|
||||
user, _ = await cls.get_or_create(user_qq=user_qq, group_id=group_id)
|
||||
property_ = user.property
|
||||
if property_.get(name) is None:
|
||||
property_[name] = 0
|
||||
property_[name] += num
|
||||
user.property = property_
|
||||
await user.save(update_fields=["property"])
|
||||
|
||||
@classmethod
|
||||
async def delete_property(
|
||||
cls, user_qq: int, group_id: int, name: str, num: int = 1
|
||||
) -> bool:
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
使用/删除 道具
|
||||
参数:
|
||||
参数:
|
||||
:param user_qq: qq号
|
||||
:param group_id: 所在群号
|
||||
:param name: 道具名称
|
||||
:param num: 使用个数
|
||||
"""
|
||||
query = cls.query.where((cls.user_qq == user_qq) & (cls.group_id == group_id))
|
||||
query = query.with_for_update()
|
||||
user = await query.gino.first()
|
||||
if user:
|
||||
property_ = user.property
|
||||
if name in property_:
|
||||
if property_.get(name) == num:
|
||||
del property_[name]
|
||||
else:
|
||||
property_[name] -= num
|
||||
await user.update(property=property_).apply()
|
||||
return True
|
||||
user, _ = await cls.get_or_create(user_qq=user_qq, group_id=group_id)
|
||||
property_ = user.property
|
||||
if name in property_:
|
||||
if (n := property_.get(name, 0)) < num:
|
||||
return False
|
||||
if n == num:
|
||||
del property_[name]
|
||||
else:
|
||||
property_[name] -= num
|
||||
await user.save(update_fields=["property"])
|
||||
return True
|
||||
return False
|
||||
|
||||
@classmethod
|
||||
async def buy_property(
|
||||
cls, user_qq: int, group_id: int, goods: "GoodsInfo", goods_num: int
|
||||
) -> bool:
|
||||
"""
|
||||
说明:
|
||||
购买道具
|
||||
参数:
|
||||
:param user_qq: 用户qq
|
||||
:param group_id: 所在群聊
|
||||
:param goods: 商品
|
||||
:param goods_num: 商品数量
|
||||
"""
|
||||
try:
|
||||
# 折扣后金币
|
||||
spend_gold = goods.goods_discount * goods.goods_price * goods_num
|
||||
await BagUser.spend_gold(user_qq, group_id, spend_gold)
|
||||
for _ in range(goods_num):
|
||||
await BagUser.add_property(user_qq, group_id, goods.goods_name)
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.error(f"buy_property 发生错误 {type(e)}:{e}")
|
||||
return False
|
||||
|
||||
@classmethod
|
||||
async def get_all_users(cls, group_id: Optional[int] = None) -> List["BagUser"]:
|
||||
"""
|
||||
说明:
|
||||
获取所有用户数据
|
||||
参数:
|
||||
:param group_id: 群号
|
||||
"""
|
||||
if not group_id:
|
||||
query = await cls.query.gino.all()
|
||||
else:
|
||||
query = await cls.query.where((cls.group_id == group_id)).gino.all()
|
||||
return query
|
||||
async def _run_script(cls):
|
||||
await cls.raw("ALTER TABLE bag_users DROP props;")
|
||||
"""删除 props 字段"""
|
||||
|
||||
+70
-65
@@ -1,119 +1,124 @@
|
||||
from services.db_context import db
|
||||
import time
|
||||
from typing import Union
|
||||
|
||||
from tortoise import fields
|
||||
|
||||
from services.db_context import Model
|
||||
from services.log import logger
|
||||
|
||||
|
||||
class BanUser(db.Model):
|
||||
__tablename__ = "ban_users"
|
||||
class BanUser(Model):
|
||||
|
||||
user_qq = db.Column(db.BigInteger(), nullable=False, primary_key=True)
|
||||
ban_level = db.Column(db.Integer(), nullable=False)
|
||||
ban_time = db.Column(db.BigInteger())
|
||||
duration = db.Column(db.BigInteger())
|
||||
user_qq = fields.IntField(pk=True)
|
||||
"""用户id"""
|
||||
ban_level = fields.IntField()
|
||||
"""使用ban命令的用户等级"""
|
||||
ban_time = fields.BigIntField()
|
||||
"""ban开始的时间"""
|
||||
duration = fields.BigIntField()
|
||||
"""ban时长"""
|
||||
|
||||
_idx1 = db.Index("ban_group_users_idx1", "user_qq", unique=True)
|
||||
class Meta:
|
||||
table = "ban_users"
|
||||
table_description = ".ban/b了 封禁人员数据表"
|
||||
|
||||
@classmethod
|
||||
async def check_ban_level(cls, user_qq: int, level: int) -> bool:
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
检测ban掉目标的用户与unban用户的权限等级大小
|
||||
参数:
|
||||
参数:
|
||||
:param user_qq: unban用户的qq号
|
||||
:param level: ban掉目标用户的权限等级
|
||||
"""
|
||||
user = await cls.query.where((cls.user_qq == user_qq)).gino.first()
|
||||
if not user:
|
||||
return False
|
||||
if user.ban_level > level:
|
||||
return True
|
||||
user = await cls.filter(user_qq=user_qq).first()
|
||||
if user:
|
||||
logger.debug(
|
||||
f"检测用户被ban等级,user_level: {user.ban_level},level: {level}",
|
||||
target=user_qq,
|
||||
)
|
||||
return bool(user and user.ban_level > level)
|
||||
return False
|
||||
|
||||
@classmethod
|
||||
async def check_ban_time(cls, user_qq: int) -> str:
|
||||
async def check_ban_time(cls, user_qq: int) -> Union[str, int]:
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
检测用户被ban时长
|
||||
参数:
|
||||
参数:
|
||||
:param user_qq: qq号
|
||||
"""
|
||||
query = cls.query.where((cls.user_qq == user_qq))
|
||||
user = await query.gino.first()
|
||||
if not user:
|
||||
return ""
|
||||
if time.time() - (user.ban_time + user.duration) > 0 and user.duration != -1:
|
||||
return ""
|
||||
if user.duration == -1:
|
||||
return "∞"
|
||||
return time.time() - user.ban_time - user.duration
|
||||
logger.debug(f"获取用户ban时长", target=user_qq)
|
||||
if user := await cls.filter(user_qq=user_qq).first():
|
||||
if (
|
||||
time.time() - (user.ban_time + user.duration) > 0
|
||||
and user.duration != -1
|
||||
):
|
||||
return ""
|
||||
if user.duration == -1:
|
||||
return "∞"
|
||||
return int(time.time() - user.ban_time - user.duration)
|
||||
return ""
|
||||
|
||||
@classmethod
|
||||
async def is_ban(cls, user_qq: int) -> bool:
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
判断用户是否被ban
|
||||
参数:
|
||||
参数:
|
||||
:param user_qq: qq号
|
||||
"""
|
||||
logger.debug(f"检测是否被ban", target=user_qq)
|
||||
if await cls.check_ban_time(user_qq):
|
||||
return True
|
||||
else:
|
||||
await cls.unban(user_qq)
|
||||
return False
|
||||
return False
|
||||
|
||||
@classmethod
|
||||
async def is_super_ban(cls, user_qq: int) -> bool:
|
||||
"""
|
||||
说明:
|
||||
判断用户是否被ban
|
||||
参数:
|
||||
说明:
|
||||
判断用户是否被超级用户ban / b了
|
||||
参数:
|
||||
:param user_qq: qq号
|
||||
"""
|
||||
user = await cls.query.where((cls.user_qq == user_qq)).gino.first()
|
||||
if not user:
|
||||
return False
|
||||
if user.ban_level == 10:
|
||||
return True
|
||||
logger.debug(f"检测是否被超级用户权限封禁", target=user_qq)
|
||||
if user := await cls.filter(user_qq=user_qq).first():
|
||||
if user.ban_level == 10:
|
||||
return True
|
||||
return False
|
||||
|
||||
@classmethod
|
||||
async def ban(cls, user_qq: int, ban_level: int, duration: int) -> bool:
|
||||
async def ban(cls, user_qq: int, ban_level: int, duration: int):
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
ban掉目标用户
|
||||
参数:
|
||||
参数:
|
||||
:param user_qq: 目标用户qq号
|
||||
:param ban_level: 使用ban命令用户的权限
|
||||
:param duration: ban时长,秒
|
||||
"""
|
||||
query = cls.query.where((cls.user_qq == user_qq))
|
||||
query = query.with_for_update()
|
||||
user = await query.gino.first()
|
||||
if not await cls.check_ban_time(user_qq):
|
||||
logger.debug(f"封禁用户,等级:{ban_level},时长: {duration}", target=user_qq)
|
||||
if await cls.filter(user_qq=user_qq).first():
|
||||
await cls.unban(user_qq)
|
||||
user = None
|
||||
if user is None:
|
||||
await cls.create(
|
||||
user_qq=user_qq,
|
||||
ban_level=ban_level,
|
||||
ban_time=time.time(),
|
||||
duration=duration,
|
||||
)
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
await cls.create(
|
||||
user_qq=user_qq,
|
||||
ban_level=ban_level,
|
||||
ban_time=time.time(),
|
||||
duration=duration,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
async def unban(cls, user_qq: int) -> bool:
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
unban用户
|
||||
参数:
|
||||
参数:
|
||||
:param user_qq: qq号
|
||||
"""
|
||||
query = cls.query.where((cls.user_qq == user_qq))
|
||||
query = query.with_for_update()
|
||||
user = await query.gino.first()
|
||||
if user is None:
|
||||
return False
|
||||
else:
|
||||
await cls.delete.where((cls.user_qq == user_qq)).gino.status()
|
||||
if user := await cls.filter(user_qq=user_qq).first():
|
||||
logger.debug("解除封禁", target=user_qq)
|
||||
await user.delete()
|
||||
return True
|
||||
return False
|
||||
|
||||
+63
-160
@@ -1,80 +1,30 @@
|
||||
from datetime import datetime, timedelta
|
||||
from typing import List, Literal, Optional, Tuple, Union
|
||||
from typing import Any, List, Literal, Optional, Tuple, Union
|
||||
|
||||
from services.db_context import db
|
||||
from tortoise import fields
|
||||
from tortoise.functions import Count
|
||||
|
||||
from services.db_context import Model
|
||||
|
||||
|
||||
class ChatHistory(db.Model):
|
||||
__tablename__ = "chat_history"
|
||||
class ChatHistory(Model):
|
||||
|
||||
id = db.Column(db.Integer(), primary_key=True)
|
||||
user_qq = db.Column(db.BigInteger(), nullable=False)
|
||||
group_id = db.Column(db.BigInteger())
|
||||
text = db.Column(db.Text())
|
||||
create_time = db.Column(db.DateTime(timezone=True), nullable=False)
|
||||
id = fields.IntField(pk=True, generated=True, auto_increment=True)
|
||||
"""自增id"""
|
||||
user_qq = fields.BigIntField()
|
||||
"""用户id"""
|
||||
group_id = fields.BigIntField(null=True)
|
||||
"""群聊id"""
|
||||
text = fields.TextField(null=True)
|
||||
"""文本内容"""
|
||||
plain_text = fields.TextField(null=True)
|
||||
"""纯文本"""
|
||||
create_time = fields.DatetimeField(auto_now_add=True)
|
||||
"""创建时间"""
|
||||
|
||||
@classmethod
|
||||
async def add_chat_msg(cls, user_qq: int, group_id: Optional[int], text: str):
|
||||
await cls.create(
|
||||
user_qq=user_qq, group_id=group_id, text=text, create_time=datetime.now()
|
||||
)
|
||||
|
||||
@classmethod
|
||||
async def get_user_msg(
|
||||
cls,
|
||||
uid: int,
|
||||
msg_type: Optional[Literal["private", "group"]],
|
||||
days: Optional[int] = None,
|
||||
) -> List["ChatHistory"]:
|
||||
"""
|
||||
说明:
|
||||
获取用户消息
|
||||
参数:
|
||||
:param uid: 用户qq
|
||||
:param msg_type: 消息类型,私聊或群聊
|
||||
:param days: 限制日期
|
||||
"""
|
||||
return await cls._get_msg(uid, None, "user", msg_type, days).gino.all()
|
||||
|
||||
@classmethod
|
||||
async def get_group_user_msg(
|
||||
cls,
|
||||
uid: int,
|
||||
gid: int,
|
||||
limit: int = 10,
|
||||
date_scope: Tuple[datetime, datetime] = None,
|
||||
) -> List["ChatHistory"]:
|
||||
"""
|
||||
说明:
|
||||
获取群聊指定用户聊天记录
|
||||
参数:
|
||||
:param uid: qq
|
||||
:param gid: 群号
|
||||
:param limit: 获取数量
|
||||
:param date_scope: 日期范围,默认None为全搜索
|
||||
"""
|
||||
return (
|
||||
await cls._get_msg(uid, gid, "group", days=date_scope)
|
||||
.limit(limit)
|
||||
.gino.all()
|
||||
)
|
||||
|
||||
@classmethod
|
||||
async def get_group_user_msg_count(cls, uid: int, gid: int) -> Optional[int]:
|
||||
"""
|
||||
说明:
|
||||
查询群聊指定用户的聊天记录数量
|
||||
参数:
|
||||
:param uid: qq
|
||||
:param gid: 群号
|
||||
"""
|
||||
if x := await db.first(
|
||||
db.text(
|
||||
f"SELECT COUNT(id) as sum FROM public.chat_history WHERE user_qq = {uid} AND group_id = {gid}"
|
||||
)
|
||||
):
|
||||
return x[0]
|
||||
return None
|
||||
class Meta:
|
||||
table = "chat_history"
|
||||
table_description = "聊天记录数据表"
|
||||
|
||||
@classmethod
|
||||
async def get_group_msg_rank(
|
||||
@@ -83,132 +33,85 @@ class ChatHistory(db.Model):
|
||||
limit: int = 10,
|
||||
order: str = "DESC",
|
||||
date_scope: Optional[Tuple[datetime, datetime]] = None,
|
||||
) -> Optional[Tuple[int, int]]:
|
||||
) -> List["ChatHistory"]:
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
获取排行数据
|
||||
参数:
|
||||
参数:
|
||||
:param gid: 群号
|
||||
:param limit: 获取数量
|
||||
:param order: 排序类型,desc,des
|
||||
:param date_scope: 日期范围
|
||||
"""
|
||||
sql = f"SELECT user_qq, COUNT(id) as sum FROM public.chat_history WHERE group_id = {gid} "
|
||||
o = "-" if order == "DESC" else ""
|
||||
query = cls.filter(group_id=gid)
|
||||
if date_scope:
|
||||
sql += f"AND create_time BETWEEN '{date_scope[0]}' AND '{date_scope[1]}' "
|
||||
sql += f"GROUP BY user_qq ORDER BY sum {order if order and order.upper() != 'DES' else ''} LIMIT {limit}"
|
||||
print(sql)
|
||||
return await db.all(db.text(sql))
|
||||
query = query.filter(create_time__range=date_scope)
|
||||
return list(
|
||||
await query.annotate(count=Count("user_qq"))
|
||||
.order_by(o + "count")
|
||||
.group_by("user_qq")
|
||||
.limit(limit)
|
||||
.values_list("user_qq", "count")
|
||||
) # type: ignore
|
||||
|
||||
@classmethod
|
||||
async def get_group_first_msg_datetime(cls, gid: int) -> Optional[datetime]:
|
||||
async def get_group_first_msg_datetime(cls, group_id: int) -> Optional[datetime]:
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
获取群第一条记录消息时间
|
||||
参数:
|
||||
:param gid:
|
||||
参数:
|
||||
:param group_id: 群聊id
|
||||
"""
|
||||
if (
|
||||
msg := await cls.query.where(cls.group_id == gid)
|
||||
.order_by(cls.create_time)
|
||||
.gino.first()
|
||||
message := await cls.filter(group_id=group_id)
|
||||
.order_by("create_time")
|
||||
.first()
|
||||
):
|
||||
return msg.create_time
|
||||
return None
|
||||
return message.create_time
|
||||
|
||||
@classmethod
|
||||
async def get_user_msg_count(
|
||||
cls,
|
||||
uid: int,
|
||||
msg_type: Optional[Literal["private", "group"]],
|
||||
days: Optional[int] = None,
|
||||
) -> int:
|
||||
"""
|
||||
说明:
|
||||
获取用户消息数量
|
||||
参数:
|
||||
:param uid: 用户qq
|
||||
:param msg_type: 消息类型,私聊或群聊
|
||||
:param days: 限制日期
|
||||
"""
|
||||
return (
|
||||
await cls._get_msg(uid, None, "user", msg_type, days, True).gino.first()
|
||||
)[0]
|
||||
|
||||
@classmethod
|
||||
async def get_group_msg(
|
||||
cls,
|
||||
gid: int,
|
||||
days: Optional[int] = None,
|
||||
) -> List["ChatHistory"]:
|
||||
"""
|
||||
说明:
|
||||
获取群聊消息
|
||||
参数:
|
||||
:param gid: 用户qq
|
||||
:param days: 限制日期
|
||||
"""
|
||||
return await cls._get_msg(None, gid, "group", None, days).gino.all()
|
||||
|
||||
@classmethod
|
||||
async def get_group_msg_count(
|
||||
cls,
|
||||
gid: int,
|
||||
days: Optional[int] = None,
|
||||
) -> List["ChatHistory"]:
|
||||
"""
|
||||
说明:
|
||||
获取群聊消息数量
|
||||
参数:
|
||||
:param gid: 用户qq
|
||||
:param days: 限制日期
|
||||
"""
|
||||
return (await cls._get_msg(None, gid, "group", None, days, True).gino.first())[
|
||||
0
|
||||
]
|
||||
|
||||
@classmethod
|
||||
def _get_msg(
|
||||
async def get_message(
|
||||
cls,
|
||||
uid: Optional[int],
|
||||
gid: Optional[int],
|
||||
type_: Literal["user", "group"],
|
||||
msg_type: Optional[Literal["private", "group"]] = None,
|
||||
days: Optional[Union[int, Tuple[datetime, datetime]]] = None,
|
||||
is_select_count: bool = False,
|
||||
):
|
||||
) -> List["ChatHistory"]:
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
获取消息查询query
|
||||
参数:
|
||||
参数:
|
||||
:param uid: 用户qq
|
||||
:param gid: 群号
|
||||
:param type_: 类型,私聊或群聊
|
||||
:param msg_type: 消息类型,用户或群聊
|
||||
:param days: 限制日期
|
||||
"""
|
||||
if is_select_count:
|
||||
setattr(ChatHistory, "count", db.func.count(cls.id).label("count"))
|
||||
query = cls.select("count")
|
||||
else:
|
||||
query = cls.query
|
||||
if type_ == "user":
|
||||
query = query.where(cls.user_qq == uid)
|
||||
query = cls.filter(user_qq=uid)
|
||||
if msg_type == "private":
|
||||
query = query.where(cls.group_id == None)
|
||||
query = query.filter(group_id__isnull=True)
|
||||
elif msg_type == "group":
|
||||
query = query.where(cls.group_id != None)
|
||||
query = query.filter(group_id__not_isnull=True)
|
||||
else:
|
||||
query = query.where(cls.group_id == gid)
|
||||
query = cls.filter(group_id=gid)
|
||||
if uid:
|
||||
query = query.where(cls.user_qq == uid)
|
||||
query = query.filter(user_qq=uid)
|
||||
if days:
|
||||
if isinstance(days, int):
|
||||
query = query.where(
|
||||
cls.create_time >= datetime.now() - timedelta(days=days)
|
||||
query = query.filter(
|
||||
create_time__gte=datetime.now() - timedelta(days=days)
|
||||
)
|
||||
elif isinstance(days, tuple):
|
||||
query = query.where(cls.create_time >= days[0]).where(
|
||||
cls.create_time <= days[1]
|
||||
)
|
||||
return query
|
||||
query = query.filter(create_time__range=days)
|
||||
return await query.all() # type: ignore
|
||||
|
||||
@classmethod
|
||||
async def _run_script(cls):
|
||||
return [
|
||||
"alter table chat_history alter group_id drop not null;", # 允许 group_id 为空
|
||||
"alter table chat_history alter text drop not null;", # 允许 text 为空
|
||||
"alter table chat_history alter plain_text drop not null;", # 允许 plain_text 为空
|
||||
]
|
||||
|
||||
+28
-83
@@ -1,115 +1,60 @@
|
||||
from services.db_context import db
|
||||
from tortoise import fields
|
||||
|
||||
from configs.config import Config
|
||||
from services.db_context import Model
|
||||
|
||||
|
||||
class FriendUser(db.Model):
|
||||
__tablename__ = "friend_users"
|
||||
class FriendUser(Model):
|
||||
|
||||
id = db.Column(db.Integer(), primary_key=True)
|
||||
user_id = db.Column(db.BigInteger(), nullable=False)
|
||||
user_name = db.Column(db.Unicode(), nullable=False, default="")
|
||||
nickname = db.Column(db.Unicode())
|
||||
id = fields.IntField(pk=True, generated=True, auto_increment=True)
|
||||
"""自增id"""
|
||||
user_id = fields.BigIntField(unique=True)
|
||||
"""用户id"""
|
||||
user_name = fields.CharField(max_length=255, default="")
|
||||
"""用户名称"""
|
||||
nickname = fields.CharField(max_length=255, null=True)
|
||||
"""私聊下自定义昵称"""
|
||||
|
||||
_idx1 = db.Index("friend_users_idx1", "user_id", unique=True)
|
||||
class Meta:
|
||||
table = "friend_users"
|
||||
table_description = "好友信息数据表"
|
||||
|
||||
@classmethod
|
||||
async def get_user_name(cls, user_id: int) -> str:
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
获取好友用户名称
|
||||
参数:
|
||||
参数:
|
||||
:param user_id: qq号
|
||||
"""
|
||||
query = cls.query.where(cls.user_id == user_id)
|
||||
user = await query.gino.first()
|
||||
if user:
|
||||
if user := await cls.get_or_none(user_id=user_id):
|
||||
return user.user_name
|
||||
else:
|
||||
return ""
|
||||
return ""
|
||||
|
||||
@classmethod
|
||||
async def add_friend_info(cls, user_id: int, user_name: str) -> bool:
|
||||
async def get_user_nickname(cls, user_id: int) -> str:
|
||||
"""
|
||||
说明:
|
||||
添加好友信息
|
||||
参数:
|
||||
:param user_id: qq号
|
||||
:param user_name: 用户名称
|
||||
"""
|
||||
try:
|
||||
query = cls.query.where(cls.user_id == user_id)
|
||||
user = await query.with_for_update().gino.first()
|
||||
if not user:
|
||||
await cls.create(
|
||||
user_id=user_id,
|
||||
user_name=user_name,
|
||||
)
|
||||
else:
|
||||
await user.update(
|
||||
user_name=user_name,
|
||||
).apply()
|
||||
return True
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
@classmethod
|
||||
async def delete_friend_info(cls, user_id: int) -> bool:
|
||||
"""
|
||||
说明:
|
||||
删除好友信息
|
||||
参数:
|
||||
:param user_id: qq号
|
||||
"""
|
||||
try:
|
||||
query = cls.query.where(cls.user_id == user_id)
|
||||
user = await query.with_for_update().gino.first()
|
||||
if user:
|
||||
await user.delete()
|
||||
return True
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
@classmethod
|
||||
async def get_friend_nickname(cls, user_id: int) -> str:
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
获取用户昵称
|
||||
参数:
|
||||
参数:
|
||||
:param user_id: qq号
|
||||
"""
|
||||
query = cls.query.where(cls.user_id == user_id)
|
||||
user = await query.gino.first()
|
||||
if user:
|
||||
if user := await cls.get_or_none(user_id=user_id):
|
||||
if user.nickname:
|
||||
_tmp = ""
|
||||
black_word = Config.get_config("nickname", "BLACK_WORD")
|
||||
if black_word:
|
||||
if black_word := Config.get_config("nickname", "BLACK_WORD"):
|
||||
for x in user.nickname:
|
||||
_tmp += "*" if x in black_word else x
|
||||
return _tmp
|
||||
return ""
|
||||
|
||||
@classmethod
|
||||
async def set_friend_nickname(cls, user_id: int, nickname: str) -> bool:
|
||||
async def set_user_nickname(cls, user_id: int, nickname: str):
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
设置用户昵称
|
||||
参数:
|
||||
参数:
|
||||
:param user_id: qq号
|
||||
:param nickname: 昵称
|
||||
"""
|
||||
try:
|
||||
query = cls.query.where(cls.user_id == user_id)
|
||||
user = await query.with_for_update().gino.first()
|
||||
if not user:
|
||||
await cls.create(
|
||||
user_id=user_id,
|
||||
nickname=nickname,
|
||||
)
|
||||
else:
|
||||
await user.update(
|
||||
nickname=nickname,
|
||||
).apply()
|
||||
return True
|
||||
except Exception:
|
||||
return False
|
||||
await cls.update_or_create(user_id=user_id, defaults={"nickname": nickname})
|
||||
|
||||
+136
-73
@@ -1,19 +1,37 @@
|
||||
from services.db_context import db
|
||||
from typing import Optional, List
|
||||
from services.log import logger
|
||||
from typing import Dict, List, Optional, Tuple
|
||||
|
||||
from tortoise import fields
|
||||
|
||||
from services.db_context import Model
|
||||
|
||||
|
||||
class GoodsInfo(db.Model):
|
||||
class GoodsInfo(Model):
|
||||
__tablename__ = "goods_info"
|
||||
|
||||
id = db.Column(db.Integer(), primary_key=True)
|
||||
goods_name = db.Column(db.TEXT(), nullable=False) # 名称
|
||||
goods_price = db.Column(db.Integer(), nullable=False) # 价格
|
||||
goods_description = db.Column(db.TEXT(), nullable=False) # 商品描述
|
||||
goods_discount = db.Column(db.Numeric(scale=3, asdecimal=False), default=1) # 打折
|
||||
goods_limit_time = db.Column(db.BigInteger(), default=0) # 限时
|
||||
id = fields.IntField(pk=True, generated=True, auto_increment=True)
|
||||
"""自增id"""
|
||||
goods_name = fields.CharField(255, unique=True)
|
||||
"""商品名称"""
|
||||
goods_price = fields.IntField()
|
||||
"""价格"""
|
||||
goods_description = fields.TextField()
|
||||
"""描述"""
|
||||
goods_discount = fields.FloatField(default=1)
|
||||
"""折扣"""
|
||||
goods_limit_time = fields.BigIntField(default=0)
|
||||
"""限时"""
|
||||
daily_limit = fields.IntField(default=0)
|
||||
"""每日限购"""
|
||||
daily_purchase_limit: Dict[str, Dict[str, int]] = fields.JSONField(default={})
|
||||
"""用户限购记录"""
|
||||
is_passive = fields.BooleanField(default=False)
|
||||
"""是否为被动道具"""
|
||||
icon = fields.TextField(null=True)
|
||||
"""图标路径"""
|
||||
|
||||
_idx1 = db.Index("goods_group_users_idx1", "goods_name", unique=True)
|
||||
class Meta:
|
||||
table = "goods_info"
|
||||
table_description = "商品数据表"
|
||||
|
||||
@classmethod
|
||||
async def add_goods(
|
||||
@@ -23,48 +41,47 @@ class GoodsInfo(db.Model):
|
||||
goods_description: str,
|
||||
goods_discount: float = 1,
|
||||
goods_limit_time: int = 0,
|
||||
) -> bool:
|
||||
daily_limit: int = 0,
|
||||
is_passive: bool = False,
|
||||
icon: Optional[str] = None,
|
||||
):
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
添加商品
|
||||
参数:
|
||||
参数:
|
||||
:param goods_name: 商品名称
|
||||
:param goods_price: 商品价格
|
||||
:param goods_description: 商品简介
|
||||
:param goods_discount: 商品折扣
|
||||
:param goods_limit_time: 商品限时
|
||||
:param daily_limit: 每日购买限制
|
||||
:param is_passive: 是否为被动道具
|
||||
:param icon: 图标
|
||||
"""
|
||||
try:
|
||||
if not await cls.get_goods_info(goods_name):
|
||||
await cls.create(
|
||||
goods_name=goods_name,
|
||||
goods_price=goods_price,
|
||||
goods_description=goods_description,
|
||||
goods_discount=goods_discount,
|
||||
goods_limit_time=goods_limit_time,
|
||||
)
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.error(f"GoodsInfo add_goods 发生错误 {type(e)}:{e}")
|
||||
return False
|
||||
if not await cls.filter(goods_name=goods_name).first():
|
||||
await cls.create(
|
||||
goods_name=goods_name,
|
||||
goods_price=goods_price,
|
||||
goods_description=goods_description,
|
||||
goods_discount=goods_discount,
|
||||
goods_limit_time=goods_limit_time,
|
||||
daily_limit=daily_limit,
|
||||
is_passive=is_passive,
|
||||
icon=icon,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
async def delete_goods(cls, goods_name: str) -> bool:
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
删除商品
|
||||
参数:
|
||||
参数:
|
||||
:param goods_name: 商品名称
|
||||
"""
|
||||
query = (
|
||||
await cls.query.where(cls.goods_name == goods_name)
|
||||
.with_for_update()
|
||||
.gino.first()
|
||||
)
|
||||
if not query:
|
||||
return False
|
||||
await query.delete()
|
||||
return True
|
||||
if goods := await cls.get_or_none(goods_name=goods_name):
|
||||
await goods.delete()
|
||||
return True
|
||||
return False
|
||||
|
||||
@classmethod
|
||||
async def update_goods(
|
||||
@@ -74,56 +91,50 @@ class GoodsInfo(db.Model):
|
||||
goods_description: Optional[str] = None,
|
||||
goods_discount: Optional[float] = None,
|
||||
goods_limit_time: Optional[int] = None,
|
||||
) -> bool:
|
||||
daily_limit: Optional[int] = None,
|
||||
is_passive: Optional[bool] = None,
|
||||
icon: Optional[str] = None,
|
||||
):
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
更新商品信息
|
||||
参数:
|
||||
参数:
|
||||
:param goods_name: 商品名称
|
||||
:param goods_price: 商品价格
|
||||
:param goods_description: 商品简介
|
||||
:param goods_discount: 商品折扣
|
||||
:param goods_limit_time: 商品限时时间
|
||||
:param daily_limit: 每日次数限制
|
||||
:param is_passive: 是否为被动
|
||||
:param icon: 图标
|
||||
"""
|
||||
try:
|
||||
query = (
|
||||
await cls.query.where(cls.goods_name == goods_name)
|
||||
.with_for_update()
|
||||
.gino.first()
|
||||
if goods := await cls.get_or_none(goods_name=goods_name):
|
||||
await cls.update_or_create(
|
||||
goods_name=goods_name,
|
||||
defaults={
|
||||
"goods_price": goods_price or goods.goods_price,
|
||||
"goods_description": goods_description or goods.goods_description,
|
||||
"goods_discount": goods_discount or goods.goods_discount,
|
||||
"goods_limit_time": goods_limit_time
|
||||
if goods_limit_time is not None
|
||||
else goods.goods_limit_time,
|
||||
"daily_limit": daily_limit
|
||||
if daily_limit is not None
|
||||
else goods.daily_limit,
|
||||
"is_passive": is_passive
|
||||
if is_passive is not None
|
||||
else goods.is_passive,
|
||||
"icon": icon or goods.icon,
|
||||
},
|
||||
)
|
||||
if not query:
|
||||
return False
|
||||
if goods_price:
|
||||
await query.update(goods_price=goods_price).apply()
|
||||
if goods_description:
|
||||
await query.update(goods_description=goods_description).apply()
|
||||
if goods_discount:
|
||||
await query.update(goods_discount=goods_discount).apply()
|
||||
if goods_limit_time:
|
||||
await query.update(goods_limit_time=goods_limit_time).apply()
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.error(f"GoodsInfo update_goods 发生错误 {type(e)}:{e}")
|
||||
return False
|
||||
|
||||
@classmethod
|
||||
async def get_goods_info(cls, goods_name: str) -> "GoodsInfo":
|
||||
"""
|
||||
说明:
|
||||
获取商品对象
|
||||
参数:
|
||||
:param goods_name: 商品名称
|
||||
"""
|
||||
query = await cls.query.where(cls.goods_name == goods_name).gino.first()
|
||||
return query
|
||||
|
||||
@classmethod
|
||||
async def get_all_goods(cls) -> List["GoodsInfo"]:
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
获得全部有序商品对象
|
||||
"""
|
||||
query = await cls.query.gino.all()
|
||||
query = await cls.all()
|
||||
id_lst = [x.id for x in query]
|
||||
goods_lst = []
|
||||
for _ in range(len(query)):
|
||||
@@ -131,3 +142,55 @@ class GoodsInfo(db.Model):
|
||||
goods_lst.append([x for x in query if x.id == min_id][0])
|
||||
id_lst.remove(min_id)
|
||||
return goods_lst
|
||||
|
||||
@classmethod
|
||||
async def add_user_daily_purchase(
|
||||
cls, goods: "GoodsInfo", user_id_: int, group_id_: int, num: int = 1
|
||||
):
|
||||
"""
|
||||
说明:
|
||||
添加用户明日购买限制
|
||||
参数:
|
||||
:param goods: 商品
|
||||
:param user_id: 用户id
|
||||
:param group_id: 群号
|
||||
:param num: 数量
|
||||
"""
|
||||
user_id = str(user_id_)
|
||||
group_id = str(group_id_)
|
||||
if goods and goods.daily_limit and goods.daily_limit > 0:
|
||||
if not goods.daily_purchase_limit.get(group_id):
|
||||
goods.daily_purchase_limit[group_id] = {}
|
||||
if not goods.daily_purchase_limit[group_id].get(user_id):
|
||||
goods.daily_purchase_limit[group_id][user_id] = 0
|
||||
goods.daily_purchase_limit[group_id][user_id] += num
|
||||
await goods.save(update_fields=["daily_purchase_limit"])
|
||||
|
||||
@classmethod
|
||||
async def check_user_daily_purchase(
|
||||
cls, goods: "GoodsInfo", user_id_: int, group_id_: int, num: int = 1
|
||||
) -> Tuple[bool, int]:
|
||||
"""
|
||||
说明:
|
||||
检测用户每日购买上限
|
||||
参数:
|
||||
:param goods: 商品
|
||||
:param user_id: 用户id
|
||||
:param group_id: 群号
|
||||
:param num: 数量
|
||||
"""
|
||||
user_id = str(user_id_)
|
||||
group_id = str(group_id_)
|
||||
if goods and goods.daily_limit > 0:
|
||||
if (
|
||||
not goods.daily_limit
|
||||
or not goods.daily_purchase_limit.get(group_id)
|
||||
or not goods.daily_purchase_limit[group_id].get(user_id)
|
||||
):
|
||||
return goods.daily_limit - num < 0, goods.daily_limit
|
||||
if goods.daily_purchase_limit[group_id][user_id] + num > goods.daily_limit:
|
||||
return (
|
||||
True,
|
||||
goods.daily_limit - goods.daily_purchase_limit[group_id][user_id],
|
||||
)
|
||||
return False, 0
|
||||
|
||||
+17
-104
@@ -1,111 +1,24 @@
|
||||
from services.db_context import db
|
||||
from services.log import logger
|
||||
from typing import List, Optional
|
||||
|
||||
from tortoise import fields
|
||||
|
||||
class GroupInfo(db.Model):
|
||||
__tablename__ = "group_info"
|
||||
from services.db_context import Model
|
||||
from services.log import logger
|
||||
|
||||
group_id = db.Column(db.BigInteger(), nullable=False, primary_key=True)
|
||||
group_name = db.Column(db.Unicode(), nullable=False, default="")
|
||||
max_member_count = db.Column(db.Integer(), nullable=False, default=0)
|
||||
member_count = db.Column(db.Integer(), nullable=False, default=0)
|
||||
group_flag = db.Column(db.Integer(), nullable=False, default=0)
|
||||
|
||||
_idx1 = db.Index("group_info_idx1", "group_id", unique=True)
|
||||
class GroupInfo(Model):
|
||||
|
||||
@classmethod
|
||||
async def get_group_info(cls, group_id: int) -> "GroupInfo":
|
||||
"""
|
||||
说明:
|
||||
获取群信息
|
||||
参数:
|
||||
:param group_id: 群号
|
||||
"""
|
||||
query = cls.query.where(cls.group_id == group_id)
|
||||
return await query.gino.first()
|
||||
group_id = fields.BigIntField(pk=True)
|
||||
"""群聊id"""
|
||||
group_name = fields.TextField(default="")
|
||||
"""群聊名称"""
|
||||
max_member_count = fields.IntField(default=0)
|
||||
"""最大人数"""
|
||||
member_count = fields.IntField(default=0)
|
||||
"""当前人数"""
|
||||
group_flag: int = fields.IntField(default=0)
|
||||
"""群认证标记"""
|
||||
|
||||
@classmethod
|
||||
async def add_group_info(
|
||||
cls,
|
||||
group_id: int,
|
||||
group_name: str,
|
||||
max_member_count: int,
|
||||
member_count: int,
|
||||
group_flag: Optional[int] = None,
|
||||
) -> bool:
|
||||
"""
|
||||
说明:
|
||||
添加群信息
|
||||
参数:
|
||||
:param group_id: 群号
|
||||
:param group_name: 群名称
|
||||
:param max_member_count: 群员最大数量
|
||||
:param member_count: 群员数量
|
||||
:param group_flag: 群认证,0为未认证,1为认证
|
||||
"""
|
||||
try:
|
||||
group = (
|
||||
await cls.query.where(cls.group_id == group_id)
|
||||
.with_for_update()
|
||||
.gino.first()
|
||||
)
|
||||
if group:
|
||||
await group.update(
|
||||
group_name=group_name,
|
||||
max_member_count=max_member_count,
|
||||
member_count=member_count,
|
||||
).apply()
|
||||
if group_flag is not None:
|
||||
await group.update(group_flag=group_flag).apply()
|
||||
else:
|
||||
await cls.create(
|
||||
group_id=group_id,
|
||||
group_name=group_name,
|
||||
max_member_count=max_member_count,
|
||||
member_count=member_count,
|
||||
group_flag=group_flag,
|
||||
)
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.info(f"GroupInfo 调用 add_group_info 发生错误 {type(e)}:{e}")
|
||||
return False
|
||||
|
||||
@classmethod
|
||||
async def delete_group_info(cls, group_id: int):
|
||||
"""
|
||||
说明:
|
||||
删除群信息
|
||||
参数:
|
||||
:param group_id: 群号
|
||||
"""
|
||||
await cls.delete.where(cls.group_id == group_id).gino.status()
|
||||
|
||||
@classmethod
|
||||
async def get_all_group(cls) -> List["GroupInfo"]:
|
||||
"""
|
||||
说明:
|
||||
获取所有群对象
|
||||
"""
|
||||
query = await cls.query.gino.all()
|
||||
return query
|
||||
|
||||
@classmethod
|
||||
async def set_group_flag(cls, group_id: int, group_flag: int) -> bool:
|
||||
"""
|
||||
设置群认证
|
||||
:param group_id: 群号
|
||||
:param group_flag: 群认证,0为未认证,1为认证
|
||||
"""
|
||||
group = (
|
||||
await cls.query.where(cls.group_id == group_id)
|
||||
.with_for_update()
|
||||
.gino.first()
|
||||
)
|
||||
if group:
|
||||
if group.group_flag != group_flag:
|
||||
await group.update(
|
||||
group_flag=group_flag,
|
||||
).apply()
|
||||
return True
|
||||
return False
|
||||
class Meta:
|
||||
table = "group_info"
|
||||
table_description = "群聊信息表"
|
||||
|
||||
+71
-149
@@ -1,195 +1,117 @@
|
||||
from datetime import datetime
|
||||
from typing import List, Optional, Set
|
||||
|
||||
from tortoise import fields
|
||||
|
||||
from configs.config import Config
|
||||
from services.db_context import db
|
||||
from typing import List, Optional
|
||||
from services.db_context import Model
|
||||
from services.log import logger
|
||||
|
||||
|
||||
class GroupInfoUser(db.Model):
|
||||
__tablename__ = "group_info_users"
|
||||
class GroupInfoUser(Model):
|
||||
|
||||
id = db.Column(db.Integer(), primary_key=True)
|
||||
user_qq = db.Column(db.BigInteger(), nullable=False)
|
||||
user_name = db.Column(db.Unicode(), nullable=False)
|
||||
group_id = db.Column(db.BigInteger(), nullable=False)
|
||||
user_join_time = db.Column(db.DateTime(), nullable=False)
|
||||
nickname = db.Column(db.Unicode())
|
||||
uid = db.Column(db.BigInteger())
|
||||
id = fields.IntField(pk=True, generated=True, auto_increment=True)
|
||||
"""自增id"""
|
||||
user_qq = fields.BigIntField()
|
||||
"""用户id"""
|
||||
user_name = fields.CharField(255, default="")
|
||||
"""用户昵称"""
|
||||
group_id = fields.BigIntField()
|
||||
"""群聊id"""
|
||||
user_join_time: datetime = fields.DatetimeField(null=True)
|
||||
"""用户入群时间"""
|
||||
nickname = fields.CharField(255, null=True)
|
||||
"""群聊昵称"""
|
||||
uid = fields.BigIntField(null=True)
|
||||
"""用户uid"""
|
||||
|
||||
_idx1 = db.Index("info_group_users_idx1", "user_qq", "group_id", unique=True)
|
||||
class Meta:
|
||||
table = "group_info_users"
|
||||
table_description = "群员信息数据表"
|
||||
unique_together = ("user_qq", "group_id")
|
||||
|
||||
@classmethod
|
||||
async def add_member_info(
|
||||
cls,
|
||||
user_qq: int,
|
||||
group_id: int,
|
||||
user_name: str,
|
||||
user_join_time: datetime,
|
||||
uid: Optional[int] = None,
|
||||
) -> bool:
|
||||
async def get_group_member_id_list(cls, group_id: int) -> Set[int]:
|
||||
"""
|
||||
说明:
|
||||
添加群内用户信息
|
||||
参数:
|
||||
:param user_qq: qq号
|
||||
:param group_id: 群号
|
||||
:param user_name: 用户名称
|
||||
:param user_join_time: 入群时间
|
||||
:param uid: 用户唯一 id(自动生成)
|
||||
"""
|
||||
query = cls.query.where(
|
||||
(cls.user_qq == user_qq) & (cls.group_id == group_id)
|
||||
)
|
||||
try:
|
||||
if not await query.gino.first():
|
||||
await cls.create(
|
||||
user_qq=user_qq,
|
||||
user_name=user_name,
|
||||
group_id=group_id,
|
||||
user_join_time=user_join_time,
|
||||
uid=uid
|
||||
)
|
||||
return True
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
@classmethod
|
||||
async def get_member_info(
|
||||
cls, user_qq: int, group_id: int
|
||||
) -> "GroupInfoUser":
|
||||
"""
|
||||
说明:
|
||||
查询群员信息
|
||||
参数:
|
||||
:param user_qq: qq号
|
||||
:param group_id: 群号
|
||||
"""
|
||||
query = cls.query.where(
|
||||
(cls.user_qq == user_qq) & (cls.group_id == group_id)
|
||||
)
|
||||
return await query.gino.first()
|
||||
|
||||
@classmethod
|
||||
async def delete_member_info(cls, user_qq: int, group_id: int) -> bool:
|
||||
"""
|
||||
说明:
|
||||
删除群员信息
|
||||
参数:
|
||||
:param user_qq: qq号
|
||||
:param group_id: 群号
|
||||
"""
|
||||
query = cls.query.where(
|
||||
(cls.user_qq == user_qq) & (cls.group_id == group_id)
|
||||
)
|
||||
query = query.with_for_update()
|
||||
user = await query.gino.first()
|
||||
try:
|
||||
if user is None:
|
||||
return True
|
||||
else:
|
||||
await cls.delete.where(
|
||||
(cls.user_qq == user_qq) & (cls.group_id == group_id)
|
||||
).gino.status()
|
||||
return True
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
@classmethod
|
||||
async def get_group_member_id_list(cls, group_id: int) -> List[int]:
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
获取该群所有用户qq
|
||||
参数:
|
||||
参数:
|
||||
:param group_id: 群号
|
||||
"""
|
||||
member_list = []
|
||||
query = cls.query.where((cls.group_id == group_id))
|
||||
for user in await query.gino.all():
|
||||
member_list.append(user.user_qq)
|
||||
return member_list
|
||||
return set(
|
||||
await cls.filter(group_id=group_id).values_list("user_qq", flat=True)
|
||||
) # type: ignore
|
||||
|
||||
@classmethod
|
||||
async def set_group_member_nickname(
|
||||
cls, user_qq: int, group_id: int, nickname: str
|
||||
) -> bool:
|
||||
async def set_user_nickname(cls, user_qq: int, group_id: int, nickname: str):
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
设置群员在该群内的昵称
|
||||
参数:
|
||||
参数:
|
||||
:param user_qq: qq号
|
||||
:param group_id: 群号
|
||||
:param nickname: 昵称
|
||||
"""
|
||||
query = cls.query.where(
|
||||
(cls.user_qq == user_qq) & (cls.group_id == group_id)
|
||||
await cls.update_or_create(
|
||||
user_qq=user_qq,
|
||||
group_id=group_id,
|
||||
defaults={"nickname": nickname},
|
||||
)
|
||||
user = await query.with_for_update().gino.first()
|
||||
if user:
|
||||
await user.update(nickname=nickname).apply()
|
||||
return True
|
||||
return False
|
||||
|
||||
@classmethod
|
||||
async def get_user_all_group(cls, user_qq: int) -> List[int]:
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
获取该用户所在的所有群聊
|
||||
参数:
|
||||
参数:
|
||||
:param user_qq: 用户qq
|
||||
"""
|
||||
query = await cls.query.where(cls.user_qq == user_qq).gino.all()
|
||||
if query:
|
||||
query = [x.group_id for x in query]
|
||||
return query
|
||||
return list(
|
||||
await cls.filter(user_qq=user_qq).values_list("group_id", flat=True)
|
||||
) # type: ignore
|
||||
|
||||
@classmethod
|
||||
async def get_group_member_nickname(cls, user_qq: int, group_id: int) -> str:
|
||||
async def get_user_nickname(cls, user_qq: int, group_id: int) -> str:
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
获取用户在该群的昵称
|
||||
参数:
|
||||
参数:
|
||||
:param user_qq: qq号
|
||||
:param group_id: 群号
|
||||
"""
|
||||
query = cls.query.where(
|
||||
(cls.user_qq == user_qq) & (cls.group_id == group_id)
|
||||
)
|
||||
user = await query.gino.first()
|
||||
if user:
|
||||
if user := await cls.get_or_none(user_qq=user_qq, group_id=group_id):
|
||||
if user.nickname:
|
||||
_tmp = ""
|
||||
black_word = Config.get_config("nickname", "BLACK_WORD")
|
||||
if black_word:
|
||||
nickname = ""
|
||||
if black_word := Config.get_config("nickname", "BLACK_WORD"):
|
||||
for x in user.nickname:
|
||||
_tmp += "*" if x in black_word else x
|
||||
return _tmp
|
||||
nickname += "*" if x in black_word else x
|
||||
return nickname
|
||||
return user.nickname
|
||||
return ""
|
||||
|
||||
@classmethod
|
||||
async def get_group_member_uid(cls, user_qq: int, group_id: int) -> Optional[str]:
|
||||
query = cls.query.where(
|
||||
(cls.user_qq == user_qq) & (cls.group_id == group_id)
|
||||
async def get_group_member_uid(cls, user_qq: int, group_id: int) -> Optional[int]:
|
||||
logger.debug(
|
||||
f"GroupInfoUser 尝试获取 用户[<u><e>{user_qq}</e></u>] 群聊[<u><e>{group_id}</e></u>] UID"
|
||||
)
|
||||
user = await query.gino.first()
|
||||
_max_uid = cls.query.where((cls.user_qq == 114514) & (cls.group_id == 114514)).with_for_update()
|
||||
_max_uid_user = await _max_uid.gino.first()
|
||||
user, _ = await cls.get_or_create(user_qq=user_qq, group_id=group_id)
|
||||
_max_uid_user, _ = await cls.get_or_create(user_qq=114514, group_id=114514)
|
||||
_max_uid = _max_uid_user.uid
|
||||
if not user or not user.uid:
|
||||
all_user = await cls.query.where(cls.user_qq == user_qq).gino.all()
|
||||
if not user.uid:
|
||||
all_user = await cls.filter(user_qq=user_qq).all()
|
||||
for x in all_user:
|
||||
if x.uid:
|
||||
return x.uid
|
||||
else:
|
||||
if not user:
|
||||
await GroupInfoUser.add_member_info(user_qq, group_id, '', datetime.min)
|
||||
user = await cls.query.where(
|
||||
(cls.user_qq == user_qq) & (cls.group_id == group_id)
|
||||
).gino.first()
|
||||
await user.update(
|
||||
uid=_max_uid + 1,
|
||||
).apply()
|
||||
await _max_uid_user.update(
|
||||
uid=_max_uid + 1,
|
||||
).apply()
|
||||
|
||||
return user.uid if user and user.uid else None
|
||||
user.uid = _max_uid + 1
|
||||
_max_uid_user.uid = _max_uid + 1
|
||||
await cls.bulk_update([user, _max_uid_user], ["uid"])
|
||||
logger.debug(
|
||||
f"GroupInfoUser 获取 用户[<u><e>{user_qq}</e></u>] 群聊[<u><e>{group_id}</e></u>] UID: {user.uid}"
|
||||
)
|
||||
return user.uid
|
||||
|
||||
@classmethod
|
||||
async def _run_script(cls):
|
||||
return [
|
||||
"alter table group_info_users alter user_join_time drop not null;" # 允许 user_join_time 为空
|
||||
]
|
||||
|
||||
+48
-72
@@ -1,124 +1,100 @@
|
||||
from services.db_context import db
|
||||
from tortoise import fields
|
||||
|
||||
from services.db_context import Model
|
||||
|
||||
|
||||
class LevelUser(db.Model):
|
||||
__tablename__ = "level_users"
|
||||
class LevelUser(Model):
|
||||
|
||||
id = db.Column(db.Integer(), primary_key=True)
|
||||
user_qq = db.Column(db.BigInteger(), nullable=False)
|
||||
group_id = db.Column(db.BigInteger(), nullable=False)
|
||||
user_level = db.Column(db.BigInteger(), nullable=False)
|
||||
group_flag = db.Column(db.Integer(), nullable=False, default=0)
|
||||
id = fields.IntField(pk=True, generated=True, auto_increment=True)
|
||||
"""自增id"""
|
||||
user_qq = fields.BigIntField()
|
||||
"""用户id"""
|
||||
group_id = fields.BigIntField()
|
||||
"""群聊id"""
|
||||
user_level = fields.BigIntField()
|
||||
"""用户权限等级"""
|
||||
group_flag = fields.IntField(default=0)
|
||||
"""特殊标记,是否随群管理员变更而设置权限"""
|
||||
|
||||
_idx1 = db.Index("level_group_users_idx1", "user_qq", "group_id", unique=True)
|
||||
class Meta:
|
||||
table = "level_users"
|
||||
table_description = "用户权限数据库"
|
||||
unique_together = ("user_qq", "group_id")
|
||||
|
||||
@classmethod
|
||||
async def get_user_level(cls, user_qq: int, group_id: int) -> int:
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
获取用户在群内的等级
|
||||
参数:
|
||||
参数:
|
||||
:param user_qq: qq号
|
||||
:param group_id: 群号
|
||||
"""
|
||||
query = cls.query.where((cls.user_qq == user_qq) & (cls.group_id == group_id))
|
||||
user = await query.gino.first()
|
||||
if user:
|
||||
if user := await cls.get_or_none(user_qq=user_qq, group_id=group_id):
|
||||
return user.user_level
|
||||
else:
|
||||
return -1
|
||||
return -1
|
||||
|
||||
@classmethod
|
||||
async def set_level(
|
||||
cls, user_qq: int, group_id: int, level: int, group_flag: int = 0
|
||||
) -> bool:
|
||||
):
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
设置用户在群内的权限
|
||||
参数:
|
||||
参数:
|
||||
:param user_qq: qq号
|
||||
:param group_id: 群号
|
||||
:param level: 权限等级
|
||||
:param group_flag: 是否被自动更新刷新权限 0:是,1:否
|
||||
"""
|
||||
query = cls.query.where((cls.user_qq == user_qq) & (cls.group_id == group_id))
|
||||
query = query.with_for_update()
|
||||
user = await query.gino.first()
|
||||
if user is None:
|
||||
await cls.create(
|
||||
user_qq=user_qq,
|
||||
group_id=group_id,
|
||||
user_level=level,
|
||||
group_flag=group_flag,
|
||||
)
|
||||
return True
|
||||
else:
|
||||
await user.update(user_level=level, group_flag=group_flag).apply()
|
||||
return False
|
||||
await cls.update_or_create(
|
||||
user_qq=user_qq,
|
||||
group_id=group_id,
|
||||
defaults={"user_level": level, "group_flag": group_flag},
|
||||
)
|
||||
|
||||
@classmethod
|
||||
async def delete_level(cls, user_qq: int, group_id: int) -> bool:
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
删除用户权限
|
||||
参数:
|
||||
参数:
|
||||
:param user_qq: qq号
|
||||
:param group_id: 群号
|
||||
"""
|
||||
query = cls.query.where((cls.user_qq == user_qq) & (cls.group_id == group_id))
|
||||
query = query.with_for_update()
|
||||
user = await query.gino.first()
|
||||
if user is None:
|
||||
return False
|
||||
else:
|
||||
if user := await cls.get_or_none(user_qq=user_qq, group_id=group_id):
|
||||
await user.delete()
|
||||
return True
|
||||
return False
|
||||
|
||||
@classmethod
|
||||
async def check_level(cls, user_qq: int, group_id: int, level: int) -> bool:
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
检查用户权限等级是否大于 level
|
||||
参数:
|
||||
参数:
|
||||
:param user_qq: qq号
|
||||
:param group_id: 群号
|
||||
:param level: 权限等级
|
||||
"""
|
||||
if group_id != 0:
|
||||
query = cls.query.where(
|
||||
(cls.user_qq == user_qq) & (cls.group_id == group_id)
|
||||
)
|
||||
user = await query.gino.first()
|
||||
if user is None:
|
||||
return False
|
||||
user_level = user.user_level
|
||||
if group_id:
|
||||
if user := await cls.get_or_none(user_qq=user_qq, group_id=group_id):
|
||||
return user.user_level >= level
|
||||
else:
|
||||
query = cls.query.where(cls.user_qq == user_qq)
|
||||
highest_level = 0
|
||||
for user in await query.gino.all():
|
||||
if user.user_level > highest_level:
|
||||
highest_level = user.user_level
|
||||
user_level = highest_level
|
||||
if user_level >= level:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
user_list = await cls.filter(user_qq=user_qq).all()
|
||||
user = max(user_list, key=lambda x: x.user_level)
|
||||
return user.user_level >= level
|
||||
return False
|
||||
|
||||
@classmethod
|
||||
async def is_group_flag(cls, user_qq: int, group_id: int) -> bool:
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
检测是否会被自动更新刷新权限
|
||||
参数:
|
||||
参数:
|
||||
:param user_qq: qq号
|
||||
:param group_id: 群号
|
||||
"""
|
||||
user = await cls.query.where(
|
||||
(cls.user_qq == user_qq) & (cls.group_id == group_id)
|
||||
).gino.first()
|
||||
if not user:
|
||||
return False
|
||||
if user.group_flag == 1:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
if user := await cls.get_or_none(user_qq=user_qq, group_id=group_id):
|
||||
return user.group_flag == 1
|
||||
return False
|
||||
|
||||
+49
-77
@@ -1,99 +1,71 @@
|
||||
from datetime import datetime
|
||||
from typing import List
|
||||
from services.db_context import db
|
||||
from typing import List, Literal, Optional, Tuple
|
||||
|
||||
from tortoise import fields
|
||||
|
||||
from services.db_context import Model
|
||||
|
||||
|
||||
class SignGroupUser(db.Model):
|
||||
__tablename__ = "sign_group_users"
|
||||
class SignGroupUser(Model):
|
||||
|
||||
id = db.Column(db.Integer(), primary_key=True)
|
||||
user_qq = db.Column(db.BigInteger(), nullable=False)
|
||||
group_id = db.Column(db.BigInteger(), nullable=False)
|
||||
checkin_count = db.Column(db.Integer(), nullable=False)
|
||||
checkin_time_last = db.Column(db.DateTime(timezone=True), nullable=False)
|
||||
impression = db.Column(db.Numeric(scale=3, asdecimal=False), nullable=False)
|
||||
add_probability = db.Column(
|
||||
db.Numeric(scale=3, asdecimal=False), nullable=False, default=0
|
||||
)
|
||||
specify_probability = db.Column(
|
||||
db.Numeric(scale=3, asdecimal=False), nullable=False, default=0
|
||||
)
|
||||
id = fields.IntField(pk=True, generated=True, auto_increment=True)
|
||||
"""自增id"""
|
||||
user_qq = fields.BigIntField()
|
||||
"""用户id"""
|
||||
group_id = fields.BigIntField()
|
||||
"""群聊id"""
|
||||
checkin_count = fields.IntField(default=0)
|
||||
"""签到次数"""
|
||||
checkin_time_last = fields.DatetimeField(auto_now=True)
|
||||
"""最后签到时间"""
|
||||
impression = fields.DecimalField(10, 3, default=0)
|
||||
"""好感度"""
|
||||
add_probability = fields.DecimalField(10, 3, default=0)
|
||||
"""双倍签到增加概率"""
|
||||
specify_probability = fields.DecimalField(10, 3, default=0)
|
||||
"""使用指定双倍概率"""
|
||||
# specify_probability = fields.DecimalField(10, 3, default=0)
|
||||
|
||||
_idx1 = db.Index("sign_group_users_idx1", "user_qq", "group_id", unique=True)
|
||||
class Meta:
|
||||
table = "sign_group_users"
|
||||
table_description = "群员签到数据表"
|
||||
unique_together = ("user_qq", "group_id")
|
||||
|
||||
@classmethod
|
||||
async def ensure(
|
||||
cls, user_qq: int, group_id: int, for_update: bool = False
|
||||
) -> "SignGroupUser":
|
||||
"""
|
||||
说明:
|
||||
获取签到用户
|
||||
参数:
|
||||
:param user_qq: 用户qq
|
||||
:param group_id: 所在群聊
|
||||
:param for_update: 是否存在修改数据
|
||||
"""
|
||||
query = cls.query.where(
|
||||
(cls.user_qq == user_qq) & (cls.group_id == group_id)
|
||||
)
|
||||
if for_update:
|
||||
query = query.with_for_update()
|
||||
user = await query.gino.first()
|
||||
return user or await cls.create(
|
||||
user_qq=user_qq,
|
||||
group_id=group_id,
|
||||
checkin_count=0,
|
||||
checkin_time_last=datetime.min, # 从未签到过
|
||||
impression=0,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
async def get_user_all_data(cls, user_qq: int) -> List["SignGroupUser"]:
|
||||
"""
|
||||
说明:
|
||||
获取某用户所有数据
|
||||
参数:
|
||||
:param user_qq: 用户qq
|
||||
"""
|
||||
query = cls.query.where(cls.user_qq == user_qq)
|
||||
query = query.with_for_update()
|
||||
return await query.gino.all()
|
||||
|
||||
@classmethod
|
||||
async def sign(cls, user: "SignGroupUser", impression: float, checkin_time_last: datetime):
|
||||
async def sign(cls, user: "SignGroupUser", impression: float):
|
||||
"""
|
||||
说明:
|
||||
签到
|
||||
说明:
|
||||
:param user: 用户
|
||||
:param impression: 增加的好感度
|
||||
:param checkin_time_last: 签到时间
|
||||
"""
|
||||
await user.update(
|
||||
checkin_count=user.checkin_count + 1,
|
||||
checkin_time_last=checkin_time_last,
|
||||
impression=user.impression + impression,
|
||||
add_probability=0,
|
||||
specify_probability=0,
|
||||
).apply()
|
||||
user.checkin_count = user.checkin_count + 1
|
||||
user.add_probability = 0
|
||||
user.specify_probability = 0
|
||||
user.impression = float(user.impression) + impression
|
||||
await user.save()
|
||||
|
||||
@classmethod
|
||||
async def get_all_impression(cls, group_id: int) -> "list, list, list":
|
||||
async def get_all_impression(
|
||||
cls, group_id: Optional[int]
|
||||
) -> Tuple[List[int], List[int], List[float]]:
|
||||
"""
|
||||
说明:
|
||||
说明:
|
||||
获取该群所有用户 id 及对应 好感度
|
||||
参数:
|
||||
参数:
|
||||
:param group_id: 群号
|
||||
"""
|
||||
impression_list = []
|
||||
user_qq_list = []
|
||||
user_group = []
|
||||
if group_id:
|
||||
query = cls.query.where(cls.group_id == group_id)
|
||||
query = cls.filter(group_id=group_id)
|
||||
else:
|
||||
query = cls.query
|
||||
for user in await query.gino.all():
|
||||
impression_list.append(user.impression)
|
||||
user_qq_list.append(user.user_qq)
|
||||
user_group.append(user.group_id)
|
||||
return user_qq_list, impression_list, user_group
|
||||
query = cls
|
||||
value_list = await query.all().values_list("user_qq", "group_id", "impression") # type: ignore
|
||||
qq_list = []
|
||||
group_list = []
|
||||
impression_list = []
|
||||
for value in value_list:
|
||||
qq_list.append(value[0])
|
||||
group_list.append(value[1])
|
||||
impression_list.append(float(value[2]))
|
||||
return qq_list, impression_list, group_list
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
from datetime import datetime
|
||||
|
||||
from tortoise import fields
|
||||
|
||||
from services.db_context import Model
|
||||
|
||||
|
||||
class UserShopGoldLog(Model):
|
||||
|
||||
id = fields.IntField(pk=True, generated=True, auto_increment=True)
|
||||
"""自增id"""
|
||||
user_qq = fields.BigIntField()
|
||||
"""用户id"""
|
||||
group_id = fields.BigIntField()
|
||||
"""群聊id"""
|
||||
type = fields.IntField()
|
||||
"""金币使用类型 0: 购买, 1: 使用, 2: 插件"""
|
||||
name = fields.CharField(255)
|
||||
"""商品/插件 名称"""
|
||||
spend_gold = fields.IntField(default=0)
|
||||
"""花费金币"""
|
||||
num = fields.IntField()
|
||||
"""数量"""
|
||||
create_time = fields.DatetimeField(auto_now_add=True)
|
||||
"""创建时间"""
|
||||
|
||||
class Meta:
|
||||
table = "user_shop_gold_log"
|
||||
table_description = "金币使用日志表"
|
||||
+14
-15
@@ -1,20 +1,20 @@
|
||||
from utils.message_builder import image
|
||||
from configs.path_config import IMAGE_PATH
|
||||
from nonebot import on_command
|
||||
from nonebot.rule import to_me
|
||||
import os
|
||||
import random
|
||||
|
||||
from nonebot import on_command, on_keyword
|
||||
from nonebot.adapters.onebot.v11 import GroupMessageEvent
|
||||
from nonebot.adapters.onebot.v11.permission import GROUP
|
||||
from utils.utils import FreqLimiter
|
||||
from configs.config import NICKNAME
|
||||
import random
|
||||
from nonebot import on_keyword
|
||||
import os
|
||||
from nonebot.rule import to_me
|
||||
|
||||
from configs.config import NICKNAME
|
||||
from configs.path_config import IMAGE_PATH
|
||||
from utils.message_builder import image
|
||||
from utils.utils import FreqLimiter
|
||||
|
||||
__zx_plugin_name__ = "基本设置 [Hidden]"
|
||||
__plugin_usage__ = "用法: 无"
|
||||
__plugin_version__ = 0.1
|
||||
__plugin_author__ = 'HibiKier'
|
||||
__plugin_author__ = "HibiKier"
|
||||
|
||||
|
||||
_flmt = FreqLimiter(300)
|
||||
@@ -29,7 +29,7 @@ async def _(event: GroupMessageEvent):
|
||||
return
|
||||
_flmt.start_cd(event.group_id)
|
||||
await config_play_game.finish(
|
||||
image(random.choice(os.listdir(IMAGE_PATH / "dayouxi")), "dayouxi")
|
||||
image(IMAGE_PATH / random.choice(os.listdir(IMAGE_PATH / "dayouxi")))
|
||||
)
|
||||
|
||||
|
||||
@@ -40,14 +40,14 @@ self_introduction = on_command(
|
||||
|
||||
@self_introduction.handle()
|
||||
async def _():
|
||||
if NICKNAME.find('真寻') != -1:
|
||||
if NICKNAME.find("真寻") != -1:
|
||||
result = (
|
||||
"我叫绪山真寻\n"
|
||||
"你们可以叫我真寻,小真寻,哪怕你们叫我小寻子我也能接受!\n"
|
||||
"年龄的话我还是个**岁初中生(至少现在是)\n"
|
||||
"身高保密!!!(也就比美波里(姐姐..(妹妹))矮一点)\n"
|
||||
"我生日是在3月6号, 能记住的话我会很高兴的\n现在是自宅警备系的现役JC\n"
|
||||
"最好的朋友是椛!\n" + image("zhenxun")
|
||||
"最好的朋友是椛!\n" + image("zhenxun.jpg")
|
||||
)
|
||||
await self_introduction.finish(result)
|
||||
|
||||
@@ -57,5 +57,4 @@ my_wife = on_keyword({"老婆"}, rule=to_me(), priority=5, block=True)
|
||||
|
||||
@my_wife.handle()
|
||||
async def _():
|
||||
await my_wife.finish(image("laopo.jpg", "other"))
|
||||
|
||||
await my_wife.finish(image(IMAGE_PATH / "other" / "laopo.jpg"))
|
||||
|
||||
+17
-14
@@ -1,17 +1,16 @@
|
||||
from typing import List
|
||||
|
||||
from nonebot import on_message
|
||||
from nonebot.adapters.onebot.v11 import (
|
||||
Bot,
|
||||
GroupMessageEvent,
|
||||
Message,
|
||||
MessageEvent,
|
||||
)
|
||||
from nonebot.adapters.onebot.v11 import Bot, GroupMessageEvent, Message, MessageEvent
|
||||
from nonebot.rule import to_me
|
||||
|
||||
from configs.config import NICKNAME, Config
|
||||
from models.friend_user import FriendUser
|
||||
from models.group_member_info import GroupInfoUser
|
||||
from services.log import logger
|
||||
from utils.utils import get_message_img, get_message_text
|
||||
|
||||
from .data_source import get_chat_result, hello, no_result
|
||||
from configs.config import NICKNAME, Config
|
||||
|
||||
__zx_plugin_name__ = "AI"
|
||||
__plugin_usage__ = f"""
|
||||
@@ -25,19 +24,25 @@ __plugin_settings__ = {
|
||||
"cmd": ["Ai", "ai", "AI", "aI"],
|
||||
}
|
||||
__plugin_configs__ = {
|
||||
"TL_KEY": {"value": [], "help": "图灵Key"},
|
||||
"ALAPI_AI_CHECK": {"value": False, "help": "是否检测青云客骂娘回复", "default_value": False},
|
||||
"TL_KEY": {"value": [], "help": "图灵Key", "type": List[str]},
|
||||
"ALAPI_AI_CHECK": {
|
||||
"value": False,
|
||||
"help": "是否检测青云客骂娘回复",
|
||||
"default_value": False,
|
||||
"type": bool,
|
||||
},
|
||||
"TEXT_FILTER": {
|
||||
"value": ["鸡", "口交"],
|
||||
"help": "文本过滤器,将敏感词更改为*",
|
||||
"default_value": [],
|
||||
"type": List[str],
|
||||
},
|
||||
}
|
||||
Config.add_plugin_config(
|
||||
"alapi", "ALAPI_TOKEN", None, help_="在 https://admin.alapi.cn/user/login 登录后获取token"
|
||||
)
|
||||
|
||||
ai = on_message(rule=to_me(), priority=8)
|
||||
ai = on_message(rule=to_me(), priority=998)
|
||||
|
||||
|
||||
@ai.handle()
|
||||
@@ -60,11 +65,9 @@ async def _(bot: Bot, event: MessageEvent):
|
||||
await ai.finish(hello())
|
||||
img = img[0] if img else ""
|
||||
if isinstance(event, GroupMessageEvent):
|
||||
nickname = await GroupInfoUser.get_group_member_nickname(
|
||||
event.user_id, event.group_id
|
||||
)
|
||||
nickname = await GroupInfoUser.get_user_nickname(event.user_id, event.group_id)
|
||||
else:
|
||||
nickname = await FriendUser.get_friend_nickname(event.user_id)
|
||||
nickname = await FriendUser.get_user_nickname(event.user_id)
|
||||
if not nickname:
|
||||
if isinstance(event, GroupMessageEvent):
|
||||
nickname = event.sender.card or event.sender.nickname
|
||||
|
||||
+54
-47
@@ -1,11 +1,13 @@
|
||||
import os
|
||||
import random
|
||||
import re
|
||||
from utils.http_utils import AsyncHttpx
|
||||
from configs.path_config import IMAGE_PATH, DATA_PATH
|
||||
|
||||
from configs.config import NICKNAME, Config
|
||||
from configs.path_config import DATA_PATH, IMAGE_PATH
|
||||
from services.log import logger
|
||||
from utils.message_builder import image, face
|
||||
from configs.config import Config, NICKNAME
|
||||
from utils.http_utils import AsyncHttpx
|
||||
from utils.message_builder import face, image
|
||||
|
||||
from .utils import ai_message_manager
|
||||
|
||||
try:
|
||||
@@ -124,37 +126,43 @@ async def xie_ai(text: str) -> str:
|
||||
:param text: 问题
|
||||
:return: 青云可回复
|
||||
"""
|
||||
res = await AsyncHttpx.get(f"http://api.qingyunke.com/api.php?key=free&appid=0&msg={text}")
|
||||
content = ""
|
||||
data = json.loads(res.text)
|
||||
if data["result"] == 0:
|
||||
content = data["content"]
|
||||
if "菲菲" in content:
|
||||
content = content.replace("菲菲", NICKNAME)
|
||||
if "艳儿" in content:
|
||||
content = content.replace("艳儿", NICKNAME)
|
||||
if "公众号" in content:
|
||||
content = ""
|
||||
if "{br}" in content:
|
||||
content = content.replace("{br}", "\n")
|
||||
if "提示" in content:
|
||||
content = content[: content.find("提示")]
|
||||
if "淘宝" in content or "taobao.com" in content:
|
||||
return ""
|
||||
while True:
|
||||
r = re.search("{face:(.*)}", content)
|
||||
if r:
|
||||
id_ = r.group(1)
|
||||
content = content.replace(
|
||||
"{" + f"face:{id_}" + "}", str(face(int(id_)))
|
||||
)
|
||||
else:
|
||||
break
|
||||
return (
|
||||
content
|
||||
if not content and not Config.get_config("ai", "ALAPI_AI_CHECK")
|
||||
else await check_text(content)
|
||||
res = await AsyncHttpx.get(
|
||||
f"http://api.qingyunke.com/api.php?key=free&appid=0&msg={text}"
|
||||
)
|
||||
content = ""
|
||||
try:
|
||||
data = json.loads(res.text)
|
||||
if data["result"] == 0:
|
||||
content = data["content"]
|
||||
if "菲菲" in content:
|
||||
content = content.replace("菲菲", NICKNAME)
|
||||
if "艳儿" in content:
|
||||
content = content.replace("艳儿", NICKNAME)
|
||||
if "公众号" in content:
|
||||
content = ""
|
||||
if "{br}" in content:
|
||||
content = content.replace("{br}", "\n")
|
||||
if "提示" in content:
|
||||
content = content[: content.find("提示")]
|
||||
if "淘宝" in content or "taobao.com" in content:
|
||||
return ""
|
||||
while True:
|
||||
r = re.search("{face:(.*)}", content)
|
||||
if r:
|
||||
id_ = r.group(1)
|
||||
content = content.replace(
|
||||
"{" + f"face:{id_}" + "}", str(face(int(id_)))
|
||||
)
|
||||
else:
|
||||
break
|
||||
return (
|
||||
content
|
||||
if not content and not Config.get_config("ai", "ALAPI_AI_CHECK")
|
||||
else await check_text(content)
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f"Ai xie_ai 发生错误 {type(e)}:{e}")
|
||||
return ""
|
||||
|
||||
|
||||
def hello() -> str:
|
||||
@@ -172,9 +180,9 @@ def hello() -> str:
|
||||
)
|
||||
img = random.choice(os.listdir(IMAGE_PATH / "zai"))
|
||||
if img[-4:] == ".gif":
|
||||
result += image(img, "zai")
|
||||
result += image(IMAGE_PATH / "zai" / img)
|
||||
else:
|
||||
result += image(img, "zai")
|
||||
result += image(IMAGE_PATH / "zai" / img)
|
||||
return result
|
||||
|
||||
|
||||
@@ -183,17 +191,16 @@ def no_result() -> str:
|
||||
"""
|
||||
没有回答时的回复
|
||||
"""
|
||||
return (
|
||||
random.choice(
|
||||
[
|
||||
"你在说啥子?",
|
||||
f"纯洁的{NICKNAME}没听懂",
|
||||
"下次再告诉你(下次一定)",
|
||||
"你觉得我听懂了吗?嗯?",
|
||||
"我!不!知!道!",
|
||||
]
|
||||
)
|
||||
+ image(random.choice(os.listdir(IMAGE_PATH / "noresult")), "noresult")
|
||||
return random.choice(
|
||||
[
|
||||
"你在说啥子?",
|
||||
f"纯洁的{NICKNAME}没听懂",
|
||||
"下次再告诉你(下次一定)",
|
||||
"你觉得我听懂了吗?嗯?",
|
||||
"我!不!知!道!",
|
||||
]
|
||||
) + image(
|
||||
IMAGE_PATH / "noresult" / random.choice(os.listdir(IMAGE_PATH / "noresult"))
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -1,14 +1,11 @@
|
||||
from configs.config import Config
|
||||
from pathlib import Path
|
||||
|
||||
import nonebot
|
||||
|
||||
from configs.config import Config
|
||||
|
||||
Config.add_plugin_config(
|
||||
"alapi",
|
||||
"ALAPI_TOKEN",
|
||||
None,
|
||||
help_="在https://admin.alapi.cn/user/login登录后获取token"
|
||||
"alapi", "ALAPI_TOKEN", None, help_="在https://admin.alapi.cn/user/login登录后获取token"
|
||||
)
|
||||
|
||||
|
||||
nonebot.load_plugins("plugins/alapi")
|
||||
|
||||
nonebot.load_plugins(str(Path(__file__).parent.resolve()))
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
from nonebot.adapters.onebot.v11 import MessageSegment
|
||||
from utils.image_utils import BuildImage
|
||||
from utils.message_builder import image
|
||||
from configs.path_config import IMAGE_PATH
|
||||
from typing import Optional, Tuple, Union
|
||||
from configs.config import Config
|
||||
from utils.http_utils import AsyncHttpx
|
||||
@@ -23,27 +19,8 @@ async def get_data(url: str, params: Optional[dict] = None) -> Tuple[Union[dict,
|
||||
return "没有搜索到...", 997
|
||||
return data, 200
|
||||
else:
|
||||
if data["code"] == 101:
|
||||
return "缺失ALAPI TOKEN,请在配置文件中填写!", 999
|
||||
return f'发生了错误...code:{data["code"]}', 999
|
||||
except TimeoutError:
|
||||
return "超时了....", 998
|
||||
|
||||
|
||||
def gen_wbtop_pic(data: dict) -> MessageSegment:
|
||||
"""
|
||||
生成微博热搜图片
|
||||
:param data: 微博热搜数据
|
||||
"""
|
||||
bk = BuildImage(700, 32 * 50 + 280, 700, 32, color="#797979")
|
||||
wbtop_bk = BuildImage(700, 280, background=f"{IMAGE_PATH}/other/webtop.png")
|
||||
bk.paste(wbtop_bk)
|
||||
text_bk = BuildImage(700, 32 * 50, 700, 32, color="#797979")
|
||||
for i, data in enumerate(data):
|
||||
title = f"{i+1}. {data['hot_word']}"
|
||||
hot = data["hot_word_num"]
|
||||
img = BuildImage(700, 30, font_size=20)
|
||||
w, h = img.getsize(title)
|
||||
img.text((10, int((30 - h) / 2)), title)
|
||||
img.text((580, int((30 - h) / 2)), hot)
|
||||
text_bk.paste(img)
|
||||
bk.paste(text_bk, (0, 280))
|
||||
return image(b64=bk.pic2bs4())
|
||||
|
||||
@@ -1,24 +1,29 @@
|
||||
from nonebot import on_command
|
||||
from typing import Any, Optional, Tuple
|
||||
|
||||
import nonebot
|
||||
from nonebot import Driver, on_command, on_regex
|
||||
from nonebot.adapters.onebot.v11 import Bot, GroupMessageEvent, Message, MessageEvent
|
||||
from nonebot.params import ArgStr, CommandArg, RegexGroup
|
||||
from nonebot.typing import T_State
|
||||
from nonebot.adapters.onebot.v11 import Bot, MessageEvent, GroupMessageEvent, Message
|
||||
|
||||
from configs.config import Config
|
||||
from models.level_user import LevelUser
|
||||
from services.log import logger
|
||||
from utils.image_utils import text2image
|
||||
from utils.manager import group_manager
|
||||
from utils.message_builder import image
|
||||
from utils.utils import get_bot, is_number, scheduler
|
||||
|
||||
from .data_source import (
|
||||
BilibiliSub,
|
||||
SubManager,
|
||||
add_live_sub,
|
||||
delete_sub,
|
||||
add_up_sub,
|
||||
add_season_sub,
|
||||
add_up_sub,
|
||||
delete_sub,
|
||||
get_media_id,
|
||||
get_sub_status,
|
||||
SubManager,
|
||||
BilibiliSub,
|
||||
)
|
||||
from models.level_user import LevelUser
|
||||
from configs.config import Config
|
||||
from utils.utils import is_number, scheduler, get_bot
|
||||
from typing import Optional
|
||||
from services.log import logger
|
||||
from nonebot import Driver
|
||||
from nonebot.params import CommandArg, ArgStr
|
||||
import nonebot
|
||||
|
||||
__zx_plugin_name__ = "B站订阅"
|
||||
__plugin_usage__ = """
|
||||
@@ -27,18 +32,18 @@ usage:
|
||||
主播订阅相当于 直播间订阅 + UP订阅
|
||||
指令:[示例Id乱打的,仅做示例]
|
||||
添加订阅 ['主播'/'UP'/'番剧'] [id/链接/番名]
|
||||
删除订阅 [id]
|
||||
删除订阅 ['主播'/'UP'/'id'] [id]
|
||||
查看订阅
|
||||
示例:添加订阅主播 2345344 <-(直播房间id)
|
||||
示例:添加订阅UP 2355543 <-(个人主页id)
|
||||
示例:添加订阅番剧 史莱姆 <-(支持模糊搜索)
|
||||
示例:添加订阅番剧 125344 <-(番剧id)
|
||||
示例:删除订阅 2324344 <-(任意id,通过查看订阅获取)
|
||||
示例:删除订阅id 2324344 <-(任意id,通过查看订阅获取)
|
||||
""".strip()
|
||||
__plugin_des__ = "非常便利的B站订阅通知"
|
||||
__plugin_cmd__ = ["添加订阅 [主播/UP/番剧] [id/链接/番名]", "删除订阅 [id]", "查看订阅"]
|
||||
__plugin_cmd__ = ["添加订阅 [主播/UP/番剧] [id/链接/番名]", "删除订阅 ['主播'/'UP'/'id'] [id]", "查看订阅"]
|
||||
__plugin_version__ = 0.1
|
||||
__plugin_author__ = "HibiKier"
|
||||
__plugin_author__ = "HibiKier & NumberSir"
|
||||
__plugin_settings__ = {
|
||||
"level": 5,
|
||||
"default_status": True,
|
||||
@@ -50,12 +55,25 @@ __plugin_configs__ = {
|
||||
"value": 5,
|
||||
"help": "群内bilibili订阅需要管理的权限",
|
||||
"default_value": 5,
|
||||
}
|
||||
"type": int,
|
||||
},
|
||||
"LIVE_MSG_AT_ALL": {
|
||||
"value": False,
|
||||
"help": "直播提醒是否AT全体(仅在真寻是管理员时生效)",
|
||||
"default_value": False,
|
||||
"type": bool,
|
||||
},
|
||||
"UP_MSG_AT_ALL": {
|
||||
"value": False,
|
||||
"help": "UP动态投稿提醒是否AT全体(仅在真寻是管理员时生效)",
|
||||
"default_value": False,
|
||||
"type": bool,
|
||||
},
|
||||
}
|
||||
|
||||
add_sub = on_command("添加订阅", priority=5, block=True)
|
||||
del_sub = on_command("删除订阅", priority=5, block=True)
|
||||
show_sub_info = on_command("查看订阅", priority=5, block=True)
|
||||
show_sub_info = on_regex("^查看订阅$", priority=5, block=True)
|
||||
|
||||
driver: Driver = nonebot.get_driver()
|
||||
|
||||
@@ -70,6 +88,7 @@ async def _():
|
||||
|
||||
|
||||
@add_sub.handle()
|
||||
@del_sub.handle()
|
||||
async def _(event: MessageEvent, state: T_State, arg: Message = CommandArg()):
|
||||
msg = arg.extract_plain_text().strip().split()
|
||||
if len(msg) < 2:
|
||||
@@ -143,25 +162,30 @@ async def _(
|
||||
)
|
||||
|
||||
|
||||
@del_sub.handle()
|
||||
async def _(event: MessageEvent, arg: Message = CommandArg()):
|
||||
msg = arg.extract_plain_text().strip()
|
||||
if not is_number(msg):
|
||||
await del_sub.finish("Id必须为数字!", at_sender=True)
|
||||
id_ = (
|
||||
f"{event.user_id}:{event.group_id}"
|
||||
if isinstance(event, GroupMessageEvent)
|
||||
else f"{event.user_id}"
|
||||
)
|
||||
if await BilibiliSub.delete_bilibili_sub(int(msg), id_):
|
||||
await del_sub.send(f"删除订阅id:{msg} 成功...")
|
||||
@del_sub.got("sub_type")
|
||||
@del_sub.got("sub_user")
|
||||
@del_sub.got("id")
|
||||
async def _(
|
||||
event: MessageEvent,
|
||||
id_: str = ArgStr("id"),
|
||||
sub_type: str = ArgStr("sub_type"),
|
||||
sub_user: str = ArgStr("sub_user"),
|
||||
):
|
||||
if sub_type in ["主播", "直播"]:
|
||||
result = await BilibiliSub.delete_bilibili_sub(int(id_), sub_user, "live")
|
||||
elif sub_type.lower() in ["up", "用户"]:
|
||||
result = await BilibiliSub.delete_bilibili_sub(int(id_), sub_user, "up")
|
||||
else:
|
||||
result = await BilibiliSub.delete_bilibili_sub(int(id_), sub_user)
|
||||
if result:
|
||||
await del_sub.send(f"删除订阅id:{id_} 成功...")
|
||||
logger.info(
|
||||
f"(USER {event.user_id}, GROUP "
|
||||
f"{event.group_id if isinstance(event, GroupMessageEvent) else 'private'})"
|
||||
f" 删除订阅 {id_}"
|
||||
)
|
||||
else:
|
||||
await del_sub.send(f"删除订阅id:{msg} 失败...")
|
||||
await del_sub.send(f"删除订阅id:{id_} 失败...")
|
||||
|
||||
|
||||
@show_sub_info.handle()
|
||||
@@ -170,7 +194,7 @@ async def _(event: MessageEvent):
|
||||
id_ = f"{event.group_id}"
|
||||
else:
|
||||
id_ = f"{event.user_id}"
|
||||
data = await BilibiliSub.get_sub_data(id_)
|
||||
data = await BilibiliSub.filter(sub_users__contains=id_).all()
|
||||
live_rst = ""
|
||||
up_rst = ""
|
||||
season_rst = ""
|
||||
@@ -195,7 +219,13 @@ async def _(event: MessageEvent):
|
||||
live_rst = (
|
||||
"该群目前没有任何订阅..." if isinstance(event, GroupMessageEvent) else "您目前没有任何订阅..."
|
||||
)
|
||||
await show_sub_info.send(live_rst + up_rst + season_rst)
|
||||
await show_sub_info.send(
|
||||
image(
|
||||
await text2image(
|
||||
live_rst + up_rst + season_rst, padding=10, color="#f9f6f2"
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
# 推送
|
||||
@@ -207,18 +237,18 @@ async def _():
|
||||
bot = get_bot()
|
||||
sub = None
|
||||
if bot:
|
||||
try:
|
||||
await sub_manager.reload_sub_data()
|
||||
sub = await sub_manager.random_sub_data()
|
||||
if sub:
|
||||
logger.debug(f"Bilibili订阅开始检测:{sub.sub_id}")
|
||||
rst = await get_sub_status(sub.sub_id, sub.sub_type)
|
||||
# try:
|
||||
await sub_manager.reload_sub_data()
|
||||
sub = await sub_manager.random_sub_data()
|
||||
if sub:
|
||||
logger.debug(f"Bilibili订阅开始检测:{sub.sub_id}")
|
||||
rst = await get_sub_status(sub.sub_id, sub.sub_type)
|
||||
await send_sub_msg(rst, sub, bot)
|
||||
if sub.sub_type == "live":
|
||||
rst = await get_sub_status(sub.sub_id, "up")
|
||||
await send_sub_msg(rst, sub, bot)
|
||||
if sub.sub_type == "live":
|
||||
rst = await get_sub_status(sub.sub_id, "up")
|
||||
await send_sub_msg(rst, sub, bot)
|
||||
except Exception as e:
|
||||
logger.error(f"B站订阅推送发生错误 sub_id:{sub.sub_id if sub else 0} {type(e)}:{e}")
|
||||
# except Exception as e:
|
||||
# logger.error(f"B站订阅推送发生错误 sub_id:{sub.sub_id if sub else 0} {type(e)}:{e}")
|
||||
|
||||
|
||||
async def send_sub_msg(rst: str, sub: BilibiliSub, bot: Bot):
|
||||
@@ -228,13 +258,30 @@ async def send_sub_msg(rst: str, sub: BilibiliSub, bot: Bot):
|
||||
:param sub: BilibiliSub
|
||||
:param bot: Bot
|
||||
"""
|
||||
temp_group = []
|
||||
if rst:
|
||||
for x in sub.sub_users.split(",")[:-1]:
|
||||
try:
|
||||
if ":" in x:
|
||||
await bot.send_group_msg(
|
||||
group_id=int(x.split(":")[1]), message=Message(rst)
|
||||
)
|
||||
if ":" in x and x.split(":")[1] not in temp_group:
|
||||
group_id = int(x.split(":")[1])
|
||||
temp_group.append(group_id)
|
||||
if (
|
||||
await bot.get_group_member_info(
|
||||
group_id=group_id, user_id=int(bot.self_id), no_cache=True
|
||||
)
|
||||
)["role"] in ["owner", "admin"]:
|
||||
if (
|
||||
sub.sub_type == "live"
|
||||
and Config.get_config("bilibili_sub", "LIVE_MSG_AT_ALL")
|
||||
) or (
|
||||
sub.sub_type == "up"
|
||||
and Config.get_config("bilibili_sub", "UP_MSG_AT_ALL")
|
||||
):
|
||||
rst = "[CQ:at,qq=all]\n" + rst
|
||||
if group_manager.get_plugin_status("bilibili_sub", group_id):
|
||||
await bot.send_group_msg(
|
||||
group_id=group_id, message=Message(rst)
|
||||
)
|
||||
else:
|
||||
await bot.send_private_msg(user_id=int(x), message=Message(rst))
|
||||
except Exception as e:
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user