mirror of
https://github.com/FloatTech/ZeroBot-Plugin.git
synced 2026-02-06 15:20:22 +00:00
Compare commits
1202 Commits
v1.3.2-bet
...
v1.10.5
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8fc733ffd1 | ||
|
|
6deaa8b36e | ||
|
|
e187a3159c | ||
|
|
0d80a43055 | ||
|
|
9bad003301 | ||
|
|
a5a1edda2d | ||
|
|
6a5ffa0b16 | ||
|
|
531ea7fa0c | ||
|
|
f23e44503c | ||
|
|
3f0e284cee | ||
|
|
6634d94ff4 | ||
|
|
bf3ecfcb55 | ||
|
|
6a83d74eb7 | ||
|
|
a13486faef | ||
|
|
a828f49012 | ||
|
|
2ac3730f87 | ||
|
|
b897a11158 | ||
|
|
62a57fa61d | ||
|
|
0142050d6a | ||
|
|
9cfad308b0 | ||
|
|
28a8ee945c | ||
|
|
2154faec2c | ||
|
|
577b3f3f30 | ||
|
|
99c9cc3de6 | ||
|
|
216d99c05d | ||
|
|
8101c02ca8 | ||
|
|
da81875f8b | ||
|
|
dd1a0ae478 | ||
|
|
aafac8d6fa | ||
|
|
a74bcb869a | ||
|
|
584882282f | ||
|
|
05345e4c13 | ||
|
|
b8a57b80f2 | ||
|
|
177fdbae68 | ||
|
|
fef48e405a | ||
|
|
2d1603d65a | ||
|
|
73c7bbda1a | ||
|
|
558c3ee482 | ||
|
|
bc20d95bd1 | ||
|
|
883adc621c | ||
|
|
7be1f2342b | ||
|
|
4481ea4861 | ||
|
|
4aace203cf | ||
|
|
324a0022a7 | ||
|
|
8825514fb2 | ||
|
|
d2e6c9780f | ||
|
|
06b8b518f8 | ||
|
|
aa67d09ee1 | ||
|
|
724e48f3e8 | ||
|
|
57c95178d8 | ||
|
|
1e2d425cf4 | ||
|
|
c22e4b543c | ||
|
|
4434a617ed | ||
|
|
d6eb3ba28c | ||
|
|
d0f4d296d9 | ||
|
|
13aaaba8f3 | ||
|
|
3302d8d295 | ||
|
|
dd328afae8 | ||
|
|
d304cda7f0 | ||
|
|
109395a6af | ||
|
|
f7c0f4df98 | ||
|
|
43cc90b724 | ||
|
|
7bd1653cb5 | ||
|
|
6c4c6a3b8b | ||
|
|
72e0e796c4 | ||
|
|
3c41c18d27 | ||
|
|
7640f0cfac | ||
|
|
73eef961b5 | ||
|
|
8e87be262c | ||
|
|
8811df5968 | ||
|
|
f09f15937c | ||
|
|
49983ab451 | ||
|
|
4dd5189a3c | ||
|
|
b9250ab82d | ||
|
|
641495b579 | ||
|
|
e6a4dfcdf2 | ||
|
|
6b505d050a | ||
|
|
fc9a21d2d1 | ||
|
|
e84e44476a | ||
|
|
b012df4c23 | ||
|
|
08e02ab730 | ||
|
|
fb090839d6 | ||
|
|
ac2d53352c | ||
|
|
35292a69fc | ||
|
|
cd16a755d7 | ||
|
|
1e7b2d3335 | ||
|
|
20d49ccf15 | ||
|
|
1f66f47ce6 | ||
|
|
34f3b9ba2a | ||
|
|
2fa7868838 | ||
|
|
b6ddda1d51 | ||
|
|
a1621f34a0 | ||
|
|
21aa3bc49f | ||
|
|
617d4f50a4 | ||
|
|
cb0ffa0c17 | ||
|
|
0615993297 | ||
|
|
1c0d91424a | ||
|
|
c94ee365ce | ||
|
|
19e5e6636f | ||
|
|
cac3a4be81 | ||
|
|
beada7f4da | ||
|
|
43b45ce6c5 | ||
|
|
95dd5e6b94 | ||
|
|
566f6ecfd5 | ||
|
|
5b28ad75b7 | ||
|
|
997857a558 | ||
|
|
4269057283 | ||
|
|
f70cab80c2 | ||
|
|
609d819610 | ||
|
|
961fbb098e | ||
|
|
42fe124b09 | ||
|
|
076b113455 | ||
|
|
c888936489 | ||
|
|
e1d2dee881 | ||
|
|
39e1f56955 | ||
|
|
4151464bdc | ||
|
|
0b89312d9d | ||
|
|
2c607dedee | ||
|
|
30e9d04f74 | ||
|
|
4b90a0659b | ||
|
|
109b7661b7 | ||
|
|
8da52a2772 | ||
|
|
7515983b55 | ||
|
|
7519ea548d | ||
|
|
6a55d9b279 | ||
|
|
d5227f1159 | ||
|
|
62e9fe69ed | ||
|
|
2df52161e5 | ||
|
|
a29f4cb1f9 | ||
|
|
6a747d2f9d | ||
|
|
164c71a3a3 | ||
|
|
3f1b0ad67b | ||
|
|
6c6699a5d6 | ||
|
|
e292b69ee5 | ||
|
|
e6e6dd4565 | ||
|
|
28bfc3e71d | ||
|
|
d3975cf461 | ||
|
|
7430c41c1e | ||
|
|
dd1064db26 | ||
|
|
1167866b16 | ||
|
|
caed8eab0c | ||
|
|
8d85334049 | ||
|
|
da89fb29ca | ||
|
|
e005621fbb | ||
|
|
511f04c6f9 | ||
|
|
fb29619b9e | ||
|
|
325adf8911 | ||
|
|
b494390373 | ||
|
|
085e95cd48 | ||
|
|
659f4e07c2 | ||
|
|
3abf6317de | ||
|
|
e8305c5886 | ||
|
|
71d029f7a0 | ||
|
|
dc899f55fe | ||
|
|
8c11f48502 | ||
|
|
e12ec697e6 | ||
|
|
b01d3a4f14 | ||
|
|
552c1a9a35 | ||
|
|
aacf720b88 | ||
|
|
832addc55e | ||
|
|
f7c5584016 | ||
|
|
df5ceb930f | ||
|
|
d4071f54f9 | ||
|
|
ae859a1ece | ||
|
|
112328a7f2 | ||
|
|
512561d8fd | ||
|
|
f73bf5a270 | ||
|
|
3c7034e46c | ||
|
|
2a848366f3 | ||
|
|
549a8ce30b | ||
|
|
d00c4aec47 | ||
|
|
4ef5b854bc | ||
|
|
41f02f34c2 | ||
|
|
b9cf52404d | ||
|
|
350e2e3907 | ||
|
|
a8b2587cee | ||
|
|
735c3cdde1 | ||
|
|
24955dc4a7 | ||
|
|
b4eb61e36a | ||
|
|
70ebb03434 | ||
|
|
2c5596cd96 | ||
|
|
d6c13337d1 | ||
|
|
eb2daf1827 | ||
|
|
c4d4bd9f40 | ||
|
|
fd1c8bf9b9 | ||
|
|
7fef4b08c0 | ||
|
|
b04c244cab | ||
|
|
cfb98ca241 | ||
|
|
885544077b | ||
|
|
fcb01c2c18 | ||
|
|
41d28b71bf | ||
|
|
8fa91a13c3 | ||
|
|
696a0ac99c | ||
|
|
37818acee1 | ||
|
|
38eae9375d | ||
|
|
c35fc543d3 | ||
|
|
2c8726dda3 | ||
|
|
83037f621c | ||
|
|
6a2c7e8740 | ||
|
|
9e8ae43b15 | ||
|
|
4ba0b36be1 | ||
|
|
9470977092 | ||
|
|
08d33fd74f | ||
|
|
66a8ec91bc | ||
|
|
b4edabb91d | ||
|
|
83ca8c344b | ||
|
|
f5e1c197dd | ||
|
|
7c5a17761e | ||
|
|
3c7289997a | ||
|
|
eb065e1984 | ||
|
|
814fa0ce33 | ||
|
|
4b0a2a12a8 | ||
|
|
321c941ce5 | ||
|
|
e653475e08 | ||
|
|
23c0949388 | ||
|
|
6349f38d57 | ||
|
|
105553c386 | ||
|
|
48a2703b99 | ||
|
|
ba3df3d9a0 | ||
|
|
b448e972e1 | ||
|
|
0537fcbd6e | ||
|
|
6c79b3385f | ||
|
|
42b63d836d | ||
|
|
b9bea7dff7 | ||
|
|
78d156395b | ||
|
|
8bcab9bf43 | ||
|
|
ef3fa92de3 | ||
|
|
50be1bbe68 | ||
|
|
4cf296c839 | ||
|
|
26d2074db6 | ||
|
|
419f67f1c7 | ||
|
|
066ee59741 | ||
|
|
001fdcceae | ||
|
|
baf1a80dc2 | ||
|
|
6a038643d6 | ||
|
|
339b9db271 | ||
|
|
6a0d8fbaa4 | ||
|
|
24b438741b | ||
|
|
25ce35fd63 | ||
|
|
8b302d715f | ||
|
|
077da3746c | ||
|
|
831fe5482a | ||
|
|
edf186f5b8 | ||
|
|
db50375141 | ||
|
|
8e6eb618dd | ||
|
|
3102577a16 | ||
|
|
24ce891c4d | ||
|
|
112da0f37d | ||
|
|
a8cb6f2061 | ||
|
|
74edd1c375 | ||
|
|
defcdf34bb | ||
|
|
f13342350d | ||
|
|
b777b34126 | ||
|
|
410dd05600 | ||
|
|
147ebb001c | ||
|
|
096bd4c99a | ||
|
|
a248bb1420 | ||
|
|
95451f96d8 | ||
|
|
f3a841fc60 | ||
|
|
e4cfcef05b | ||
|
|
be26b052a6 | ||
|
|
c2b7ae31cc | ||
|
|
7d48916d2c | ||
|
|
a15c241bd6 | ||
|
|
5e91bd7a63 | ||
|
|
817c4fab61 | ||
|
|
fbadd8924f | ||
|
|
c008214be4 | ||
|
|
54471d16e4 | ||
|
|
6f7d4beb17 | ||
|
|
2cee275771 | ||
|
|
9de8092416 | ||
|
|
3cffc36dec | ||
|
|
7d5c770398 | ||
|
|
13ef54ef62 | ||
|
|
6ec3dff78a | ||
|
|
158baea605 | ||
|
|
e861b66ace | ||
|
|
3866ff8a46 | ||
|
|
863f99cb05 | ||
|
|
badd65a207 | ||
|
|
ff62723e91 | ||
|
|
7b45374310 | ||
|
|
3da55c4b75 | ||
|
|
a7d2a31bb8 | ||
|
|
074e77dfa2 | ||
|
|
ac909f95db | ||
|
|
4fd923c731 | ||
|
|
4b771f2809 | ||
|
|
89a5b68601 | ||
|
|
adbcb3956d | ||
|
|
293859460b | ||
|
|
c8594cfc54 | ||
|
|
31b70179f9 | ||
|
|
5d2898bc53 | ||
|
|
47f176a56e | ||
|
|
fa02189fad | ||
|
|
de558dc87b | ||
|
|
c9939d145b | ||
|
|
852c5f2f35 | ||
|
|
1c0f8ffadb | ||
|
|
2a4aa39bab | ||
|
|
26fcb5aad5 | ||
|
|
9119ded754 | ||
|
|
f6ef326495 | ||
|
|
4ffa18c511 | ||
|
|
2710074f00 | ||
|
|
0afe577b1b | ||
|
|
7329426fa4 | ||
|
|
97a647a620 | ||
|
|
61501d8ed9 | ||
|
|
289e74a619 | ||
|
|
18f77165ec | ||
|
|
7a3589e26d | ||
|
|
ca12560d29 | ||
|
|
c70766a989 | ||
|
|
b637fe18e9 | ||
|
|
81e255eb3a | ||
|
|
3b3b17aa33 | ||
|
|
4d7b7835e8 | ||
|
|
180f7564f3 | ||
|
|
835ba2617f | ||
|
|
286caaca8c | ||
|
|
445d82b090 | ||
|
|
487cd4db20 | ||
|
|
07122bbbf7 | ||
|
|
018f6c4008 | ||
|
|
fdb291d1d7 | ||
|
|
dc0d7c4151 | ||
|
|
e55c1fbd3d | ||
|
|
8c1f4e2d2f | ||
|
|
502693e222 | ||
|
|
23e567f2b1 | ||
|
|
a5a24e748e | ||
|
|
6210ed9496 | ||
|
|
a29b116f0d | ||
|
|
3faabc4fd8 | ||
|
|
7c47a55d74 | ||
|
|
94efc8a1af | ||
|
|
cda583e7b6 | ||
|
|
e62c86a740 | ||
|
|
a9dfbdb54b | ||
|
|
8fa928d37f | ||
|
|
d832f9aaea | ||
|
|
5e047cafb0 | ||
|
|
baaef6cb7f | ||
|
|
d1b83f4764 | ||
|
|
78c64ac310 | ||
|
|
0a45fafb1b | ||
|
|
509c462c8d | ||
|
|
61b70a86de | ||
|
|
8d0279fec7 | ||
|
|
8e5fbbae15 | ||
|
|
995f37ce17 | ||
|
|
97950bebcc | ||
|
|
a0e9816ea4 | ||
|
|
15df65af7b | ||
|
|
1351a7edbe | ||
|
|
4f193f4251 | ||
|
|
71109f23fb | ||
|
|
ec00251972 | ||
|
|
79f1beac53 | ||
|
|
1a20ecb634 | ||
|
|
3225a2d6c7 | ||
|
|
0e3c025b80 | ||
|
|
2a820cfc54 | ||
|
|
d57d7a7abf | ||
|
|
ad9a74e159 | ||
|
|
4c0c06ab1b | ||
|
|
c287ae7d92 | ||
|
|
c8d846d747 | ||
|
|
ff898b86ed | ||
|
|
a4094cca64 | ||
|
|
803c027264 | ||
|
|
63784b4ed0 | ||
|
|
1971e2274a | ||
|
|
68fee3532b | ||
|
|
c29b1238c2 | ||
|
|
a72bec15a9 | ||
|
|
f4e9d3e4dd | ||
|
|
6e54d214f0 | ||
|
|
f54b9adf84 | ||
|
|
cd91373215 | ||
|
|
a497609340 | ||
|
|
75d91b3d45 | ||
|
|
06fd754cc5 | ||
|
|
8e8c51a263 | ||
|
|
33580ab947 | ||
|
|
9531347292 | ||
|
|
717446ae6c | ||
|
|
786b5055c2 | ||
|
|
9eadefba73 | ||
|
|
730ab28d92 | ||
|
|
83b46dd071 | ||
|
|
a7d07ecf60 | ||
|
|
4334f4dd22 | ||
|
|
879948a5c4 | ||
|
|
b09025a330 | ||
|
|
d95760a534 | ||
|
|
a5130cd7f6 | ||
|
|
bab5c4c308 | ||
|
|
e0891200af | ||
|
|
b050b5d5b5 | ||
|
|
2ff164c2df | ||
|
|
cab917d054 | ||
|
|
dfe6d347a5 | ||
|
|
5c6af1af61 | ||
|
|
d7cdfb1544 | ||
|
|
cf0e87ab9a | ||
|
|
0a33224571 | ||
|
|
dd878f7dfe | ||
|
|
ac52c9cfca | ||
|
|
ece68dae9b | ||
|
|
6c20f22548 | ||
|
|
6f2c9f7fc0 | ||
|
|
b8858f0acd | ||
|
|
9c65383a7c | ||
|
|
a006b30392 | ||
|
|
54c67e30f4 | ||
|
|
61168adb2e | ||
|
|
73b879a6f4 | ||
|
|
a5d2e3dfc3 | ||
|
|
0ea804fc86 | ||
|
|
17bfa2a35c | ||
|
|
ff3d1d1de3 | ||
|
|
3a31802735 | ||
|
|
a514bde4e6 | ||
|
|
7f90ba278a | ||
|
|
2e77703571 | ||
|
|
192eef2bbb | ||
|
|
73ecc47e4c | ||
|
|
714df78ea2 | ||
|
|
f4f50acb1d | ||
|
|
8c24194772 | ||
|
|
844b727859 | ||
|
|
148f271586 | ||
|
|
b062198fae | ||
|
|
1c57a64002 | ||
|
|
13bb760f9f | ||
|
|
6c1257184b | ||
|
|
b0463b3cb4 | ||
|
|
1bb8e8b058 | ||
|
|
209b2e8a1f | ||
|
|
0c55c743f7 | ||
|
|
ba7c6cb0a0 | ||
|
|
d3e587f935 | ||
|
|
36190a4f6a | ||
|
|
54d9486c06 | ||
|
|
fe6130872b | ||
|
|
5e41c15d88 | ||
|
|
e776d02d98 | ||
|
|
cc39d0fdb4 | ||
|
|
1456bed668 | ||
|
|
8d2da4199d | ||
|
|
50e75b2257 | ||
|
|
a6e82d578a | ||
|
|
5b6ab7e20d | ||
|
|
23fea2dae9 | ||
|
|
97c1985479 | ||
|
|
035ed3bd13 | ||
|
|
3e04445cac | ||
|
|
f0078bb23f | ||
|
|
1a8ebe02fb | ||
|
|
de986d8be8 | ||
|
|
ab7daa6bdc | ||
|
|
0bf3253cbb | ||
|
|
21269ee835 | ||
|
|
df927d0b7f | ||
|
|
6a3456f4b0 | ||
|
|
db1d583a37 | ||
|
|
146ea6812e | ||
|
|
99b14e183a | ||
|
|
cb52bf6af7 | ||
|
|
a534c9efbd | ||
|
|
7ac397373d | ||
|
|
73c6f4795e | ||
|
|
598efe182c | ||
|
|
a9bb078079 | ||
|
|
673c715d2f | ||
|
|
dbda1b466d | ||
|
|
b7e21fc111 | ||
|
|
6bff62b91b | ||
|
|
5485cc3be9 | ||
|
|
ba0ef37b74 | ||
|
|
9676b26de0 | ||
|
|
30f60bc7e9 | ||
|
|
9dade7f3e8 | ||
|
|
f043fbf0c0 | ||
|
|
e75814e3b3 | ||
|
|
245b50ba94 | ||
|
|
16e5b84e5c | ||
|
|
26e4b3c513 | ||
|
|
986e50705d | ||
|
|
38b11bb8c9 | ||
|
|
0357405cdf | ||
|
|
7f264964c2 | ||
|
|
c8dcdbb0b2 | ||
|
|
ebcd3cdef2 | ||
|
|
eb43f84d34 | ||
|
|
5931e03c00 | ||
|
|
aaf659f6d4 | ||
|
|
f4add826ff | ||
|
|
0d8157e2f8 | ||
|
|
283667cfe5 | ||
|
|
6403614fa8 | ||
|
|
bdcb833217 | ||
|
|
278d7e0cf9 | ||
|
|
821f8d4949 | ||
|
|
436838d2f8 | ||
|
|
30301f1aba | ||
|
|
df1d71b9b2 | ||
|
|
0315718ab7 | ||
|
|
971c179227 | ||
|
|
1c0270fda0 | ||
|
|
5463945b6c | ||
|
|
8eb058fb26 | ||
|
|
33e5b0286f | ||
|
|
296fd60cd7 | ||
|
|
667c9fbcca | ||
|
|
3d8d0ad107 | ||
|
|
f35a89e401 | ||
|
|
c5c63d0b64 | ||
|
|
3e15754031 | ||
|
|
c1e37b293c | ||
|
|
8edc3f6de6 | ||
|
|
416cb3f2b8 | ||
|
|
8050f12d51 | ||
|
|
292fc311d6 | ||
|
|
28cb0491f7 | ||
|
|
1bad611018 | ||
|
|
d49224fd50 | ||
|
|
2324837052 | ||
|
|
35f8458d67 | ||
|
|
8a6b7f65c7 | ||
|
|
7b1d2b7eaa | ||
|
|
7ced79c6bb | ||
|
|
e224bf9252 | ||
|
|
2b1caafa16 | ||
|
|
c1702c5d5e | ||
|
|
5c8ead8b1d | ||
|
|
635eb9832a | ||
|
|
f1e49dc02e | ||
|
|
573c942a9d | ||
|
|
e8b61b39f0 | ||
|
|
ed26d31326 | ||
|
|
76dbf5a4b6 | ||
|
|
f1ea658c7d | ||
|
|
2964986ee5 | ||
|
|
52ef28cd6a | ||
|
|
df4c0438a2 | ||
|
|
864f92a728 | ||
|
|
6ea50cf8b7 | ||
|
|
d66dd9b7fa | ||
|
|
4a15326977 | ||
|
|
b4a1f93993 | ||
|
|
0c8dfb3f9c | ||
|
|
1aa11879dc | ||
|
|
14c596be1f | ||
|
|
309afecadd | ||
|
|
f465e78460 | ||
|
|
aa5c324592 | ||
|
|
1a0575af64 | ||
|
|
587d3967ef | ||
|
|
d693f988bb | ||
|
|
500dcaa9ed | ||
|
|
1376803b07 | ||
|
|
68386910c4 | ||
|
|
1734f1f7d4 | ||
|
|
86b87c2b4e | ||
|
|
e9eb4c5602 | ||
|
|
6474b36ccd | ||
|
|
107149892c | ||
|
|
f1dba97922 | ||
|
|
bc3c4303be | ||
|
|
f7a61ed0ee | ||
|
|
107b38e2f5 | ||
|
|
0d85fa04e1 | ||
|
|
1d8f5a7869 | ||
|
|
5e93368e57 | ||
|
|
56e931665e | ||
|
|
e5b2b369e3 | ||
|
|
58cf08fc39 | ||
|
|
c18f9b8b72 | ||
|
|
0df465e1c6 | ||
|
|
8b010321d5 | ||
|
|
e1591c44b6 | ||
|
|
cc26eb1332 | ||
|
|
366c7acb90 | ||
|
|
d8cb5206e8 | ||
|
|
90efebf02f | ||
|
|
960cd3ad8b | ||
|
|
09a3e807c9 | ||
|
|
fb2718b495 | ||
|
|
1550cb7fcc | ||
|
|
7b27bc8f7a | ||
|
|
54598fbf54 | ||
|
|
bb844eaa40 | ||
|
|
4ef6f584c9 | ||
|
|
861b3cc82f | ||
|
|
5226548cec | ||
|
|
d41ae01f01 | ||
|
|
e294059de4 | ||
|
|
84bc79eaa6 | ||
|
|
8f9a396821 | ||
|
|
6f96c389c4 | ||
|
|
c55e31ed6f | ||
|
|
f3a0813fed | ||
|
|
c1602c323d | ||
|
|
1bceaa52ff | ||
|
|
86296a28df | ||
|
|
fe9b5023f6 | ||
|
|
d68223f63d | ||
|
|
1c274d6eb8 | ||
|
|
0e52bbe0f7 | ||
|
|
a177448315 | ||
|
|
ca3ff1b522 | ||
|
|
41f0b11469 | ||
|
|
ba0e471a19 | ||
|
|
84115a7316 | ||
|
|
4dc837854b | ||
|
|
64f9e309ef | ||
|
|
67a1050df6 | ||
|
|
d85865825e | ||
|
|
2704d3e7d4 | ||
|
|
0944fc7854 | ||
|
|
611c275092 | ||
|
|
e3e991e6ab | ||
|
|
f2cebcf95a | ||
|
|
4aca16a393 | ||
|
|
891bbb96aa | ||
|
|
d60acd3a47 | ||
|
|
7dc6499127 | ||
|
|
f28e746652 | ||
|
|
3c905d9061 | ||
|
|
091d3170fc | ||
|
|
bdc4138d78 | ||
|
|
e231235ed1 | ||
|
|
1795b9196b | ||
|
|
d5d0563544 | ||
|
|
723b7a9857 | ||
|
|
6a0a16662e | ||
|
|
f82ba852ba | ||
|
|
180945670b | ||
|
|
56984ef7fb | ||
|
|
ac4de84188 | ||
|
|
591ad79201 | ||
|
|
2e51c9e9c3 | ||
|
|
ca89f01f7d | ||
|
|
5dfc118f64 | ||
|
|
2410a7b3ed | ||
|
|
7e11924b3f | ||
|
|
4f65900688 | ||
|
|
20821827b3 | ||
|
|
3dd10169e3 | ||
|
|
8cd60a84d5 | ||
|
|
03a2f4c331 | ||
|
|
cfc9b55fce | ||
|
|
fc99eb3e22 | ||
|
|
93625f9c4f | ||
|
|
b6ce9f7b2e | ||
|
|
20642b3776 | ||
|
|
16ab7c08f6 | ||
|
|
7bce7f28dc | ||
|
|
8c695c96d7 | ||
|
|
d4a057e21a | ||
|
|
f18c809355 | ||
|
|
0d9c8d9a43 | ||
|
|
76e7f81a6c | ||
|
|
faf00dfea7 | ||
|
|
39af90e63d | ||
|
|
36b09b8e94 | ||
|
|
7ec2b46d75 | ||
|
|
7cc5771a9a | ||
|
|
f96b5f8965 | ||
|
|
07e6f33b04 | ||
|
|
f1a33a1845 | ||
|
|
0a72566378 | ||
|
|
a029add486 | ||
|
|
778b8749ea | ||
|
|
7d5944fccf | ||
|
|
3e16a24ab5 | ||
|
|
ff682da3fe | ||
|
|
a0120485de | ||
|
|
0c8e9198ee | ||
|
|
40da2481cc | ||
|
|
a7ae56481b | ||
|
|
f550d58ad7 | ||
|
|
f488b75ac6 | ||
|
|
4308e386b4 | ||
|
|
df1c207d77 | ||
|
|
8ca71967b6 | ||
|
|
c99d09ea63 | ||
|
|
9ce65de698 | ||
|
|
cc7953c486 | ||
|
|
d50f330ccf | ||
|
|
36f2a23aa1 | ||
|
|
14b98d4006 | ||
|
|
d401136966 | ||
|
|
d0610b152f | ||
|
|
fa8428ae8b | ||
|
|
7d1a4b4005 | ||
|
|
19e3688499 | ||
|
|
b8da6f8cfd | ||
|
|
58631c5b19 | ||
|
|
e994768318 | ||
|
|
c304d72d77 | ||
|
|
a1f1016fb9 | ||
|
|
913f01d69f | ||
|
|
f56929c852 | ||
|
|
e9e0498b34 | ||
|
|
b6026ee76a | ||
|
|
8fdc358cc5 | ||
|
|
fef0ac6049 | ||
|
|
e144db9205 | ||
|
|
825a5a0d43 | ||
|
|
6dccfc862c | ||
|
|
771f93f9af | ||
|
|
7893fc9ebe | ||
|
|
9b4363dda8 | ||
|
|
257263bfa2 | ||
|
|
8f009bb4ee | ||
|
|
34a3cf82e7 | ||
|
|
cd927ec2c4 | ||
|
|
1266b2378a | ||
|
|
575d158d5a | ||
|
|
a008faf805 | ||
|
|
9a2081de0a | ||
|
|
9550b817c5 | ||
|
|
ee895e7dc4 | ||
|
|
7aa2996540 | ||
|
|
725aa5b180 | ||
|
|
f96f14052e | ||
|
|
adfa9f9e0f | ||
|
|
d8bc336006 | ||
|
|
b69bd3d640 | ||
|
|
5d9ac50106 | ||
|
|
df83ffc8b0 | ||
|
|
e49b568d6d | ||
|
|
8399e49072 | ||
|
|
d76a65ec07 | ||
|
|
ed7cef7566 | ||
|
|
3349ec7694 | ||
|
|
11f358bcbf | ||
|
|
82608d01bb | ||
|
|
a93f4f68b4 | ||
|
|
6f8c576a44 | ||
|
|
e75fc83412 | ||
|
|
2e538e3905 | ||
|
|
a69399fa58 | ||
|
|
3e94d27e6a | ||
|
|
20293b35f8 | ||
|
|
b768e48a74 | ||
|
|
6a64c1ffd6 | ||
|
|
3b1ad2250f | ||
|
|
83778afd51 | ||
|
|
cbbae11fcd | ||
|
|
11c01af9da | ||
|
|
7547b33f65 | ||
|
|
4305f589df | ||
|
|
c8684f74a8 | ||
|
|
7cdc92c142 | ||
|
|
a7ccf302f2 | ||
|
|
f4b41efc75 | ||
|
|
ac18d3df96 | ||
|
|
daff47d912 | ||
|
|
27d04cbae2 | ||
|
|
8eef7718ee | ||
|
|
fb4259b696 | ||
|
|
eb378ea45a | ||
|
|
02992271a1 | ||
|
|
72b11c2717 | ||
|
|
a55f28c006 | ||
|
|
9f37962d22 | ||
|
|
8d001122e2 | ||
|
|
960aa2231c | ||
|
|
e55a133f00 | ||
|
|
4075c92e53 | ||
|
|
743e5509a2 | ||
|
|
1c93611a16 | ||
|
|
df8a320fbe | ||
|
|
8e8464d727 | ||
|
|
356d1dfe4d | ||
|
|
d185de0f3d | ||
|
|
644a6cdcc2 | ||
|
|
85f5fb2d87 | ||
|
|
8674c14754 | ||
|
|
6c2fc1b4c2 | ||
|
|
5d94980e7c | ||
|
|
9bed71a99f | ||
|
|
6faa2b65e2 | ||
|
|
98812b103e | ||
|
|
deb655de36 | ||
|
|
8d1c18cb4a | ||
|
|
88824126f7 | ||
|
|
68a9ecf995 | ||
|
|
57404b2dcd | ||
|
|
29705c63c9 | ||
|
|
5a854d9715 | ||
|
|
eb1cbeae3f | ||
|
|
b6246cfee6 | ||
|
|
0619000f9a | ||
|
|
01361781c8 | ||
|
|
3c03a308b3 | ||
|
|
7895e48420 | ||
|
|
ba15713b99 | ||
|
|
bd98fa3aed | ||
|
|
fce75a9475 | ||
|
|
7616d5759f | ||
|
|
ea2c81a9c7 | ||
|
|
37cff9ff31 | ||
|
|
b8d3d6af9b | ||
|
|
fdf90a72cc | ||
|
|
ba0c05a774 | ||
|
|
3250ec14ac | ||
|
|
ce2f390361 | ||
|
|
71434232fe | ||
|
|
852629fa2e | ||
|
|
f10676d16d | ||
|
|
d43ea7df1d | ||
|
|
21712c6299 | ||
|
|
2fc47a38fa | ||
|
|
cbb4408668 | ||
|
|
a0df41b859 | ||
|
|
494c1b33b4 | ||
|
|
2034d91912 | ||
|
|
235ced0b78 | ||
|
|
3339b6a16f | ||
|
|
c08e3dfe4a | ||
|
|
894cf41d37 | ||
|
|
55aa869b1a | ||
|
|
42b18619c3 | ||
|
|
b0c57dfec5 | ||
|
|
bdbcf2a5f8 | ||
|
|
7807e68519 | ||
|
|
4cd81cb2e9 | ||
|
|
dc34a33346 | ||
|
|
2922854f96 | ||
|
|
29c85442ff | ||
|
|
048cbf51ca | ||
|
|
467dd2ea65 | ||
|
|
aef678be5c | ||
|
|
77f7567b29 | ||
|
|
c3c0685d3a | ||
|
|
c4c3f79c8c | ||
|
|
9cb54a34da | ||
|
|
519ae62760 | ||
|
|
6971da1abc | ||
|
|
5f3054d344 | ||
|
|
5b586a7402 | ||
|
|
f2ff66ec1d | ||
|
|
92b9bab76a | ||
|
|
4609985b79 | ||
|
|
6cf14fc7dd | ||
|
|
053363f781 | ||
|
|
e319bd61f1 | ||
|
|
baad83172f | ||
|
|
4b288a59b9 | ||
|
|
a2ada0b67e | ||
|
|
97fea6a93a | ||
|
|
145a9fb0b1 | ||
|
|
202d5e41a1 | ||
|
|
a64bead5e6 | ||
|
|
f9b84f8cb1 | ||
|
|
76e0c13f89 | ||
|
|
f9319c2a27 | ||
|
|
295c50a0ae | ||
|
|
942f5abbe4 | ||
|
|
965df40476 | ||
|
|
11f7f07009 | ||
|
|
76cd2afbd3 | ||
|
|
00d07cb7af | ||
|
|
3234c18718 | ||
|
|
d48285c450 | ||
|
|
98ea387254 | ||
|
|
fb80ff3b74 | ||
|
|
41e0cf8424 | ||
|
|
1bcd321477 | ||
|
|
b0482ee84b | ||
|
|
404f4041a9 | ||
|
|
f6d6375667 | ||
|
|
04acf4af49 | ||
|
|
df6957cfb6 | ||
|
|
d3ab1e1560 | ||
|
|
598a255e34 | ||
|
|
b4418fda55 | ||
|
|
3f26dde791 | ||
|
|
14d81411b1 | ||
|
|
4b667835f9 | ||
|
|
002f971882 | ||
|
|
99a8fcd73a | ||
|
|
3705da38b7 | ||
|
|
32afee62c5 | ||
|
|
a5ecb4bf7d | ||
|
|
1a424ebcd0 | ||
|
|
6a44331edd | ||
|
|
4e78f43649 | ||
|
|
ca7ac08808 | ||
|
|
3351450d88 | ||
|
|
fa82bdec79 | ||
|
|
0860a105ed | ||
|
|
b834307178 | ||
|
|
fafe24f1e4 | ||
|
|
6f1ec04ac8 | ||
|
|
263278d71d | ||
|
|
9ee5b044b4 | ||
|
|
21bfa14b55 | ||
|
|
0e2d717aa6 | ||
|
|
f5d2bd78b9 | ||
|
|
ef9f764fa0 | ||
|
|
3cfe1a6960 | ||
|
|
9387a48b5b | ||
|
|
3812150669 | ||
|
|
4ca100b0bf | ||
|
|
43dee54678 | ||
|
|
14786a94bf | ||
|
|
98a652f440 | ||
|
|
a004ed8064 | ||
|
|
37b0edde38 | ||
|
|
fd9f19562f | ||
|
|
acb7b42ded | ||
|
|
f1e4a523ce | ||
|
|
676ea312a3 | ||
|
|
6acd930455 | ||
|
|
340db0c644 | ||
|
|
f3d66b6abb | ||
|
|
6b8ae2e864 | ||
|
|
664cb2f2f9 | ||
|
|
486292434e | ||
|
|
e5f186ace1 | ||
|
|
0c637d4c8e | ||
|
|
0943023a19 | ||
|
|
941d6d2884 | ||
|
|
169af4ccce | ||
|
|
6f1d0f8c02 | ||
|
|
dd970b9a91 | ||
|
|
b997cb650e | ||
|
|
dd3ccf1d2f | ||
|
|
52a7977461 | ||
|
|
70713c0719 | ||
|
|
b4d6e1ef04 | ||
|
|
5295701541 | ||
|
|
43b9cea801 | ||
|
|
926d48af81 | ||
|
|
962c48ca36 | ||
|
|
03fc569f87 | ||
|
|
e5b712ff39 | ||
|
|
c7f7d0409e | ||
|
|
e4ec7ea5bb | ||
|
|
4bf734778b | ||
|
|
461325c8ca | ||
|
|
efa2ec6aac | ||
|
|
eb8be40f77 | ||
|
|
ae7e3c9c89 | ||
|
|
4d68cc9fce | ||
|
|
0226e2739a | ||
|
|
caa163a879 | ||
|
|
0e29010897 | ||
|
|
e9a8b99f4f | ||
|
|
27c637ba66 | ||
|
|
305db3395c | ||
|
|
3da37f0a3e | ||
|
|
c30c9192f4 | ||
|
|
ceb3df513d | ||
|
|
83372c4209 | ||
|
|
20d5e014b7 | ||
|
|
1c9a7eb158 | ||
|
|
31555cbf2f | ||
|
|
4a1d4644ed | ||
|
|
9182d214af | ||
|
|
45e3383031 | ||
|
|
fc91b69b55 | ||
|
|
13b6614dc2 | ||
|
|
fec5e4d73f | ||
|
|
585add29fc | ||
|
|
1e48e59cb7 | ||
|
|
41911af09a | ||
|
|
37b95e9559 | ||
|
|
a7901745e8 | ||
|
|
da9f085a50 | ||
|
|
192c2a8bd6 | ||
|
|
e6c00e9b68 | ||
|
|
1faa5aa5d8 | ||
|
|
7e7191536a | ||
|
|
16ff1975ff | ||
|
|
b32c86863e | ||
|
|
d9b361f765 | ||
|
|
5b2810e6c5 | ||
|
|
15a5b347e8 | ||
|
|
477ffa825b | ||
|
|
84363b1513 | ||
|
|
01bf6f049b | ||
|
|
a8d93de000 | ||
|
|
2da5de3743 | ||
|
|
bf6bac7be6 | ||
|
|
394d2e4a08 | ||
|
|
0dd447e270 | ||
|
|
92c913e3eb | ||
|
|
5dea461963 | ||
|
|
f768081849 | ||
|
|
668418c48a | ||
|
|
2bf387a57c | ||
|
|
42a4726985 | ||
|
|
5c4b5359e0 | ||
|
|
8e3f56a3a2 | ||
|
|
91a837477a | ||
|
|
a2dfc54068 | ||
|
|
7ded2fe13a | ||
|
|
b6f1b27a81 | ||
|
|
621fd614ea | ||
|
|
be3e7c892c | ||
|
|
1f640b7644 | ||
|
|
987b505d32 | ||
|
|
4b2f23f093 | ||
|
|
1d52d0bafc | ||
|
|
e73db14976 | ||
|
|
fe8a677541 | ||
|
|
926cba8197 | ||
|
|
ee6e5db0a6 | ||
|
|
0c62d8dd07 | ||
|
|
d6e352b3b4 | ||
|
|
59e46be262 | ||
|
|
746c048550 | ||
|
|
a92c584997 | ||
|
|
7f4c6eb4ac | ||
|
|
fbeb048c44 | ||
|
|
9f748a8119 | ||
|
|
b9fd590095 | ||
|
|
430e210b3c | ||
|
|
706d54db76 | ||
|
|
9ac276767d | ||
|
|
29144348b3 | ||
|
|
2f91b11672 | ||
|
|
caf0ef88ac | ||
|
|
c8154db108 | ||
|
|
7ce6b5592a | ||
|
|
e1bc4fdf36 | ||
|
|
fd7f5f719f | ||
|
|
6678ca6c93 | ||
|
|
180e03e249 | ||
|
|
357f15eb01 | ||
|
|
a08704b60f | ||
|
|
03ba81a9c5 | ||
|
|
2933962df8 | ||
|
|
4f248c4dc7 | ||
|
|
14096f24ba | ||
|
|
c0b75accf4 | ||
|
|
cb1b7d5da7 | ||
|
|
c8072171f0 | ||
|
|
485f6d4cef | ||
|
|
2b85097e30 | ||
|
|
0904789291 | ||
|
|
fda69793a5 | ||
|
|
3c22aaf99d | ||
|
|
151a5d480f | ||
|
|
88d136626d | ||
|
|
170f5bba79 | ||
|
|
c4cbb0cd59 | ||
|
|
0fa04070f1 | ||
|
|
f8bea241a2 | ||
|
|
bb6b2e4b29 | ||
|
|
557251a679 | ||
|
|
696783b0fe | ||
|
|
6eb29d2cc9 | ||
|
|
4b4cafcda8 | ||
|
|
ed2ed8d968 | ||
|
|
fbb387bd9f | ||
|
|
0db2db482f | ||
|
|
af2fe727fc | ||
|
|
a7387dcf22 | ||
|
|
5ce44fb161 | ||
|
|
9086b9c3ca | ||
|
|
4837c5387a | ||
|
|
5d73216238 | ||
|
|
5053091e44 | ||
|
|
6319ed0473 | ||
|
|
c67c109af6 | ||
|
|
b3cdb1464b | ||
|
|
3c01c3f0ba | ||
|
|
07d541be20 | ||
|
|
4a0dc59585 | ||
|
|
b1dd8f52e8 | ||
|
|
6907fe230f | ||
|
|
f2e4071b90 | ||
|
|
452f5e5f83 | ||
|
|
c94b9f54ce | ||
|
|
e993f93cf4 | ||
|
|
e9fac38c1b | ||
|
|
31a49b8ff9 | ||
|
|
7831cdf10c | ||
|
|
c0f176f0af | ||
|
|
dc96a557eb | ||
|
|
7a8c915e62 | ||
|
|
dec42cc72b | ||
|
|
92c9d1d2ce | ||
|
|
de017fcd27 | ||
|
|
1229f62abd | ||
|
|
512852b817 | ||
|
|
5a087000a9 | ||
|
|
6b56d6649e | ||
|
|
5081aab497 | ||
|
|
bbb4afde53 | ||
|
|
d28495c3eb | ||
|
|
1785e0c4a3 | ||
|
|
1e93e75aac | ||
|
|
a3ed7eeee2 | ||
|
|
c3582637df | ||
|
|
3b277b0eb0 | ||
|
|
5e0991681f | ||
|
|
354fbb7cdd | ||
|
|
a166d06096 | ||
|
|
b8a6e07095 | ||
|
|
a8fba6e7b5 | ||
|
|
17a8c10f8c | ||
|
|
7ccd29faac | ||
|
|
d432e48cce | ||
|
|
19091319cc | ||
|
|
d29e12a92c | ||
|
|
5db03b0441 | ||
|
|
f8c7e2bf6d | ||
|
|
a7fdc6a88f | ||
|
|
4aaf5b0779 | ||
|
|
6150e4c736 | ||
|
|
9a3b4b3ecb | ||
|
|
cbaeb4d5f2 | ||
|
|
592c306aa0 | ||
|
|
136e6bddf4 | ||
|
|
0e8bef0541 | ||
|
|
9d8c52303e | ||
|
|
e6ae7df546 | ||
|
|
5b57a759be | ||
|
|
cc96eeae32 | ||
|
|
484fef1228 | ||
|
|
ba4ca11c83 | ||
|
|
9372902348 | ||
|
|
0cc9420570 | ||
|
|
18fa48d283 | ||
|
|
9034fedbd0 | ||
|
|
ec3b6ec5fa | ||
|
|
8cc2a4174e | ||
|
|
8a0014928d | ||
|
|
228ca7f9c0 | ||
|
|
c0fa7efb27 | ||
|
|
3553683bb1 | ||
|
|
ccb58e0a6c | ||
|
|
93253e2188 | ||
|
|
e34bcc9bbd | ||
|
|
72169e3a4d | ||
|
|
41c499840f | ||
|
|
f5cef170f0 | ||
|
|
d7922a7c9b | ||
|
|
23fc77e8e1 | ||
|
|
51f3eda09a | ||
|
|
6ec08a5227 | ||
|
|
4855f694cb | ||
|
|
eaef37a831 | ||
|
|
b182ff36bb | ||
|
|
c4e543449c | ||
|
|
c76d9d4461 | ||
|
|
dfd184724b | ||
|
|
fef254031e | ||
|
|
89c4e59bf1 | ||
|
|
2e9acd3276 | ||
|
|
5cab59c93f | ||
|
|
ebfb5878a1 | ||
|
|
de6b11aa22 | ||
|
|
9e7e7159e8 | ||
|
|
00c909271b | ||
|
|
7401a0cbec | ||
|
|
a9327d1774 | ||
|
|
9e99ef1348 | ||
|
|
5b4b9fdb01 | ||
|
|
d2998c4908 | ||
|
|
4ab968d563 | ||
|
|
9b26eea754 | ||
|
|
a6bd717971 | ||
|
|
f2914adb65 | ||
|
|
aa5a426aac | ||
|
|
fef8144187 | ||
|
|
d8a429d3b3 | ||
|
|
56e53b75a5 | ||
|
|
f89b6f5feb | ||
|
|
ec513e8893 | ||
|
|
35d6792d2e | ||
|
|
74387aae2d | ||
|
|
959043962d | ||
|
|
4efb71bafb | ||
|
|
6fd0bc0445 | ||
|
|
176a2eb392 | ||
|
|
1a85deb36e | ||
|
|
5b409ff3de | ||
|
|
a9e13d3a92 | ||
|
|
0698d8e3b1 | ||
|
|
8e4c496b54 | ||
|
|
27054b9722 | ||
|
|
bcad2c4998 | ||
|
|
aae2bd4884 | ||
|
|
006ef6f672 | ||
|
|
7be1a61347 | ||
|
|
1ef48f6cd6 | ||
|
|
a9a061a4bb | ||
|
|
14ed8b7af7 | ||
|
|
d17b8fa720 |
BIN
.github/hua_nobg_512.gif
vendored
Normal file
BIN
.github/hua_nobg_512.gif
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 187 KiB |
49
.github/workflows/gomod2nix.yml
vendored
Normal file
49
.github/workflows/gomod2nix.yml
vendored
Normal file
@@ -0,0 +1,49 @@
|
||||
name: 自动更新 nix 依赖
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
paths:
|
||||
- 'go.mod'
|
||||
- 'go.sum'
|
||||
- '.github/workflows/gomod2nix.yml'
|
||||
jobs:
|
||||
gomod2nix:
|
||||
name: gomod2nix update
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Set up nix
|
||||
uses: cachix/install-nix-action@v27
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@master
|
||||
with:
|
||||
go-version: "1.25"
|
||||
|
||||
- name: Check out code into the Go module directory
|
||||
uses: actions/checkout@master
|
||||
with:
|
||||
ref: master
|
||||
fetch-depth: 0
|
||||
|
||||
- name: gomod2nix update
|
||||
run: |
|
||||
nix run github:nix-community/gomod2nix
|
||||
- name: Commit back
|
||||
if: ${{ !github.head_ref }}
|
||||
continue-on-error: true
|
||||
run: |
|
||||
git config --local user.name 'github-actions[bot]'
|
||||
git config --local user.email '41898282+github-actions[bot]@users.noreply.github.com'
|
||||
git add --all
|
||||
git commit -m "chore(nix): bump deps"
|
||||
- name: Create Pull Request
|
||||
if: ${{ !github.head_ref }}
|
||||
continue-on-error: true
|
||||
uses: peter-evans/create-pull-request@v8
|
||||
with:
|
||||
delete-branch: true
|
||||
branch-suffix: short-commit-hash
|
||||
title: "chore(nix): bump deps"
|
||||
20
.github/workflows/nightly.yml
vendored
20
.github/workflows/nightly.yml
vendored
@@ -22,15 +22,20 @@ jobs:
|
||||
goarch: arm
|
||||
- goos: windows
|
||||
goarch: arm64
|
||||
- goos: windows
|
||||
goarch: 386
|
||||
fail-fast: true
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Setup Go environment
|
||||
uses: actions/setup-go@v2.1.3
|
||||
- uses: actions/checkout@master
|
||||
with:
|
||||
go-version: 1.18
|
||||
fetch-depth: 0
|
||||
- name: Setup Go environment
|
||||
uses: actions/setup-go@master
|
||||
with:
|
||||
go-version: '1.25'
|
||||
- name: Cache downloaded module
|
||||
uses: actions/cache@v2
|
||||
uses: actions/cache@master
|
||||
continue-on-error: true
|
||||
with:
|
||||
path: |
|
||||
~/.cache/go-build
|
||||
@@ -42,14 +47,15 @@ jobs:
|
||||
GOARCH: ${{ matrix.goarch }}
|
||||
IS_PR: ${{ !!github.head_ref }}
|
||||
run: |
|
||||
GOOS= GOARCH= go generate ./...
|
||||
if [ $GOOS = "windows" ]; then export BINARY_SUFFIX="$BINARY_SUFFIX.exe"; fi
|
||||
if $IS_PR ; then echo $PR_PROMPT; fi
|
||||
export BINARY_NAME="$BINARY_PREFIX$GOOS_$GOARCH$BINARY_SUFFIX"
|
||||
export CGO_ENABLED=0
|
||||
go build -o "output/$BINARY_NAME" -trimpath -ldflags "$LD_FLAGS" .
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@master
|
||||
if: ${{ !github.head_ref }}
|
||||
with:
|
||||
name: ${{ matrix.goos }}_${{ matrix.goarch }}
|
||||
path: output/
|
||||
path: output/
|
||||
|
||||
34
.github/workflows/pull.yml
vendored
34
.github/workflows/pull.yml
vendored
@@ -1,17 +1,43 @@
|
||||
name: PullLint
|
||||
on: [ pull_request ]
|
||||
on:
|
||||
pull_request_target:
|
||||
types: [assigned, opened, synchronize, reopened]
|
||||
jobs:
|
||||
# This workflow closes invalid PR
|
||||
close-pr:
|
||||
name: closepr
|
||||
# The type of runner that the job will run on
|
||||
runs-on: ubuntu-latest
|
||||
permissions: write-all
|
||||
|
||||
# Steps represent a sequence of tasks that will be executed as part of the job
|
||||
steps:
|
||||
- name: Close PR if commit message contains ".go"
|
||||
if: contains(github.event.pull_request.title, '.go')
|
||||
uses: superbrothers/close-pull-request@v3
|
||||
with:
|
||||
# Optional. Post a issue comment just before closing a pull request.
|
||||
comment: "非法PR. 请`fork`后修改自己的仓库, 而不是向主仓库提交更改. 如果您确信您的PR是为了给主仓库新增功能或修复bug, 请更改默认PR标题. **注意**: 如果您再次触发本提示, 则有可能导致账号被封禁."
|
||||
|
||||
golangci:
|
||||
name: lint
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v2
|
||||
uses: actions/setup-go@master
|
||||
with:
|
||||
go-version: 1.18
|
||||
go-version: '1.24'
|
||||
|
||||
- name: Check out code into the Go module directory
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Tidy Modules
|
||||
run: |
|
||||
go mod tidy
|
||||
go generate main.go
|
||||
|
||||
- name: golangci-lint
|
||||
uses: golangci/golangci-lint-action@master
|
||||
|
||||
35
.github/workflows/push.yml
vendored
35
.github/workflows/push.yml
vendored
@@ -1,28 +1,47 @@
|
||||
name: PushLint
|
||||
on: [ push ]
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
jobs:
|
||||
golangci:
|
||||
name: lint
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v2
|
||||
uses: actions/setup-go@master
|
||||
with:
|
||||
go-version: 1.18
|
||||
go-version: '1.25'
|
||||
|
||||
- name: Check out code into the Go module directory
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@master
|
||||
with:
|
||||
ref: master
|
||||
fetch-depth: 0
|
||||
submodules: 'recursive'
|
||||
|
||||
- name: golangci-lint
|
||||
- name: Tidy Modules
|
||||
run: |
|
||||
go mod tidy
|
||||
go generate main.go
|
||||
|
||||
- name: Run Lint
|
||||
uses: golangci/golangci-lint-action@master
|
||||
with:
|
||||
version: latest
|
||||
args: --issues-exit-code=0
|
||||
|
||||
- name: Commit back
|
||||
if: ${{ !github.head_ref }}
|
||||
continue-on-error: true
|
||||
run: |
|
||||
git config --local user.name 'github-actions[bot]'
|
||||
git config --local user.email '41898282+github-actions[bot]@users.noreply.github.com'
|
||||
git add --all
|
||||
git commit -m "🎨 改进代码样式"
|
||||
git push
|
||||
git commit -m "chore(lint): 改进代码样式"
|
||||
|
||||
- name: Create Pull Request
|
||||
if: ${{ !github.head_ref }}
|
||||
continue-on-error: true
|
||||
uses: peter-evans/create-pull-request@v8
|
||||
with:
|
||||
title: "chore(lint): 改进代码样式"
|
||||
|
||||
12
.github/workflows/release.yml
vendored
12
.github/workflows/release.yml
vendored
@@ -9,19 +9,19 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2.3.4
|
||||
uses: actions/checkout@master
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v2
|
||||
uses: actions/setup-go@master
|
||||
with:
|
||||
go-version: '1.18'
|
||||
go-version: '1.25'
|
||||
|
||||
- name: Run GoReleaser
|
||||
uses: goreleaser/goreleaser-action@v2
|
||||
uses: goreleaser/goreleaser-action@master
|
||||
with:
|
||||
version: latest
|
||||
args: release --rm-dist
|
||||
version: "~> v2"
|
||||
args: release --clean
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
BIN
.github/yaya.jpg
vendored
BIN
.github/yaya.jpg
vendored
Binary file not shown.
|
Before Width: | Height: | Size: 63 KiB |
6
.gitignore
vendored
6
.gitignore
vendored
@@ -5,4 +5,8 @@ plugins/*.dll
|
||||
.vscode
|
||||
go-zero*
|
||||
nohup.out
|
||||
zerobot
|
||||
zerobot
|
||||
ZeroBot-Plugin*
|
||||
*.syso
|
||||
/.direnv
|
||||
/result
|
||||
@@ -1,6 +1,5 @@
|
||||
linters-settings:
|
||||
errcheck:
|
||||
ignore: fmt:.*
|
||||
ignoretests: true
|
||||
|
||||
goimports:
|
||||
@@ -18,11 +17,10 @@ linters:
|
||||
fast: false
|
||||
enable:
|
||||
- bodyclose
|
||||
- deadcode
|
||||
- depguard
|
||||
#- depguard
|
||||
- dogsled
|
||||
- errcheck
|
||||
- exportloopref
|
||||
#- exportloopref
|
||||
- exhaustive
|
||||
#- funlen
|
||||
#- goconst
|
||||
@@ -39,13 +37,11 @@ linters:
|
||||
- nolintlint
|
||||
- rowserrcheck
|
||||
- staticcheck
|
||||
- structcheck
|
||||
- stylecheck
|
||||
- typecheck
|
||||
- unconvert
|
||||
- unparam
|
||||
- unused
|
||||
- varcheck
|
||||
- whitespace
|
||||
- prealloc
|
||||
- predeclared
|
||||
@@ -60,20 +56,22 @@ run:
|
||||
deadline: 5m
|
||||
issues-exit-code: 1
|
||||
tests: false
|
||||
go: '1.24'
|
||||
skip-dirs:
|
||||
- order
|
||||
go: '1.18'
|
||||
- data
|
||||
|
||||
# output configuration options
|
||||
output:
|
||||
format: "colored-line-number"
|
||||
formats:
|
||||
- format: "colored-line-number"
|
||||
print-issued-lines: true
|
||||
print-linter-name: true
|
||||
uniq-by-line: true
|
||||
|
||||
issues:
|
||||
# Fix found issues (if it's supported by the linter)
|
||||
fix: true
|
||||
exclude-use-default: false
|
||||
uniq-by-line: true
|
||||
exclude:
|
||||
- "Error return value of .((os.)?std(out|err)..*|.*Close|.*Seek|.*Flush|os.Remove(All)?|.*print(f|ln)?|os.(Un)?Setenv). is not check"
|
||||
- "Error return value of .((os.)?std(out|err)..*|.*Close|.*Seek|.*Flush|os.Remove(All)?|.*print(f|ln)?|os.(Un)?Setenv). is not check"
|
||||
- 'identifier ".*" contain non-ASCII character: U\+.*'
|
||||
|
||||
@@ -4,6 +4,9 @@ env:
|
||||
before:
|
||||
hooks:
|
||||
- go mod tidy
|
||||
- go generate ./...
|
||||
- go install github.com/tc-hib/go-winres@latest
|
||||
- go-winres make
|
||||
builds:
|
||||
- id: nowin
|
||||
env:
|
||||
@@ -31,7 +34,6 @@ builds:
|
||||
goos:
|
||||
- windows
|
||||
goarch:
|
||||
- 386
|
||||
- amd64
|
||||
mod_timestamp: "{{ .CommitTimestamp }}"
|
||||
flags:
|
||||
@@ -61,10 +63,10 @@ archives:
|
||||
name_template: "zbp_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}"
|
||||
format_overrides:
|
||||
- goos: windows
|
||||
format: zip
|
||||
formats: zip
|
||||
|
||||
nfpms:
|
||||
- license: GPL 3.0
|
||||
- license: AGPL 3.0
|
||||
homepage: https://github.com/FloatTech/ZeroBot-Plugin
|
||||
file_name_template: "zbp_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}"
|
||||
formats:
|
||||
|
||||
145
LICENSE
145
LICENSE
@@ -1,5 +1,5 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
Version 3, 19 November 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
@@ -7,17 +7,15 @@
|
||||
|
||||
Preamble
|
||||
|
||||
The GNU General Public License is a free, copyleft license for
|
||||
software and other kinds of works.
|
||||
The GNU Affero General Public License is a free, copyleft license for
|
||||
software and other kinds of works, specifically designed to ensure
|
||||
cooperation with the community in the case of network server software.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
the GNU General Public License is intended to guarantee your freedom to
|
||||
our General Public Licenses are intended to guarantee your freedom to
|
||||
share and change all versions of a program--to make sure it remains free
|
||||
software for all its users. We, the Free Software Foundation, use the
|
||||
GNU General Public License for most of our software; it applies also to
|
||||
any other work released this way by its authors. You can apply it to
|
||||
your programs, too.
|
||||
software for all its users.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
@@ -26,44 +24,34 @@ them if you wish), that you receive source code or can get it if you
|
||||
want it, that you can change the software or use pieces of it in new
|
||||
free programs, and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to prevent others from denying you
|
||||
these rights or asking you to surrender the rights. Therefore, you have
|
||||
certain responsibilities if you distribute copies of the software, or if
|
||||
you modify it: responsibilities to respect the freedom of others.
|
||||
Developers that use our General Public Licenses protect your rights
|
||||
with two steps: (1) assert copyright on the software, and (2) offer
|
||||
you this License which gives you legal permission to copy, distribute
|
||||
and/or modify the software.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must pass on to the recipients the same
|
||||
freedoms that you received. You must make sure that they, too, receive
|
||||
or can get the source code. And you must show them these terms so they
|
||||
know their rights.
|
||||
A secondary benefit of defending all users' freedom is that
|
||||
improvements made in alternate versions of the program, if they
|
||||
receive widespread use, become available for other developers to
|
||||
incorporate. Many developers of free software are heartened and
|
||||
encouraged by the resulting cooperation. However, in the case of
|
||||
software used on network servers, this result may fail to come about.
|
||||
The GNU General Public License permits making a modified version and
|
||||
letting the public access it on a server without ever releasing its
|
||||
source code to the public.
|
||||
|
||||
Developers that use the GNU GPL protect your rights with two steps:
|
||||
(1) assert copyright on the software, and (2) offer you this License
|
||||
giving you legal permission to copy, distribute and/or modify it.
|
||||
The GNU Affero General Public License is designed specifically to
|
||||
ensure that, in such cases, the modified source code becomes available
|
||||
to the community. It requires the operator of a network server to
|
||||
provide the source code of the modified version running there to the
|
||||
users of that server. Therefore, public use of a modified version, on
|
||||
a publicly accessible server, gives the public access to the source
|
||||
code of the modified version.
|
||||
|
||||
For the developers' and authors' protection, the GPL clearly explains
|
||||
that there is no warranty for this free software. For both users' and
|
||||
authors' sake, the GPL requires that modified versions be marked as
|
||||
changed, so that their problems will not be attributed erroneously to
|
||||
authors of previous versions.
|
||||
|
||||
Some devices are designed to deny users access to install or run
|
||||
modified versions of the software inside them, although the manufacturer
|
||||
can do so. This is fundamentally incompatible with the aim of
|
||||
protecting users' freedom to change the software. The systematic
|
||||
pattern of such abuse occurs in the area of products for individuals to
|
||||
use, which is precisely where it is most unacceptable. Therefore, we
|
||||
have designed this version of the GPL to prohibit the practice for those
|
||||
products. If such problems arise substantially in other domains, we
|
||||
stand ready to extend this provision to those domains in future versions
|
||||
of the GPL, as needed to protect the freedom of users.
|
||||
|
||||
Finally, every program is threatened constantly by software patents.
|
||||
States should not allow patents to restrict development and use of
|
||||
software on general-purpose computers, but in those that do, we wish to
|
||||
avoid the special danger that patents applied to a free program could
|
||||
make it effectively proprietary. To prevent this, the GPL assures that
|
||||
patents cannot be used to render the program non-free.
|
||||
An older license, called the Affero General Public License and
|
||||
published by Affero, was designed to accomplish similar goals. This is
|
||||
a different license, not a version of the Affero GPL, but Affero has
|
||||
released a new version of the Affero GPL which permits relicensing under
|
||||
this license.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
@@ -72,7 +60,7 @@ modification follow.
|
||||
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GNU General Public License.
|
||||
"This License" refers to version 3 of the GNU Affero General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
@@ -549,35 +537,45 @@ to collect a royalty for further conveying from those to whom you convey
|
||||
the Program, the only way you could satisfy both those terms and this
|
||||
License would be to refrain entirely from conveying the Program.
|
||||
|
||||
13. Use with the GNU Affero General Public License.
|
||||
13. Remote Network Interaction; Use with the GNU General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, if you modify the
|
||||
Program, your modified version must prominently offer all users
|
||||
interacting with it remotely through a computer network (if your version
|
||||
supports such interaction) an opportunity to receive the Corresponding
|
||||
Source of your version by providing access to the Corresponding Source
|
||||
from a network server at no charge, through some standard or customary
|
||||
means of facilitating copying of software. This Corresponding Source
|
||||
shall include the Corresponding Source for any work covered by version 3
|
||||
of the GNU General Public License that is incorporated pursuant to the
|
||||
following paragraph.
|
||||
|
||||
Notwithstanding any other provision of this License, you have
|
||||
permission to link or combine any covered work with a work licensed
|
||||
under version 3 of the GNU Affero General Public License into a single
|
||||
under version 3 of the GNU General Public License into a single
|
||||
combined work, and to convey the resulting work. The terms of this
|
||||
License will continue to apply to the part which is the covered work,
|
||||
but the special requirements of the GNU Affero General Public License,
|
||||
section 13, concerning interaction through a network will apply to the
|
||||
combination as such.
|
||||
but the work with which it is combined will remain governed by version
|
||||
3 of the GNU General Public License.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
the GNU Affero General Public License from time to time. Such new versions
|
||||
will be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Program specifies that a certain numbered version of the GNU General
|
||||
Program specifies that a certain numbered version of the GNU Affero General
|
||||
Public License "or any later version" applies to it, you have the
|
||||
option of following the terms and conditions either of that numbered
|
||||
version or of any later version published by the Free Software
|
||||
Foundation. If the Program does not specify a version number of the
|
||||
GNU General Public License, you may choose any version ever published
|
||||
GNU Affero General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future
|
||||
versions of the GNU General Public License can be used, that proxy's
|
||||
versions of the GNU Affero General Public License can be used, that proxy's
|
||||
public statement of acceptance of a version permanently authorizes you
|
||||
to choose that version for the Program.
|
||||
|
||||
@@ -635,40 +633,29 @@ the "copyright" line and a pointer to where the full notice is found.
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
it under the terms of the GNU Affero General Public License as published
|
||||
by the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program does terminal interaction, make it output a short
|
||||
notice like this when it starts in an interactive mode:
|
||||
|
||||
<program> Copyright (C) <year> <name of author>
|
||||
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, your program's commands
|
||||
might be different; for a GUI interface, you would use an "about box".
|
||||
If your software can interact with users remotely through a computer
|
||||
network, you should also make sure that it provides a way for users to
|
||||
get its source. For example, if your program is a web application, its
|
||||
interface could display a "Source" link that leads users to an archive
|
||||
of the code. There are many ways you could offer source, and different
|
||||
solutions will be better for different programs; see section 13 for the
|
||||
specific requirements.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU GPL, see
|
||||
<https://www.gnu.org/licenses/>.
|
||||
|
||||
The GNU General Public License does not permit incorporating your program
|
||||
into proprietary programs. If your program is a subroutine library, you
|
||||
may consider it more useful to permit linking proprietary applications with
|
||||
the library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License. But first, please read
|
||||
<https://www.gnu.org/licenses/why-not-lgpl.html>.
|
||||
For more information on this, and how to apply and follow the GNU AGPL, see
|
||||
<https://www.gnu.org/licenses/>.
|
||||
164
abineundo/main.go
Normal file
164
abineundo/main.go
Normal file
@@ -0,0 +1,164 @@
|
||||
// Package abineundo provides an explicit "from the beginning" (Latin: "ab ineundō")
|
||||
// initialization anchor.
|
||||
//
|
||||
// Name origin:
|
||||
//
|
||||
// Latin phrase "ab ineundō" meaning "from which is to be begun".
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// Place this package at the very top of top-level main.go so its init (present
|
||||
// or future) executes before other plugin packages, filling in a predictable
|
||||
// plugin priority.
|
||||
//
|
||||
// Typical usage:
|
||||
//
|
||||
// import (
|
||||
// _ "github.com/your/module/abineundo" // priority anchor
|
||||
// // ... other imports ...
|
||||
// )
|
||||
//
|
||||
// A blank identifier import preserves ordering side-effects without expanding the
|
||||
// exported API surface.
|
||||
//
|
||||
// (No further code is required here; the package's presence alone defines ordering semantics.)
|
||||
package abineundo
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
_ "embed"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/FloatTech/zbputils/control"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
//go:embed ref/main/main.go
|
||||
var maincode string
|
||||
|
||||
//go:embed ref/custom/register.go
|
||||
var customcode string
|
||||
|
||||
const (
|
||||
statusnone = iota
|
||||
statushigh
|
||||
statushighend
|
||||
statusmid
|
||||
statusmidend
|
||||
statuslow
|
||||
statuslowend
|
||||
)
|
||||
|
||||
var (
|
||||
priore = regexp.MustCompile(`^\t// -{28}(高|中|低)优先级区-{28} //$`)
|
||||
mainpluginre = regexp.MustCompile(`^\t_ "github\.com/FloatTech/ZeroBot-Plugin/plugin/(\w+)"\s+// `)
|
||||
custpluginre = regexp.MustCompile(`^\t_ "github\.com/FloatTech/ZeroBot-Plugin/custom/plugin/(\w+)"\s+// `)
|
||||
)
|
||||
|
||||
func init() {
|
||||
highprios := make([]string, 0, 64)
|
||||
midprios := make([]string, 0, 64)
|
||||
lowprios := make([]string, 0, 64)
|
||||
|
||||
status := statusnone
|
||||
scanner := bufio.NewScanner(strings.NewReader(maincode))
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
|
||||
prioword := ""
|
||||
match := priore.FindStringSubmatch(line)
|
||||
if len(match) > 1 {
|
||||
prioword = match[1]
|
||||
}
|
||||
switch prioword {
|
||||
case "高":
|
||||
switch status {
|
||||
case statusnone:
|
||||
status = statushigh
|
||||
case statushigh:
|
||||
status = statushighend
|
||||
default:
|
||||
panic("unexpected")
|
||||
}
|
||||
case "中":
|
||||
switch status {
|
||||
case statushighend:
|
||||
status = statusmid
|
||||
case statusmid:
|
||||
status = statusmidend
|
||||
default:
|
||||
panic("unexpected")
|
||||
}
|
||||
case "低":
|
||||
switch status {
|
||||
case statusmidend:
|
||||
status = statuslow
|
||||
case statuslow:
|
||||
status = statuslowend
|
||||
default:
|
||||
panic("unexpected")
|
||||
}
|
||||
default:
|
||||
switch status {
|
||||
case statusnone: // 还未开始匹配
|
||||
continue
|
||||
case statuslowend: // 匹配已结束
|
||||
break
|
||||
default: // 继续匹配插件
|
||||
}
|
||||
}
|
||||
|
||||
// 在对应优先级区域内匹配插件
|
||||
if matches := mainpluginre.FindStringSubmatch(line); len(matches) > 1 {
|
||||
name := matches[1]
|
||||
switch status {
|
||||
case statushigh:
|
||||
highprios = append(highprios, name)
|
||||
case statusmid:
|
||||
midprios = append(midprios, name)
|
||||
case statuslow:
|
||||
lowprios = append(lowprios, name)
|
||||
default: // 在不该匹配到插件的区域匹配到
|
||||
panic("unexpected")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
custprios := make([]string, 0, 64)
|
||||
|
||||
scanner = bufio.NewScanner(strings.NewReader(customcode))
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
|
||||
if matches := custpluginre.FindStringSubmatch(line); len(matches) > 1 {
|
||||
custprios = append(custprios, matches[1])
|
||||
}
|
||||
}
|
||||
|
||||
// 生成最终插件优先级表
|
||||
m := make(map[string]uint64, 4*(len(highprios)+len(midprios)+len(lowprios)+len(custprios)))
|
||||
i := 0
|
||||
for _, name := range highprios {
|
||||
m[name] = (uint64(i) + 1) * 10
|
||||
logrus.Debugln("[ab] set high plugin", name, "prio to", m[name])
|
||||
i++
|
||||
}
|
||||
for _, name := range custprios {
|
||||
m[name] = (uint64(i) + 1) * 10
|
||||
logrus.Debugln("[ab] set cust plugin", name, "prio to", m[name])
|
||||
i++
|
||||
}
|
||||
for _, name := range midprios {
|
||||
m[name] = (uint64(i) + 1) * 10
|
||||
logrus.Debugln("[ab] set mid plugin", name, "prio to", m[name])
|
||||
i++
|
||||
}
|
||||
for _, name := range lowprios {
|
||||
m[name] = (uint64(i) + 1) * 10
|
||||
logrus.Debugln("[ab] set low plugin", name, "prio to", m[name])
|
||||
i++
|
||||
}
|
||||
|
||||
control.LoadCustomPriority(m)
|
||||
}
|
||||
2
abineundo/ref/custom/.gitignore
vendored
Normal file
2
abineundo/ref/custom/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
!.gitignore
|
||||
*
|
||||
54
abineundo/ref/main.go
Normal file
54
abineundo/ref/main.go
Normal file
@@ -0,0 +1,54 @@
|
||||
// Package main generate necessary files that needed for compiling
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"io"
|
||||
"os"
|
||||
"path"
|
||||
)
|
||||
|
||||
func main() {
|
||||
root := flag.String("r", "", "project root dir")
|
||||
flag.Parse()
|
||||
|
||||
fi, err := os.Open(path.Join(*root, "main.go"))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fo, err := os.Create(path.Join(*root, "abineundo/ref/main/main.go"))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
_, err = io.Copy(fo, fi)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fi.Close()
|
||||
fo.Close()
|
||||
|
||||
regf := path.Join(*root, "custom/register.go")
|
||||
tgtf := path.Join(*root, "abineundo/ref/custom/register.go")
|
||||
if _, err := os.Stat(regf); err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
_ = os.WriteFile(tgtf, []byte("// Package custom ...\npackage custom\n"), 0644)
|
||||
return
|
||||
}
|
||||
panic(err)
|
||||
}
|
||||
|
||||
fi, err = os.Open(regf)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fo, err = os.Create(tgtf)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
_, err = io.Copy(fo, fi)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fi.Close()
|
||||
fo.Close()
|
||||
}
|
||||
2
abineundo/ref/main/.gitignore
vendored
Normal file
2
abineundo/ref/main/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
!.gitignore
|
||||
*
|
||||
14
console/console_ansi.go
Normal file
14
console/console_ansi.go
Normal file
@@ -0,0 +1,14 @@
|
||||
//go:build !windows
|
||||
|
||||
// Package console sets console's behavior on init
|
||||
package console
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/FloatTech/ZeroBot-Plugin/kanban/banner"
|
||||
)
|
||||
|
||||
func init() {
|
||||
fmt.Print("\033]0;ZeroBot-Blugin " + banner.Version + " " + banner.Copyright + "\007")
|
||||
}
|
||||
150
console/console_windows.go
Normal file
150
console/console_windows.go
Normal file
@@ -0,0 +1,150 @@
|
||||
// Package console sets console's behavior on init
|
||||
package console
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"os"
|
||||
"strings"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/FloatTech/ZeroBot-Plugin/kanban/banner"
|
||||
)
|
||||
|
||||
var (
|
||||
//go:linkname modkernel32 golang.org/x/sys/windows.modkernel32
|
||||
modkernel32 *windows.LazyDLL
|
||||
procSetConsoleTitle = modkernel32.NewProc("SetConsoleTitleW")
|
||||
)
|
||||
|
||||
//go:linkname errnoErr golang.org/x/sys/windows.errnoErr
|
||||
func errnoErr(e syscall.Errno) error
|
||||
|
||||
func setConsoleTitle(title string) (err error) {
|
||||
var p0 *uint16
|
||||
p0, err = syscall.UTF16PtrFromString(title)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
r1, _, e1 := syscall.Syscall(procSetConsoleTitle.Addr(), 1, uintptr(unsafe.Pointer(p0)), 0, 0)
|
||||
if r1 == 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func init() {
|
||||
debugMode := os.Getenv("DEBUG_MODE") == "1"
|
||||
stdin := windows.Handle(os.Stdin.Fd())
|
||||
|
||||
var mode uint32
|
||||
err := windows.GetConsoleMode(stdin, &mode)
|
||||
if err != nil {
|
||||
if debugMode {
|
||||
logrus.Warnf("调试模式下忽略控制台模式获取失败: %v", err)
|
||||
return // 调试模式下直接返回,跳过后续配置
|
||||
} else {
|
||||
panic(err) // 非调试模式下 panic
|
||||
}
|
||||
}
|
||||
|
||||
mode &^= windows.ENABLE_QUICK_EDIT_MODE // 禁用快速编辑模式
|
||||
mode |= windows.ENABLE_EXTENDED_FLAGS // 启用扩展标志
|
||||
|
||||
mode &^= windows.ENABLE_MOUSE_INPUT // 禁用鼠标输入
|
||||
mode |= windows.ENABLE_PROCESSED_INPUT // 启用控制输入
|
||||
|
||||
mode &^= windows.ENABLE_INSERT_MODE // 禁用插入模式
|
||||
mode |= windows.ENABLE_ECHO_INPUT | windows.ENABLE_LINE_INPUT // 启用输入回显&逐行输入
|
||||
|
||||
mode &^= windows.ENABLE_WINDOW_INPUT // 禁用窗口输入
|
||||
mode &^= windows.ENABLE_VIRTUAL_TERMINAL_INPUT // 禁用虚拟终端输入
|
||||
|
||||
err = windows.SetConsoleMode(stdin, mode)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
stdout := windows.Handle(os.Stdout.Fd())
|
||||
err = windows.GetConsoleMode(stdout, &mode)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
mode |= windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING // 启用虚拟终端处理
|
||||
mode |= windows.ENABLE_PROCESSED_OUTPUT // 启用处理后的输出
|
||||
|
||||
err = windows.SetConsoleMode(stdout, mode)
|
||||
// windows 带颜色 log 自定义格式
|
||||
logrus.SetFormatter(&logFormat{hasColor: err == nil})
|
||||
if err != nil {
|
||||
logrus.Warnln("VT100设置失败, 将以无色模式输出")
|
||||
}
|
||||
|
||||
err = setConsoleTitle("ZeroBot-Plugin " + banner.Version + " " + banner.Copyright)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
colorCodePanic = "\x1b[1;31m" // color.Style{color.Bold, color.Red}.String()
|
||||
colorCodeFatal = "\x1b[1;31m" // color.Style{color.Bold, color.Red}.String()
|
||||
colorCodeError = "\x1b[31m" // color.Style{color.Red}.String()
|
||||
colorCodeWarn = "\x1b[33m" // color.Style{color.Yellow}.String()
|
||||
colorCodeInfo = "\x1b[37m" // color.Style{color.White}.String()
|
||||
colorCodeDebug = "\x1b[32m" // color.Style{color.Green}.String()
|
||||
colorCodeTrace = "\x1b[36m" // color.Style{color.Cyan}.String()
|
||||
colorReset = "\x1b[0m"
|
||||
)
|
||||
|
||||
// logFormat specialize for zbp
|
||||
type logFormat struct {
|
||||
hasColor bool
|
||||
}
|
||||
|
||||
// Format implements logrus.Formatter
|
||||
func (f logFormat) Format(entry *logrus.Entry) ([]byte, error) {
|
||||
buf := new(bytes.Buffer)
|
||||
|
||||
buf.WriteByte('[')
|
||||
if f.hasColor {
|
||||
buf.WriteString(getLogLevelColorCode(entry.Level))
|
||||
}
|
||||
buf.WriteString(strings.ToUpper(entry.Level.String()))
|
||||
if f.hasColor {
|
||||
buf.WriteString(colorReset)
|
||||
}
|
||||
buf.WriteString("] ")
|
||||
buf.WriteString(entry.Message)
|
||||
buf.WriteString(" \n")
|
||||
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
// getLogLevelColorCode 获取日志等级对应色彩code
|
||||
func getLogLevelColorCode(level logrus.Level) string {
|
||||
switch level {
|
||||
case logrus.PanicLevel:
|
||||
return colorCodePanic
|
||||
case logrus.FatalLevel:
|
||||
return colorCodeFatal
|
||||
case logrus.ErrorLevel:
|
||||
return colorCodeError
|
||||
case logrus.WarnLevel:
|
||||
return colorCodeWarn
|
||||
case logrus.InfoLevel:
|
||||
return colorCodeInfo
|
||||
case logrus.DebugLevel:
|
||||
return colorCodeDebug
|
||||
case logrus.TraceLevel:
|
||||
return colorCodeTrace
|
||||
|
||||
default:
|
||||
return colorCodeInfo
|
||||
}
|
||||
}
|
||||
4
custom/.gitignore
vendored
Normal file
4
custom/.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
!.gitignore
|
||||
!doc.go
|
||||
!plugin
|
||||
*
|
||||
2
custom/doc.go
Normal file
2
custom/doc.go
Normal file
@@ -0,0 +1,2 @@
|
||||
// Package custom 注册用户自定义插件于此
|
||||
package custom
|
||||
2
custom/plugin/.gitignore
vendored
Normal file
2
custom/plugin/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
!.gitignore
|
||||
*
|
||||
2
data
2
data
Submodule data updated: 5155c7318a...1b0abcd3fe
26
default.nix
Normal file
26
default.nix
Normal file
@@ -0,0 +1,26 @@
|
||||
{
|
||||
pkgs ? (
|
||||
let
|
||||
inherit (builtins) fetchTree fromJSON readFile;
|
||||
inherit ((fromJSON (readFile ./flake.lock)).nodes) nixpkgs gomod2nix;
|
||||
in
|
||||
import (fetchTree nixpkgs.locked) {
|
||||
overlays = [
|
||||
(import "${fetchTree gomod2nix.locked}/overlay.nix")
|
||||
];
|
||||
}
|
||||
),
|
||||
buildGoApplication ? pkgs.buildGoApplication,
|
||||
...
|
||||
}:
|
||||
buildGoApplication {
|
||||
pname = "ZeroBot-Plugin";
|
||||
version = "1.8.0";
|
||||
pwd = ./.;
|
||||
src = ./.;
|
||||
# spec go version manually bcs
|
||||
# https://github.com/nix-community/gomod2nix/blob/30e3c3a9ec4ac8453282ca7f67fca9e1da12c3e6/builder/default.nix#L130
|
||||
# do not work
|
||||
go = pkgs.go_1_20;
|
||||
modules = ./gomod2nix.toml;
|
||||
}
|
||||
102
flake.lock
generated
Normal file
102
flake.lock
generated
Normal file
@@ -0,0 +1,102 @@
|
||||
{
|
||||
"nodes": {
|
||||
"flake-utils": {
|
||||
"inputs": {
|
||||
"systems": "systems"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1731533236,
|
||||
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"gomod2nix": {
|
||||
"inputs": {
|
||||
"flake-utils": [
|
||||
"flake-utils"
|
||||
],
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1742209644,
|
||||
"narHash": "sha256-jMy1XqXqD0/tJprEbUmKilTkvbDY/C0ZGSsJJH4TNCE=",
|
||||
"owner": "nix-community",
|
||||
"repo": "gomod2nix",
|
||||
"rev": "8f3534eb8f6c5c3fce799376dc3b91bae6b11884",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-community",
|
||||
"repo": "gomod2nix",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1745391562,
|
||||
"narHash": "sha256-sPwcCYuiEopaafePqlG826tBhctuJsLx/mhKKM5Fmjo=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "8a2f738d9d1f1d986b5a4cd2fd2061a7127237d7",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixos-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs-with-go_1_20": {
|
||||
"locked": {
|
||||
"lastModified": 1710843028,
|
||||
"narHash": "sha256-CMbK45c4nSkGvayiEHFkGFH+doGPbgo3AWfecd2t1Fk=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "33c51330782cb486764eb598d5907b43dc87b4c2",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "33c51330782cb486764eb598d5907b43dc87b4c2",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"flake-utils": "flake-utils",
|
||||
"gomod2nix": "gomod2nix",
|
||||
"nixpkgs": "nixpkgs",
|
||||
"nixpkgs-with-go_1_20": "nixpkgs-with-go_1_20"
|
||||
}
|
||||
},
|
||||
"systems": {
|
||||
"locked": {
|
||||
"lastModified": 1681028828,
|
||||
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"type": "github"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
"version": 7
|
||||
}
|
||||
63
flake.nix
Normal file
63
flake.nix
Normal file
@@ -0,0 +1,63 @@
|
||||
{
|
||||
description = "基于 ZeroBot 的 OneBot 插件";
|
||||
|
||||
inputs.nixpkgs-with-go_1_20.url = "github:NixOS/nixpkgs/33c51330782cb486764eb598d5907b43dc87b4c2";
|
||||
inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
||||
inputs.flake-utils.url = "github:numtide/flake-utils";
|
||||
inputs.gomod2nix.url = "github:nix-community/gomod2nix";
|
||||
inputs.gomod2nix.inputs.nixpkgs.follows = "nixpkgs";
|
||||
inputs.gomod2nix.inputs.flake-utils.follows = "flake-utils";
|
||||
|
||||
outputs = {
|
||||
self,
|
||||
nixpkgs,
|
||||
nixpkgs-with-go_1_20,
|
||||
flake-utils,
|
||||
gomod2nix,
|
||||
...
|
||||
} @ inputs: let
|
||||
allSystems = flake-utils.lib.allSystems;
|
||||
in (
|
||||
flake-utils.lib.eachSystem allSystems
|
||||
(system: let
|
||||
old-nixpkgs = nixpkgs-with-go_1_20.legacyPackages.${system};
|
||||
pkgs = import nixpkgs {
|
||||
inherit system;
|
||||
|
||||
overlays = [
|
||||
(_: _: {
|
||||
go_1_20 = old-nixpkgs.go_1_20;
|
||||
})
|
||||
];
|
||||
};
|
||||
|
||||
# The current default sdk for macOS fails to compile go projects, so we use a newer one for now.
|
||||
# This has no effect on other platforms.
|
||||
callPackage = pkgs.darwin.apple_sdk_11_0.callPackage or pkgs.callPackage;
|
||||
in {
|
||||
# doCheck will fail at write files
|
||||
packages = rec {
|
||||
ZeroBot-Plugin = (callPackage ./. (inputs
|
||||
// {
|
||||
inherit (gomod2nix.legacyPackages.${system}) buildGoApplication;
|
||||
}))
|
||||
.overrideAttrs (_: {doCheck = false;});
|
||||
|
||||
default = ZeroBot-Plugin;
|
||||
|
||||
docker_builder = pkgs.dockerTools.buildLayeredImage {
|
||||
name = "ZeroBot-Plugin";
|
||||
tag = "latest";
|
||||
contents = [
|
||||
self.packages.${system}.ZeroBot-Plugin
|
||||
pkgs.cacert
|
||||
];
|
||||
};
|
||||
};
|
||||
devShells.default = callPackage ./shell.nix {
|
||||
inherit (gomod2nix.legacyPackages.${system}) mkGoEnv gomod2nix;
|
||||
};
|
||||
formatter = pkgs.alejandra;
|
||||
})
|
||||
);
|
||||
}
|
||||
132
go.mod
132
go.mod
@@ -1,55 +1,107 @@
|
||||
module github.com/FloatTech/ZeroBot-Plugin
|
||||
|
||||
go 1.18
|
||||
go 1.24.2
|
||||
|
||||
require (
|
||||
github.com/FloatTech/AnimeAPI v1.3.2-0.20220328090901-d01ce41ecaca
|
||||
github.com/FloatTech/sqlite v0.2.1
|
||||
github.com/FloatTech/zbputils v1.3.2-0.20220328090754-b949b4e868e5
|
||||
github.com/antchfx/htmlquery v1.2.4
|
||||
github.com/corona10/goimagehash v1.0.3
|
||||
github.com/fogleman/gg v1.3.0
|
||||
github.com/Baidu-AIP/golang-sdk v1.1.1
|
||||
github.com/FloatTech/AnimeAPI v1.7.1-0.20251028071248-0c948e3db65c
|
||||
github.com/FloatTech/floatbox v0.0.0-20251002074805-f95cbc7edb31
|
||||
github.com/FloatTech/gg v1.1.3
|
||||
github.com/FloatTech/imgfactory v0.2.2-0.20230413152719-e101cc3606ef
|
||||
github.com/FloatTech/rendercard v0.2.0
|
||||
github.com/FloatTech/sqlite v1.7.2
|
||||
github.com/FloatTech/ttl v0.0.0-20250224045156-012b1463287d
|
||||
github.com/FloatTech/zbpctrl v1.7.1
|
||||
github.com/FloatTech/zbputils v1.7.2-0.20251223092310-25b804fef625
|
||||
github.com/RomiChan/syncx v0.0.0-20240418144900-b7402ffdebc7
|
||||
github.com/RomiChan/websocket v1.4.3-0.20251002072000-d3eb41798438
|
||||
github.com/Tnze/go-mc v1.20.2
|
||||
github.com/antchfx/htmlquery v1.3.4
|
||||
github.com/corona10/goimagehash v1.1.1-0.20240121134706-d8115886f360
|
||||
github.com/davidscholberg/go-durationfmt v0.0.0-20170122144659-64843a2083d3
|
||||
github.com/disintegration/imaging v1.6.2
|
||||
github.com/fumiama/ahsai v0.1.1
|
||||
github.com/fumiama/cron v1.3.0
|
||||
github.com/fumiama/go-base16384 v1.4.0
|
||||
github.com/fumiama/go-registry v0.1.2
|
||||
github.com/fumiama/gofastTEA v0.0.10
|
||||
github.com/fumiama/deepinfra v0.0.0-20251221163610-e98ee3ba437a
|
||||
github.com/fumiama/go-base16384 v1.7.1
|
||||
github.com/fumiama/go-onebot-agent v0.0.0-20251221163750-c11c679e4636
|
||||
github.com/fumiama/go-registry v0.2.7
|
||||
github.com/fumiama/gotracemoe v0.0.3
|
||||
github.com/fumiama/sqlite3 v1.14.6
|
||||
github.com/fumiama/imgsz v0.0.4
|
||||
github.com/fumiama/jieba v0.0.0-20221203025406-36c17a10b565
|
||||
github.com/fumiama/slowdo v0.0.0-20241001074058-27c4fe5259a4
|
||||
github.com/fumiama/terasu v1.0.2
|
||||
github.com/fumiama/unibase2n v0.0.0-20240530074540-ec743fd5a6d6
|
||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/jinzhu/gorm v1.9.16
|
||||
github.com/mroth/weightedrand v0.4.1
|
||||
github.com/pkumza/numcn v1.0.0
|
||||
github.com/shirou/gopsutil/v3 v3.22.2
|
||||
github.com/sirupsen/logrus v1.8.1
|
||||
github.com/tidwall/gjson v1.14.0
|
||||
github.com/wdvxdr1123/ZeroBot v1.4.2-0.20220326160643-893d4e910785
|
||||
golang.org/x/image v0.0.0-20220321031419-a8550c1d254a
|
||||
github.com/jozsefsallai/gophersauce v1.0.1
|
||||
github.com/kanrichan/resvg-go v0.0.2-0.20231001163256-63db194ca9f5
|
||||
github.com/lithammer/fuzzysearch v1.1.8
|
||||
github.com/liuzl/gocc v0.0.0-20231231122217-0372e1059ca5
|
||||
github.com/mmcdole/gofeed v1.3.0
|
||||
github.com/mroth/weightedrand v1.0.0
|
||||
github.com/notnil/chess v1.10.0
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/shirou/gopsutil/v4 v4.25.11
|
||||
github.com/sirupsen/logrus v1.9.3
|
||||
github.com/tidwall/gjson v1.18.0
|
||||
github.com/wcharczuk/go-chart/v2 v2.1.2
|
||||
github.com/wdvxdr1123/ZeroBot v1.8.2
|
||||
gitlab.com/gomidi/midi/v2 v2.3.16
|
||||
golang.org/x/image v0.34.0
|
||||
golang.org/x/sys v0.39.0
|
||||
golang.org/x/text v0.32.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/RomiChan/websocket v1.4.3-0.20220123145318-307a86b127bc // indirect
|
||||
github.com/antchfx/xpath v1.2.0 // indirect
|
||||
github.com/disintegration/imaging v1.6.2 // indirect
|
||||
github.com/PuerkitoBio/goquery v1.10.3 // indirect
|
||||
github.com/adamzy/cedar-go v0.0.0-20170805034717-80a9c64b256d // indirect
|
||||
github.com/ajstarks/svgo v0.0.0-20211024235047-1546f124cd8b // indirect
|
||||
github.com/andybalholm/cascadia v1.3.3 // indirect
|
||||
github.com/antchfx/xpath v1.3.5 // indirect
|
||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||
github.com/ebitengine/oto/v3 v3.4.0 // indirect
|
||||
github.com/ebitengine/purego v0.9.1 // indirect
|
||||
github.com/ericpauley/go-quantize v0.0.0-20200331213906-ae555eb2afa4 // indirect
|
||||
github.com/go-ole/go-ole v1.2.6 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||
github.com/google/uuid v1.3.0 // indirect
|
||||
github.com/fumiama/go-simple-protobuf v0.2.0 // indirect
|
||||
github.com/fumiama/gofastTEA v0.1.3 // indirect
|
||||
github.com/fumiama/orbyte v0.0.0-20251002065953-3bb358367eb5 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.12 // indirect
|
||||
github.com/go-ole/go-ole v1.3.0 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect
|
||||
github.com/gopxl/beep/v2 v2.1.1 // indirect
|
||||
github.com/jfreymuth/oggvorbis v1.0.5 // indirect
|
||||
github.com/jfreymuth/vorbis v1.0.2 // indirect
|
||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
|
||||
github.com/mattn/go-isatty v0.0.14 // indirect
|
||||
github.com/mattn/go-sqlite3 v1.14.12 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/liuzl/cedar-go v0.0.0-20170805034717-80a9c64b256d // indirect
|
||||
github.com/liuzl/da v0.0.0-20180704015230-14771aad5b1d // indirect
|
||||
github.com/lufia/plan9stats v0.0.0-20251013123823-9fd1530e3ec3 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mmcdole/goxpp v1.1.1 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/ncruces/go-strftime v1.0.0 // indirect
|
||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 // indirect
|
||||
github.com/power-devops/perfstat v0.0.0-20220216144756-c35f1ee13d7c // indirect
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 // indirect
|
||||
github.com/tidwall/match v1.1.1 // indirect
|
||||
github.com/tidwall/pretty v1.2.0 // indirect
|
||||
github.com/tklauser/go-sysconf v0.3.10 // indirect
|
||||
github.com/tklauser/numcpus v0.4.0 // indirect
|
||||
github.com/yusufpapurcu/wmi v1.2.2 // indirect
|
||||
golang.org/x/net v0.0.0-20220225172249-27dd8689420f // indirect
|
||||
golang.org/x/sys v0.0.0-20220327210214-530d0810a4d0 // indirect
|
||||
golang.org/x/text v0.3.7 // indirect
|
||||
modernc.org/libc v1.14.12 // indirect
|
||||
modernc.org/mathutil v1.4.1 // indirect
|
||||
modernc.org/memory v1.0.7 // indirect
|
||||
github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 // indirect
|
||||
github.com/pkumza/numcn v1.0.0 // indirect
|
||||
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||
github.com/tetratelabs/wazero v1.9.0 // indirect
|
||||
github.com/tidwall/match v1.2.0 // indirect
|
||||
github.com/tidwall/pretty v1.2.1 // indirect
|
||||
github.com/tklauser/go-sysconf v0.3.16 // indirect
|
||||
github.com/tklauser/numcpus v0.11.0 // indirect
|
||||
github.com/yusufpapurcu/wmi v1.2.4 // indirect
|
||||
golang.org/x/net v0.48.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
modernc.org/libc v1.67.1 // indirect
|
||||
modernc.org/mathutil v1.7.1 // indirect
|
||||
modernc.org/memory v1.11.0 // indirect
|
||||
modernc.org/sqlite v1.41.0 // indirect
|
||||
)
|
||||
|
||||
replace modernc.org/sqlite => github.com/fumiama/sqlite3 v1.29.10-simp
|
||||
|
||||
replace modernc.org/libc => github.com/fumiama/libc v0.0.0-20240530081950-6f6d8586b5c5
|
||||
|
||||
518
go.sum
518
go.sum
@@ -1,318 +1,354 @@
|
||||
github.com/FloatTech/AnimeAPI v1.3.2-0.20220328090901-d01ce41ecaca h1:mHrQLnuanvGeqc2EkKrNuBDSUMp8uhlPXBhLuinyMlI=
|
||||
github.com/FloatTech/AnimeAPI v1.3.2-0.20220328090901-d01ce41ecaca/go.mod h1:EPRv+7KfmYwTq8xeLXpZLqzyjw7692fWE+2InqAn8t0=
|
||||
github.com/FloatTech/sqlite v0.2.1 h1:9t6Me48XJJCIoPy4nLRvcdhcVKfT0c2lilp7SEKROG8=
|
||||
github.com/FloatTech/sqlite v0.2.1/go.mod h1:6NfHRzqOo9RWeMJEoAQVuo51Omd5LFNxCNQhMF02/9U=
|
||||
github.com/FloatTech/zbputils v1.3.2-0.20220328090754-b949b4e868e5 h1:qHuSftyvTepeXLdFIBKuAy7Ni9jlNBxXxp03efkqUJY=
|
||||
github.com/FloatTech/zbputils v1.3.2-0.20220328090754-b949b4e868e5/go.mod h1:AcOSnCQjZe0jFzAre1YBwUXi/WQIKZXTD4pcsMro+p4=
|
||||
github.com/Baidu-AIP/golang-sdk v1.1.1 h1:RQsAmgDSAkiq22I6n7XJ2t3afgzFeqjY46FGhvrx4cw=
|
||||
github.com/Baidu-AIP/golang-sdk v1.1.1/go.mod h1:bXnGw7xPeKt8aF7UCELKrV6UZ/46spItONK1RQBQj1Y=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/FloatTech/AnimeAPI v1.7.1-0.20251028071248-0c948e3db65c h1:fmvlRUzwoK6KdoRSW+XeTQ9myKHimd0pV6GbmRJLNRo=
|
||||
github.com/FloatTech/AnimeAPI v1.7.1-0.20251028071248-0c948e3db65c/go.mod h1:cuDd67B23xmICSmFBhWzXN51blod2BlM1liN9Ux0pSc=
|
||||
github.com/FloatTech/floatbox v0.0.0-20251002074805-f95cbc7edb31 h1:2K+/M64ixD1Pg5hr00Nbxr7GoWQOgahvpmp1pAMnrYc=
|
||||
github.com/FloatTech/floatbox v0.0.0-20251002074805-f95cbc7edb31/go.mod h1:kf+Ywc2lk8PLdg3RX0vrUmFLPO6k+23MFmt4GviV8C0=
|
||||
github.com/FloatTech/gg v1.1.3 h1:+GlL02lTKsxJQr4WCuNwVxC1/eBZrCvypCIBtxuOFb4=
|
||||
github.com/FloatTech/gg v1.1.3/go.mod h1:/9oLP54CMfq4r+71XL26uaFTJ1uL1boAyX67680/1HE=
|
||||
github.com/FloatTech/imgfactory v0.2.2-0.20230413152719-e101cc3606ef h1:CJbK/2FRwPuZpeb6M4sWK2d7oXDnBEGhpkQuQrgc91A=
|
||||
github.com/FloatTech/imgfactory v0.2.2-0.20230413152719-e101cc3606ef/go.mod h1:el5hGpj1C1bDRxcTXYRwEivDCr40zZeJpcrLrB1fajs=
|
||||
github.com/FloatTech/rendercard v0.2.0 h1:PBTZ2gCEy/dAEGSfWecrGTrWDYpiBJD1dVzNDDaOxh4=
|
||||
github.com/FloatTech/rendercard v0.2.0/go.mod h1:Sbojcy1t3NfFz7/WicZRmR/uKFxNMYkKF8qHx69dxY0=
|
||||
github.com/FloatTech/sqlite v1.7.2 h1:b8COegNLSzofzOyARsVwSbz9OOzWEa8IElsTlx1TBLw=
|
||||
github.com/FloatTech/sqlite v1.7.2/go.mod h1:/4tzfCGhrZnnjC1U8vcfwGQeF6eR649fhOsS3+Le0+s=
|
||||
github.com/FloatTech/ttl v0.0.0-20250224045156-012b1463287d h1:mUQ/c3wXKsUGa4Sg9DBy01APXKB68PmobhxOyaJI7lY=
|
||||
github.com/FloatTech/ttl v0.0.0-20250224045156-012b1463287d/go.mod h1:fHZFWGquNXuHttu9dUYoKuNbm3dzLETnIOnm1muSfDs=
|
||||
github.com/FloatTech/zbpctrl v1.7.1 h1:0yPEmCForhyMbnhTckmjDUFFDZgQp1RjO2bVF4ZVqOs=
|
||||
github.com/FloatTech/zbpctrl v1.7.1/go.mod h1:xmM4dSwHA02Gei3ogCRiG+RTrw/7Z69PfrN5NYf8BPE=
|
||||
github.com/FloatTech/zbputils v1.7.2-0.20251223092310-25b804fef625 h1:7CnuhyOR1WhA5A/Pf2Lh0iOQBvZnMz1KLXWB/hJbKwc=
|
||||
github.com/FloatTech/zbputils v1.7.2-0.20251223092310-25b804fef625/go.mod h1:LBanthv/2ExuzTMCuMDXLkvSPxDJTyNp4MrLoJQzIL4=
|
||||
github.com/PuerkitoBio/goquery v1.5.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc=
|
||||
github.com/RomiChan/websocket v1.4.3-0.20220123145318-307a86b127bc h1:AAx50/fb/xS4lvsdQg+bFbGvqSDhyV1MF+p2PLCamZ0=
|
||||
github.com/RomiChan/websocket v1.4.3-0.20220123145318-307a86b127bc/go.mod h1:OMmITAib6POA37xCichWM0aRnoVpSMZO1rB/G01wrr0=
|
||||
github.com/PuerkitoBio/goquery v1.10.3 h1:pFYcNSqHxBD06Fpj/KsbStFRsgRATgnf3LeXiUkhzPo=
|
||||
github.com/PuerkitoBio/goquery v1.10.3/go.mod h1:tMUX0zDMHXYlAQk6p35XxQMqMweEKB7iK7iLNd4RH4Y=
|
||||
github.com/RomiChan/syncx v0.0.0-20240418144900-b7402ffdebc7 h1:S/ferNiehVjNaBMNNBxUjLtVmP/YWD6Yh79RfPv4ehU=
|
||||
github.com/RomiChan/syncx v0.0.0-20240418144900-b7402ffdebc7/go.mod h1:vD7Ra3Q9onRtojoY5sMCLQ7JBgjUsrXDnDKyFxqpf9w=
|
||||
github.com/RomiChan/websocket v1.4.3-0.20251002072000-d3eb41798438 h1:I0bdwHZ+2DY45b39xPoTD2u+Z8zhvBuu9aZfjMZeiZM=
|
||||
github.com/RomiChan/websocket v1.4.3-0.20251002072000-d3eb41798438/go.mod h1:GO+9i5UYB4BuZEel6BfGx7O1u3ggwgZWUnGxPATUoTE=
|
||||
github.com/Tnze/go-mc v1.20.2 h1:arHCE/WxLCxY73C/4ZNLdOymRYtdwoXE05ohB7HVN6Q=
|
||||
github.com/Tnze/go-mc v1.20.2/go.mod h1:geoRj2HsXSkB3FJBuhr7wCzXegRlzWsVXd7h7jiJ6aQ=
|
||||
github.com/adamzy/cedar-go v0.0.0-20170805034717-80a9c64b256d h1:ir/IFJU5xbja5UaBEQLjcvn7aAU01nqU/NUyOBEU+ew=
|
||||
github.com/adamzy/cedar-go v0.0.0-20170805034717-80a9c64b256d/go.mod h1:PRWNwWq0yifz6XDPZu48aSld8BWwBfr2JKB2bGWiEd4=
|
||||
github.com/ajstarks/deck v0.0.0-20200831202436-30c9fc6549a9/go.mod h1:JynElWSGnm/4RlzPXRlREEwqTHAN3T56Bv2ITsFT3gY=
|
||||
github.com/ajstarks/deck/generate v0.0.0-20210309230005-c3f852c02e19/go.mod h1:T13YZdzov6OU0A1+RfKZiZN9ca6VeKdBdyDV+BY97Tk=
|
||||
github.com/ajstarks/svgo v0.0.0-20200320125537-f189e35d30ca/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw=
|
||||
github.com/ajstarks/svgo v0.0.0-20211024235047-1546f124cd8b h1:slYM766cy2nI3BwyRiyQj/Ud48djTMtMebDqepE95rw=
|
||||
github.com/ajstarks/svgo v0.0.0-20211024235047-1546f124cd8b/go.mod h1:1KcenG0jGWcpt8ov532z81sp/kMMUG485J2InIOyADM=
|
||||
github.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y=
|
||||
github.com/antchfx/htmlquery v1.2.4 h1:qLteofCMe/KGovBI6SQgmou2QNyedFUW+pE+BpeZ494=
|
||||
github.com/antchfx/htmlquery v1.2.4/go.mod h1:2xO6iu3EVWs7R2JYqBbp8YzG50gj/ofqs5/0VZoDZLc=
|
||||
github.com/antchfx/xpath v1.2.0 h1:mbwv7co+x0RwgeGAOHdrKy89GvHaGvxxBtPK0uF9Zr8=
|
||||
github.com/antchfx/xpath v1.2.0/go.mod h1:i54GszH55fYfBmoZXapTHN8T8tkcHfRgLyVwwqzXNcs=
|
||||
github.com/corona10/goimagehash v1.0.3 h1:NZM518aKLmoNluluhfHGxT3LGOnrojrxhGn63DR/CZA=
|
||||
github.com/corona10/goimagehash v1.0.3/go.mod h1:VkvE0mLn84L4aF8vCb6mafVajEb6QYMHl2ZJLn0mOGI=
|
||||
github.com/andybalholm/cascadia v1.3.3 h1:AG2YHrzJIm4BZ19iwJ/DAua6Btl3IwJX+VI4kktS1LM=
|
||||
github.com/andybalholm/cascadia v1.3.3/go.mod h1:xNd9bqTn98Ln4DwST8/nG+H0yuB8Hmgu1YHNnWw0GeA=
|
||||
github.com/antchfx/htmlquery v1.3.4 h1:Isd0srPkni2iNTWCwVj/72t7uCphFeor5Q8nCzj1jdQ=
|
||||
github.com/antchfx/htmlquery v1.3.4/go.mod h1:K9os0BwIEmLAvTqaNSua8tXLWRWZpocZIH73OzWQbwM=
|
||||
github.com/antchfx/xpath v1.3.3/go.mod h1:i54GszH55fYfBmoZXapTHN8T8tkcHfRgLyVwwqzXNcs=
|
||||
github.com/antchfx/xpath v1.3.5 h1:PqbXLC3TkfeZyakF5eeh3NTWEbYl4VHNVeufANzDbKQ=
|
||||
github.com/antchfx/xpath v1.3.5/go.mod h1:i54GszH55fYfBmoZXapTHN8T8tkcHfRgLyVwwqzXNcs=
|
||||
github.com/corona10/goimagehash v1.1.1-0.20240121134706-d8115886f360 h1:SvD9vQN+3r0wskoSrQ7IOyDmOtRIXhT3rlnf819r/bY=
|
||||
github.com/corona10/goimagehash v1.1.1-0.20240121134706-d8115886f360/go.mod h1:PFDP0Q0oYvEuipJHMSbeK8tjbuRzkQqjPDYm3n71ITA=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davidscholberg/go-durationfmt v0.0.0-20170122144659-64843a2083d3 h1:qshMBFxVjYjzI+kwvWvgoByF3uMCvnJiaK8KslWAbr8=
|
||||
github.com/davidscholberg/go-durationfmt v0.0.0-20170122144659-64843a2083d3/go.mod h1:M9fx6rAdHSYLKxXPgUXGgblb586CA7ceNrpu4DEc2No=
|
||||
github.com/denisenkom/go-mssqldb v0.0.0-20191124224453-732737034ffd h1:83Wprp6ROGeiHFAP8WJdI2RoxALQYgdllERc3N5N2DM=
|
||||
github.com/denisenkom/go-mssqldb v0.0.0-20191124224453-732737034ffd/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
|
||||
github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c=
|
||||
github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4=
|
||||
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||
github.com/ebitengine/oto/v3 v3.4.0 h1:br0PgASsEWaoWn38b2Goe7m1GKFYfNgnsjSd5Gg+/bQ=
|
||||
github.com/ebitengine/oto/v3 v3.4.0/go.mod h1:IOleLVD0m+CMak3mRVwsYY8vTctQgOM0iiL6S7Ar7eI=
|
||||
github.com/ebitengine/purego v0.9.1 h1:a/k2f2HQU3Pi399RPW1MOaZyhKJL9w/xFpKAg4q1s0A=
|
||||
github.com/ebitengine/purego v0.9.1/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
|
||||
github.com/ericpauley/go-quantize v0.0.0-20200331213906-ae555eb2afa4 h1:BBade+JlV/f7JstZ4pitd4tHhpN+w+6I+LyOS7B4fyU=
|
||||
github.com/ericpauley/go-quantize v0.0.0-20200331213906-ae555eb2afa4/go.mod h1:H7chHJglrhPPzetLdzBleF8d22WYOv7UM/lEKYiwlKM=
|
||||
github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5 h1:Yzb9+7DPaBjB8zlTR87/ElzFsnQfuHnVUVqpZZIcV5Y=
|
||||
github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0=
|
||||
github.com/fogleman/gg v1.3.0 h1:/7zJX8F6AaYQc57WQCyN9cAIz+4bCJGO9B+dyW29am8=
|
||||
github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/fumiama/ahsai v0.1.1 h1:/t5tdKRim0TK6YwgNFQfqtDOW7Y2tFBsmdUWt3JK+C0=
|
||||
github.com/fumiama/ahsai v0.1.1/go.mod h1:rBhHLgN2bygcqLpBi+XQa8B8Afn4UkPHQ5vvQibdbDQ=
|
||||
github.com/fumiama/cron v1.3.0 h1:ZWlwuexF+HQHl3cYytEE5HNwD99q+3vNZF1GrEiXCFo=
|
||||
github.com/fumiama/cron v1.3.0/go.mod h1:bz5Izvgi/xEUI8tlBN8BI2jr9Moo8N4or0KV8xXuPDY=
|
||||
github.com/fumiama/go-base16384 v1.4.0 h1:4KrtewnmAChrZjPA7/QYc72t+vvsKF+DYB0q1iRPdpo=
|
||||
github.com/fumiama/go-base16384 v1.4.0/go.mod h1:OEn+947GV5gsbTAnyuUW/SrfxJYUdYupSIQXOuGOcXM=
|
||||
github.com/fumiama/go-registry v0.1.2 h1:mlH9QtbgzfV+uvm5YM8YZau/2yrekVziPuyn3UTzrYw=
|
||||
github.com/fumiama/go-registry v0.1.2/go.mod h1:iJT3DVgH7KXpJZs6waXEjnWtJPUBBGhF+ByJIMRfngk=
|
||||
github.com/fumiama/gofastTEA v0.0.10 h1:JJJ+brWD4kie+mmK2TkspDXKzqq0IjXm89aGYfoGhhQ=
|
||||
github.com/fumiama/gofastTEA v0.0.10/go.mod h1:RIdbYZyB4MbH6ZBlPymRaXn3cD6SedlCu5W/HHfMPBk=
|
||||
github.com/fumiama/deepinfra v0.0.0-20251221163610-e98ee3ba437a h1:a0+2vaXajfxsNcIaYG7A/cAb2e4+M3s5J6oyIiPsL3c=
|
||||
github.com/fumiama/deepinfra v0.0.0-20251221163610-e98ee3ba437a/go.mod h1:uqsWK/GM9OvKV0pXZOQB63rWugBbiXInY8E1JoRKhkg=
|
||||
github.com/fumiama/go-base16384 v1.7.1 h1:1P1x6FWRvd7PtbH4idDAGWAjKKcVxggxlROYKRXbw58=
|
||||
github.com/fumiama/go-base16384 v1.7.1/go.mod h1:OEn+947GV5gsbTAnyuUW/SrfxJYUdYupSIQXOuGOcXM=
|
||||
github.com/fumiama/go-onebot-agent v0.0.0-20251221163750-c11c679e4636 h1:PzjoPyYQnrdhsiPj366q6QwyN2amw/msIciRAtt+P7s=
|
||||
github.com/fumiama/go-onebot-agent v0.0.0-20251221163750-c11c679e4636/go.mod h1:rTrS23rvTYuZcSngENJTvcBFTz1nGsImSv+bW7yfhqs=
|
||||
github.com/fumiama/go-registry v0.2.7 h1:tLEqgEpsiybQMqBv0dLHm5leia/z1DhajMupwnOHeNs=
|
||||
github.com/fumiama/go-registry v0.2.7/go.mod h1:m+wp5fF8dYgVoFkBPZl+vlK90loymaJE0JCtocVQLEs=
|
||||
github.com/fumiama/go-simple-protobuf v0.2.0 h1:ACyN1MAlu7pDR3EszWgzUeNP+IRsSHwH6V9JCJA5R5o=
|
||||
github.com/fumiama/go-simple-protobuf v0.2.0/go.mod h1:5yYNapXq1tQMOZg9bOIVhQlZk9pQqpuFIO4DZLbsdy4=
|
||||
github.com/fumiama/gofastTEA v0.1.3 h1:fxOi2D66knV6QN170hb59YiqxPhjlgizvBw+o0OjxUA=
|
||||
github.com/fumiama/gofastTEA v0.1.3/go.mod h1:RIdbYZyB4MbH6ZBlPymRaXn3cD6SedlCu5W/HHfMPBk=
|
||||
github.com/fumiama/gotracemoe v0.0.3 h1:iI5EbE9A3UUbfukG6+/soYPjp1S31eCNYf4tw7s6/Jc=
|
||||
github.com/fumiama/gotracemoe v0.0.3/go.mod h1:tyqahdUzHf0bQIAVY/GYmDWvYYe5ik1ZbhnGYh+zl40=
|
||||
github.com/fumiama/sqlite3 v1.14.6 h1:+e+iygyiDXQJVi7xeXIviBvR7hAc5y20WA9hRwfKn10=
|
||||
github.com/fumiama/sqlite3 v1.14.6/go.mod h1:Xx9a2/OtHuy9pBjow0N+bE/RhNeZ7zZz5xh25vqbA5A=
|
||||
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
|
||||
github.com/fumiama/imgsz v0.0.4 h1:Lsasu2hdSSFS+vnD+nvR1UkiRMK7hcpyYCC0FzgSMFI=
|
||||
github.com/fumiama/imgsz v0.0.4/go.mod h1:bISOQVTlw9sRytPwe8ir7tAaEmyz9hSNj9n8mXMBG0E=
|
||||
github.com/fumiama/jieba v0.0.0-20221203025406-36c17a10b565 h1:sQuR2+N5HurnvsZhiKdEg+Ig354TaqgCQRxd/0KgIOQ=
|
||||
github.com/fumiama/jieba v0.0.0-20221203025406-36c17a10b565/go.mod h1:UUEvyLTJ7yoOA/viKG4wEis4ERydM7+Ny6gZUWgkS80=
|
||||
github.com/fumiama/libc v0.0.0-20240530081950-6f6d8586b5c5 h1:jDxsIupsT84A6WHcs6kWbst+KqrRQ8/o0VyoFMnbBOA=
|
||||
github.com/fumiama/libc v0.0.0-20240530081950-6f6d8586b5c5/go.mod h1:15P6ublJ9FJR8YQCGy8DeQ2Uwur7iW9Hserr/T3OFZE=
|
||||
github.com/fumiama/orbyte v0.0.0-20251002065953-3bb358367eb5 h1:j9o0XVvdAeLwrBYMnh0SerrMc9CgNU6AGszbsvFzoc0=
|
||||
github.com/fumiama/orbyte v0.0.0-20251002065953-3bb358367eb5/go.mod h1:FOjdw7KdCbK2eH3gRPhwFNCoXKpu9sN5vPH4El/8e0c=
|
||||
github.com/fumiama/slowdo v0.0.0-20241001074058-27c4fe5259a4 h1:zN9e09TYKXI1mNkuS6YbH+Sn+4k5tBir+ovhZZcRYAs=
|
||||
github.com/fumiama/slowdo v0.0.0-20241001074058-27c4fe5259a4/go.mod h1:iZf1H/Jcw5gjOOFb4C5nlweJtViWc7uwUxRCe14pbYk=
|
||||
github.com/fumiama/sqlite3 v1.29.10-simp h1:c5y3uKyU0q9t0/SyfynzYyuslQ5zP+5CD8e0yYY554A=
|
||||
github.com/fumiama/sqlite3 v1.29.10-simp/go.mod h1:ItX2a1OVGgNsFh6Dv60JQvGfJfTPHPVpV6DF59akYOA=
|
||||
github.com/fumiama/terasu v1.0.2 h1:Dxj2vPIgLHyeMlBd61xYHx5Jg8v7k24yg7taNLlK8/8=
|
||||
github.com/fumiama/terasu v1.0.2/go.mod h1:1eHzpL/GJVcOnqEkh1vUbKu2zo6gojSuueUNJ9yHJE0=
|
||||
github.com/fumiama/unibase2n v0.0.0-20240530074540-ec743fd5a6d6 h1:LtDgr628eji8jRpjPCxsk7ibjcfi97QieZVCTjxLCBw=
|
||||
github.com/fumiama/unibase2n v0.0.0-20240530074540-ec743fd5a6d6/go.mod h1:lEaZsT4FRSqcjnQ5q8y+mkenkzR/r1D3BJmfdp0vqDg=
|
||||
github.com/gabriel-vasile/mimetype v1.0.4/go.mod h1:6CDPel/o/3/s4+bp6kIbsWATq8pmgOisOPG40CJa6To=
|
||||
github.com/gabriel-vasile/mimetype v1.4.12 h1:e9hWvmLYvtp846tLHam2o++qitpguFiYCKbn0w9jyqw=
|
||||
github.com/gabriel-vasile/mimetype v1.4.12/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s=
|
||||
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
||||
github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=
|
||||
github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
|
||||
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
|
||||
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
||||
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||
github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
|
||||
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY=
|
||||
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
|
||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g=
|
||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
|
||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o=
|
||||
github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
|
||||
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ=
|
||||
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/gopxl/beep/v2 v2.1.1 h1:6FYIYMm2qPAdWkjX+7xwKrViS1x0Po5kDMdRkq8NVbU=
|
||||
github.com/gopxl/beep/v2 v2.1.1/go.mod h1:ZAm9TGQ9lvpoiFLd4zf5B1IuyxZhgRACMId1XJbaW0E=
|
||||
github.com/jfreymuth/oggvorbis v1.0.5 h1:u+Ck+R0eLSRhgq8WTmffYnrVtSztJcYrl588DM4e3kQ=
|
||||
github.com/jfreymuth/oggvorbis v1.0.5/go.mod h1:1U4pqWmghcoVsCJJ4fRBKv9peUJMBHixthRlBeD6uII=
|
||||
github.com/jfreymuth/vorbis v1.0.2 h1:m1xH6+ZI4thH927pgKD8JOH4eaGRm18rEE9/0WKjvNE=
|
||||
github.com/jfreymuth/vorbis v1.0.2/go.mod h1:DoftRo4AznKnShRl1GxiTFCseHr4zR9BN3TWXyuzrqQ=
|
||||
github.com/jinzhu/gorm v1.9.16 h1:+IyIjPEABKRpsu/F8OvDPy9fyQlgsg2luMV2ZIH5i5o=
|
||||
github.com/jinzhu/gorm v1.9.16/go.mod h1:G3LB3wezTOWM2ITLzPxEXgSkOXAntiLHS7UdBefADcs=
|
||||
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
||||
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||
github.com/jinzhu/now v1.0.1 h1:HjfetcXq097iXP0uoPCdnM4Efp5/9MsM0/M+XOTeR3M=
|
||||
github.com/jinzhu/now v1.0.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/jozsefsallai/gophersauce v1.0.1 h1:BA3ovtQRrAb1qYU9JoRLbDHpxnDunlNcEkEfhCvDDCM=
|
||||
github.com/jozsefsallai/gophersauce v1.0.1/go.mod h1:YVEI7djliMTmZ1Vh01YPF8bUHi+oKhe3yXgKf1T49vg=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/kanrichan/resvg-go v0.0.2-0.20231001163256-63db194ca9f5 h1:BXnB1Gz4y/zwQh+ZFNy7rgd+ZfMOrwRr4uZSHEI+ieY=
|
||||
github.com/kanrichan/resvg-go v0.0.2-0.20231001163256-63db194ca9f5/go.mod h1:c9+VS9GaommgIOzNWb5ze4lYwfT8BZ2UDyGiuQTT7yc=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/lib/pq v1.1.1 h1:sJZmqHoEaY7f+NPP8pgLB/WxulyR3fewgCM2qaSlBb4=
|
||||
github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4=
|
||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
|
||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||
github.com/lithammer/fuzzysearch v1.1.8 h1:/HIuJnjHuXS8bKaiTMeeDlW2/AyIWk2brx1V8LFgLN4=
|
||||
github.com/lithammer/fuzzysearch v1.1.8/go.mod h1:IdqeyBClc3FFqSzYq/MXESsS4S0FsZ5ajtkr5xPLts4=
|
||||
github.com/liuzl/cedar-go v0.0.0-20170805034717-80a9c64b256d h1:qSmEGTgjkESUX5kPMSGJ4pcBUtYVDdkNzMrjQyvRvp0=
|
||||
github.com/liuzl/cedar-go v0.0.0-20170805034717-80a9c64b256d/go.mod h1:x7SghIWwLVcJObXbjK7S2ENsT1cAcdJcPl7dRaSFog0=
|
||||
github.com/liuzl/da v0.0.0-20180704015230-14771aad5b1d h1:hTRDIpJ1FjS9ULJuEzu69n3qTgc18eI+ztw/pJv47hs=
|
||||
github.com/liuzl/da v0.0.0-20180704015230-14771aad5b1d/go.mod h1:7xD3p0XnHvJFQ3t/stEJd877CSIMkH/fACVWen5pYnc=
|
||||
github.com/liuzl/gocc v0.0.0-20231231122217-0372e1059ca5 h1:wnbHIeP1UX8ClYEWKGnw66PfYvReCHu9G5lXSte3Sqc=
|
||||
github.com/liuzl/gocc v0.0.0-20231231122217-0372e1059ca5/go.mod h1:7KaV9YIR92M1FpbczAcfYQ3UZ5ayT27pNtunDmXvLBo=
|
||||
github.com/lufia/plan9stats v0.0.0-20251013123823-9fd1530e3ec3 h1:PwQumkgq4/acIiZhtifTV5OUqqiP82UAl0h87xj/l9k=
|
||||
github.com/lufia/plan9stats v0.0.0-20251013123823-9fd1530e3ec3/go.mod h1:autxFIvghDt3jPTLoqZ9OZ7s9qTGNAWmYCjVFWPX/zg=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-sqlite3 v1.14.0 h1:mLyGNKR8+Vv9CAU7PphKa2hkEqxxhn8i32J6FPj1/QA=
|
||||
github.com/mattn/go-sqlite3 v1.14.0/go.mod h1:JIl7NbARA7phWnGvh0LKTyg7S9BA+6gx71ShQilpsus=
|
||||
github.com/mattn/go-sqlite3 v1.14.12 h1:TJ1bhYJPV44phC+IMu1u2K/i5RriLTPe+yc68XDJ1Z0=
|
||||
github.com/mattn/go-sqlite3 v1.14.12/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
|
||||
github.com/mroth/weightedrand v0.4.1 h1:rHcbUBopmi/3x4nnrvwGJBhX9d0vk+KgoLUZeDP6YyI=
|
||||
github.com/mroth/weightedrand v0.4.1/go.mod h1:3p2SIcC8al1YMzGhAIoXD+r9olo/g/cdJgAD905gyNE=
|
||||
github.com/mmcdole/gofeed v1.3.0 h1:5yn+HeqlcvjMeAI4gu6T+crm7d0anY85+M+v6fIFNG4=
|
||||
github.com/mmcdole/gofeed v1.3.0/go.mod h1:9TGv2LcJhdXePDzxiuMnukhV2/zb6VtnZt1mS+SjkLE=
|
||||
github.com/mmcdole/goxpp v1.1.1 h1:RGIX+D6iQRIunGHrKqnA2+700XMCnNv0bAOOv5MUhx8=
|
||||
github.com/mmcdole/goxpp v1.1.1/go.mod h1:v+25+lT2ViuQ7mVxcncQ8ch1URund48oH+jhjiwEgS8=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/mroth/weightedrand v1.0.0 h1:V8JeHChvl2MP1sAoXq4brElOcza+jxLkRuwvtQu8L3E=
|
||||
github.com/mroth/weightedrand v1.0.0/go.mod h1:3p2SIcC8al1YMzGhAIoXD+r9olo/g/cdJgAD905gyNE=
|
||||
github.com/ncruces/go-strftime v1.0.0 h1:HMFp8mLCTPp341M/ZnA4qaf7ZlsbTc+miZjCLOFAw7w=
|
||||
github.com/ncruces/go-strftime v1.0.0/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
|
||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ=
|
||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/notnil/chess v1.10.0 h1:RR3MgS9G6zZmJ+VPTJolyxdaIgxoUPyUUY+2iaw35G0=
|
||||
github.com/notnil/chess v1.10.0/go.mod h1:cRuJUIBFq9Xki05TWHJxHYkC+fFpq45IWwk94DdlCrA=
|
||||
github.com/orcaman/writerseeker v0.0.0-20200621085525-1d3f536ff85e h1:s2RNOM/IGdY0Y6qfTeUKhDawdHDpK9RGBdx80qN4Ttw=
|
||||
github.com/orcaman/writerseeker v0.0.0-20200621085525-1d3f536ff85e/go.mod h1:nBdnFKj15wFbf94Rwfq4m30eAcyY9V/IyKAGQFtqkW0=
|
||||
github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 h1:onHthvaw9LFnH4t2DcNVpwGmV9E1BkGknEliJkfwQj0=
|
||||
github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58/go.mod h1:DXv8WO4yhMYhSNPKjeNKa5WY9YCIEBRbNzFFPJbWO6Y=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkumza/numcn v1.0.0 h1:ZT5cf9IJkUZgRgEtCiNNykk0RwsrKXSTsvDHOwUTzgE=
|
||||
github.com/pkumza/numcn v1.0.0/go.mod h1:QSeH+al9dWCd8di5HZM/ZqHqhZmUKfph572e9Ev/ETc=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
|
||||
github.com/power-devops/perfstat v0.0.0-20220216144756-c35f1ee13d7c h1:NRoLoZvkBTKvR5gQLgA3e0hqjkY9u1wm+iOL45VN/qI=
|
||||
github.com/power-devops/perfstat v0.0.0-20220216144756-c35f1ee13d7c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||
github.com/shirou/gopsutil/v3 v3.22.2 h1:wCrArWFkHYIdDxx/FSfF5RB4dpJYW6t7rcp3+zL8uks=
|
||||
github.com/shirou/gopsutil/v3 v3.22.2/go.mod h1:WapW1AOOPlHyXr+yOyw3uYx36enocrtSoSBy0L5vUHY=
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
|
||||
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU=
|
||||
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||
github.com/shirou/gopsutil/v4 v4.25.11 h1:X53gB7muL9Gnwwo2evPSE+SfOrltMoR6V3xJAXZILTY=
|
||||
github.com/shirou/gopsutil/v4 v4.25.11/go.mod h1:EivAfP5x2EhLp2ovdpKSozecVXn1TmuG7SMzs/Wh4PU=
|
||||
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ=
|
||||
github.com/t-tomalak/logrus-easy-formatter v0.0.0-20190827215021-c074f06c5816/go.mod h1:tzym/CEb5jnFI+Q0k4Qq3+LvRF4gO3E2pxS8fHP8jcA=
|
||||
github.com/tidwall/gjson v1.13.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||
github.com/tidwall/gjson v1.14.0 h1:6aeJ0bzojgWLa82gDQHcx3S0Lr/O51I9bJ5nv6JFx5w=
|
||||
github.com/tidwall/gjson v1.14.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
|
||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||
github.com/tetratelabs/wazero v1.9.0 h1:IcZ56OuxrtaEz8UYNRHBrUa9bYeX9oVY93KspZZBf/I=
|
||||
github.com/tetratelabs/wazero v1.9.0/go.mod h1:TSbcXCfFP0L2FGkRPxHphadXPjo1T6W+CseNNY7EkjM=
|
||||
github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY=
|
||||
github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
|
||||
github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
|
||||
github.com/tidwall/match v1.2.0 h1:0pt8FlkOwjN2fPt4bIl4BoNxb98gGHN2ObFEDkrfZnM=
|
||||
github.com/tidwall/match v1.2.0/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
|
||||
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
||||
github.com/tklauser/go-sysconf v0.3.9/go.mod h1:11DU/5sG7UexIrp/O6g35hrWzu0JxlwQ3LSFUzyeuhs=
|
||||
github.com/tklauser/go-sysconf v0.3.10 h1:IJ1AZGZRWbY8T5Vfk04D9WOA5WSejdflXxP03OUqALw=
|
||||
github.com/tklauser/go-sysconf v0.3.10/go.mod h1:C8XykCvCb+Gn0oNCWPIlcb0RuglQTYaQ2hGm7jmxEFk=
|
||||
github.com/tklauser/numcpus v0.3.0/go.mod h1:yFGUr7TUHQRAhyqBcEg0Ge34zDBAsIvJJcyE6boqnA8=
|
||||
github.com/tklauser/numcpus v0.4.0 h1:E53Dm1HjH1/R2/aoCtXtPgzmElmn51aOkhCFSuZq//o=
|
||||
github.com/tklauser/numcpus v0.4.0/go.mod h1:1+UI3pD8NW14VMwdgJNJ1ESk2UnwhAnz5hMwiKKqXCQ=
|
||||
github.com/wdvxdr1123/ZeroBot v1.4.2-0.20220326160643-893d4e910785 h1:alVsiLOu9H0GCriAkTMqn310izMNEzg9y1Z9FqAV1DY=
|
||||
github.com/wdvxdr1123/ZeroBot v1.4.2-0.20220326160643-893d4e910785/go.mod h1:NwXIp7PgjV+kUALMXJ4v4/3QcsRSOodtjhLekuPXFog=
|
||||
github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4=
|
||||
github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
||||
github.com/tklauser/go-sysconf v0.3.16 h1:frioLaCQSsF5Cy1jgRBrzr6t502KIIwQ0MArYICU0nA=
|
||||
github.com/tklauser/go-sysconf v0.3.16/go.mod h1:/qNL9xxDhc7tx3HSRsLWNnuzbVfh3e7gh/BmM179nYI=
|
||||
github.com/tklauser/numcpus v0.11.0 h1:nSTwhKH5e1dMNsCdVBukSZrURJRoHbSEQjdEbY+9RXw=
|
||||
github.com/tklauser/numcpus v0.11.0/go.mod h1:z+LwcLq54uWZTX0u/bGobaV34u6V7KNlTZejzM6/3MQ=
|
||||
github.com/wcharczuk/go-chart/v2 v2.1.2 h1:Y17/oYNuXwZg6TFag06qe8sBajwwsuvPiJJXcUcLL6E=
|
||||
github.com/wcharczuk/go-chart/v2 v2.1.2/go.mod h1:Zi4hbaqlWpYajnXB2K22IUYVXRXaLfSGNNR7P4ukyyQ=
|
||||
github.com/wdvxdr1123/ZeroBot v1.8.2 h1:H4qNHgeYLLm3ID5T9MKnO4fI0SWWl0rFCGLCUr8u10M=
|
||||
github.com/wdvxdr1123/ZeroBot v1.8.2/go.mod h1:trueIIVRywKJa3ov4QphzVvzYzgCNrlXdf9JvPJOFW8=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yusufpapurcu/wmi v1.2.2 h1:KBNDSne4vP5mbSWnJbO+51IMOXJB67QiYCSBrubbPRg=
|
||||
github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
|
||||
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
||||
gitlab.com/gomidi/midi/v2 v2.3.16 h1:yufWSENyjnJ4LFQa9BerzUm4E4aLfTyzw5nmnCteO0c=
|
||||
gitlab.com/gomidi/midi/v2 v2.3.16/go.mod h1:jDpP4O4skYi+7iVwt6Zyp18bd2M4hkjtMuw2cmgKgfw=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191205180655-e7c4368fe9dd/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20220321153916-2c7772ba3064 h1:S25/rfnfsMVgORT4/J61MJ7rdyseOZOyvLIrZEZ7s6s=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
|
||||
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
|
||||
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
|
||||
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
|
||||
golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU=
|
||||
golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0=
|
||||
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/image v0.0.0-20220321031419-a8550c1d254a h1:LnH9RNcpPv5Kzi15lXg42lYMPUf0x8CuPv1YnvBWZAg=
|
||||
golang.org/x/image v0.0.0-20220321031419-a8550c1d254a/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM=
|
||||
golang.org/x/image v0.18.0/go.mod h1:4yyo5vMFQjVjUcVk4jEQcU9MGy/rulF5WvUILseCM2E=
|
||||
golang.org/x/image v0.34.0 h1:33gCkyw9hmwbZJeZkct8XyR11yH889EQt/QH4VmXMn8=
|
||||
golang.org/x/image v0.34.0/go.mod h1:2RNFBZRB+vnwwFil8GkMdRvrJOFd1AzdZI6vOY+eJVU=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/mod v0.31.0 h1:HaW9xtz0+kOcWKwli0ZXy79Ix+UW/vOfmWI5QVd2tgI=
|
||||
golang.org/x/mod v0.31.0/go.mod h1:43JraMp9cGx1Rx3AqioxrbrhNsLl2l/iNAvuBkrezpg=
|
||||
golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200421231249-e086a090c8fd/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20220225172249-27dd8689420f h1:oA4XRj0qtSt8Yo1Zms0CUlsT3KG69V2UGQWPBxujDmc=
|
||||
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
|
||||
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
||||
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
|
||||
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
|
||||
golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU=
|
||||
golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
||||
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
|
||||
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201126233918-771906719818/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210816074244-15123e1e1f71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210902050250-f475640dd07b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220111092808-5a964db01320/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220327210214-530d0810a4d0 h1:G6WAvvcMaaFYQhMbC0L5ZWNExEcJ3j3yFTxx4mwOHtM=
|
||||
golang.org/x/sys v0.0.0-20220327210214-530d0810a4d0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk=
|
||||
golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
||||
golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
|
||||
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
|
||||
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
|
||||
golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
|
||||
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
||||
golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU=
|
||||
golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
|
||||
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
|
||||
golang.org/x/tools v0.40.0 h1:yLkxfA+Qnul4cs9QA3KnlFu0lVmd8JJfoq+E41uSutA=
|
||||
golang.org/x/tools v0.40.0/go.mod h1:Ik/tzLRlbscWpqqMRjyWYDisX8bG13FrdXp3o4Sr9lc=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
lukechampine.com/uint128 v1.1.1/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk=
|
||||
modernc.org/cc/v3 v3.33.6/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
|
||||
modernc.org/cc/v3 v3.33.9/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
|
||||
modernc.org/cc/v3 v3.33.11/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
|
||||
modernc.org/cc/v3 v3.34.0/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
|
||||
modernc.org/cc/v3 v3.35.0/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
|
||||
modernc.org/cc/v3 v3.35.4/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
|
||||
modernc.org/cc/v3 v3.35.5/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
|
||||
modernc.org/cc/v3 v3.35.7/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
|
||||
modernc.org/cc/v3 v3.35.8/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
|
||||
modernc.org/cc/v3 v3.35.10/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
|
||||
modernc.org/cc/v3 v3.35.15/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
|
||||
modernc.org/cc/v3 v3.35.16/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
|
||||
modernc.org/cc/v3 v3.35.17/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
|
||||
modernc.org/cc/v3 v3.35.18/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
|
||||
modernc.org/cc/v3 v3.35.20/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
|
||||
modernc.org/cc/v3 v3.35.22/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
|
||||
modernc.org/cc/v3 v3.35.24/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI=
|
||||
modernc.org/ccgo/v3 v3.9.5/go.mod h1:umuo2EP2oDSBnD3ckjaVUXMrmeAw8C8OSICVa0iFf60=
|
||||
modernc.org/ccgo/v3 v3.10.0/go.mod h1:c0yBmkRFi7uW4J7fwx/JiijwOjeAeR2NoSaRVFPmjMw=
|
||||
modernc.org/ccgo/v3 v3.11.0/go.mod h1:dGNposbDp9TOZ/1KBxghxtUp/bzErD0/0QW4hhSaBMI=
|
||||
modernc.org/ccgo/v3 v3.11.1/go.mod h1:lWHxfsn13L3f7hgGsGlU28D9eUOf6y3ZYHKoPaKU0ag=
|
||||
modernc.org/ccgo/v3 v3.11.3/go.mod h1:0oHunRBMBiXOKdaglfMlRPBALQqsfrCKXgw9okQ3GEw=
|
||||
modernc.org/ccgo/v3 v3.12.4/go.mod h1:Bk+m6m2tsooJchP/Yk5ji56cClmN6R1cqc9o/YtbgBQ=
|
||||
modernc.org/ccgo/v3 v3.12.6/go.mod h1:0Ji3ruvpFPpz+yu+1m0wk68pdr/LENABhTrDkMDWH6c=
|
||||
modernc.org/ccgo/v3 v3.12.8/go.mod h1:Hq9keM4ZfjCDuDXxaHptpv9N24JhgBZmUG5q60iLgUo=
|
||||
modernc.org/ccgo/v3 v3.12.11/go.mod h1:0jVcmyDwDKDGWbcrzQ+xwJjbhZruHtouiBEvDfoIsdg=
|
||||
modernc.org/ccgo/v3 v3.12.14/go.mod h1:GhTu1k0YCpJSuWwtRAEHAol5W7g1/RRfS4/9hc9vF5I=
|
||||
modernc.org/ccgo/v3 v3.12.18/go.mod h1:jvg/xVdWWmZACSgOiAhpWpwHWylbJaSzayCqNOJKIhs=
|
||||
modernc.org/ccgo/v3 v3.12.20/go.mod h1:aKEdssiu7gVgSy/jjMastnv/q6wWGRbszbheXgWRHc8=
|
||||
modernc.org/ccgo/v3 v3.12.21/go.mod h1:ydgg2tEprnyMn159ZO/N4pLBqpL7NOkJ88GT5zNU2dE=
|
||||
modernc.org/ccgo/v3 v3.12.22/go.mod h1:nyDVFMmMWhMsgQw+5JH6B6o4MnZ+UQNw1pp52XYFPRk=
|
||||
modernc.org/ccgo/v3 v3.12.25/go.mod h1:UaLyWI26TwyIT4+ZFNjkyTbsPsY3plAEB6E7L/vZV3w=
|
||||
modernc.org/ccgo/v3 v3.12.29/go.mod h1:FXVjG7YLf9FetsS2OOYcwNhcdOLGt8S9bQ48+OP75cE=
|
||||
modernc.org/ccgo/v3 v3.12.36/go.mod h1:uP3/Fiezp/Ga8onfvMLpREq+KUjUmYMxXPO8tETHtA8=
|
||||
modernc.org/ccgo/v3 v3.12.38/go.mod h1:93O0G7baRST1vNj4wnZ49b1kLxt0xCW5Hsa2qRaZPqc=
|
||||
modernc.org/ccgo/v3 v3.12.43/go.mod h1:k+DqGXd3o7W+inNujK15S5ZYuPoWYLpF5PYougCmthU=
|
||||
modernc.org/ccgo/v3 v3.12.46/go.mod h1:UZe6EvMSqOxaJ4sznY7b23/k13R8XNlyWsO5bAmSgOE=
|
||||
modernc.org/ccgo/v3 v3.12.47/go.mod h1:m8d6p0zNps187fhBwzY/ii6gxfjob1VxWb919Nk1HUk=
|
||||
modernc.org/ccgo/v3 v3.12.50/go.mod h1:bu9YIwtg+HXQxBhsRDE+cJjQRuINuT9PUK4orOco/JI=
|
||||
modernc.org/ccgo/v3 v3.12.51/go.mod h1:gaIIlx4YpmGO2bLye04/yeblmvWEmE4BBBls4aJXFiE=
|
||||
modernc.org/ccgo/v3 v3.12.53/go.mod h1:8xWGGTFkdFEWBEsUmi+DBjwu/WLy3SSOrqEmKUjMeEg=
|
||||
modernc.org/ccgo/v3 v3.12.54/go.mod h1:yANKFTm9llTFVX1FqNKHE0aMcQb1fuPJx6p8AcUx+74=
|
||||
modernc.org/ccgo/v3 v3.12.55/go.mod h1:rsXiIyJi9psOwiBkplOaHye5L4MOOaCjHg1Fxkj7IeU=
|
||||
modernc.org/ccgo/v3 v3.12.56/go.mod h1:ljeFks3faDseCkr60JMpeDb2GSO3TKAmrzm7q9YOcMU=
|
||||
modernc.org/ccgo/v3 v3.12.57/go.mod h1:hNSF4DNVgBl8wYHpMvPqQWDQx8luqxDnNGCMM4NFNMc=
|
||||
modernc.org/ccgo/v3 v3.12.60/go.mod h1:k/Nn0zdO1xHVWjPYVshDeWKqbRWIfif5dtsIOCUVMqM=
|
||||
modernc.org/ccgo/v3 v3.12.66/go.mod h1:jUuxlCFZTUZLMV08s7B1ekHX5+LIAurKTTaugUr/EhQ=
|
||||
modernc.org/ccgo/v3 v3.12.67/go.mod h1:Bll3KwKvGROizP2Xj17GEGOTrlvB1XcVaBrC90ORO84=
|
||||
modernc.org/ccgo/v3 v3.12.73/go.mod h1:hngkB+nUUqzOf3iqsM48Gf1FZhY599qzVg1iX+BT3cQ=
|
||||
modernc.org/ccgo/v3 v3.12.81/go.mod h1:p2A1duHoBBg1mFtYvnhAnQyI6vL0uw5PGYLSIgF6rYY=
|
||||
modernc.org/ccgo/v3 v3.12.84/go.mod h1:ApbflUfa5BKadjHynCficldU1ghjen84tuM5jRynB7w=
|
||||
modernc.org/ccgo/v3 v3.12.86/go.mod h1:dN7S26DLTgVSni1PVA3KxxHTcykyDurf3OgUzNqTSrU=
|
||||
modernc.org/ccgo/v3 v3.12.90/go.mod h1:obhSc3CdivCRpYZmrvO88TXlW0NvoSVvdh/ccRjJYko=
|
||||
modernc.org/ccgo/v3 v3.12.92/go.mod h1:5yDdN7ti9KWPi5bRVWPl8UNhpEAtCjuEE7ayQnzzqHA=
|
||||
modernc.org/ccgo/v3 v3.13.1/go.mod h1:aBYVOUfIlcSnrsRVU8VRS35y2DIfpgkmVkYZ0tpIXi4=
|
||||
modernc.org/ccgo/v3 v3.15.9/go.mod h1:md59wBwDT2LznX/OTCPoVS6KIsdRgY8xqQwBV+hkTH0=
|
||||
modernc.org/ccgo/v3 v3.15.10/go.mod h1:wQKxoFn0ynxMuCLfFD09c8XPUCc8obfchoVR9Cn0fI8=
|
||||
modernc.org/ccgo/v3 v3.15.12/go.mod h1:VFePOWoCd8uDGRJpq/zfJ29D0EVzMSyID8LCMWYbX6I=
|
||||
modernc.org/ccgo/v3 v3.15.14/go.mod h1:144Sz2iBCKogb9OKwsu7hQEub3EVgOlyI8wMUPGKUXQ=
|
||||
modernc.org/ccgo/v3 v3.15.15/go.mod h1:z5qltXjU4PJl0pE5nhYQCvA9DhPHiWsl5GWl89+NSYE=
|
||||
modernc.org/ccgo/v3 v3.15.16/go.mod h1:XbKRMeMWMdq712Tr5ECgATYMrzJ+g9zAZEj2ktzBe24=
|
||||
modernc.org/ccgo/v3 v3.15.17/go.mod h1:bofnFkpRFf5gLY+mBZIyTW6FEcp26xi2lgOFk2Rlvs0=
|
||||
modernc.org/ccorpus v1.11.1/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ=
|
||||
modernc.org/ccorpus v1.11.6/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ=
|
||||
modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM=
|
||||
modernc.org/libc v1.9.8/go.mod h1:U1eq8YWr/Kc1RWCMFUWEdkTg8OTcfLw2kY8EDwl039w=
|
||||
modernc.org/libc v1.9.11/go.mod h1:NyF3tsA5ArIjJ83XB0JlqhjTabTCHm9aX4XMPHyQn0Q=
|
||||
modernc.org/libc v1.11.0/go.mod h1:2lOfPmj7cz+g1MrPNmX65QCzVxgNq2C5o0jdLY2gAYg=
|
||||
modernc.org/libc v1.11.2/go.mod h1:ioIyrl3ETkugDO3SGZ+6EOKvlP3zSOycUETe4XM4n8M=
|
||||
modernc.org/libc v1.11.5/go.mod h1:k3HDCP95A6U111Q5TmG3nAyUcp3kR5YFZTeDS9v8vSU=
|
||||
modernc.org/libc v1.11.6/go.mod h1:ddqmzR6p5i4jIGK1d/EiSw97LBcE3dK24QEwCFvgNgE=
|
||||
modernc.org/libc v1.11.11/go.mod h1:lXEp9QOOk4qAYOtL3BmMve99S5Owz7Qyowzvg6LiZso=
|
||||
modernc.org/libc v1.11.13/go.mod h1:ZYawJWlXIzXy2Pzghaf7YfM8OKacP3eZQI81PDLFdY8=
|
||||
modernc.org/libc v1.11.16/go.mod h1:+DJquzYi+DMRUtWI1YNxrlQO6TcA5+dRRiq8HWBWRC8=
|
||||
modernc.org/libc v1.11.19/go.mod h1:e0dgEame6mkydy19KKaVPBeEnyJB4LGNb0bBH1EtQ3I=
|
||||
modernc.org/libc v1.11.24/go.mod h1:FOSzE0UwookyT1TtCJrRkvsOrX2k38HoInhw+cSCUGk=
|
||||
modernc.org/libc v1.11.26/go.mod h1:SFjnYi9OSd2W7f4ct622o/PAYqk7KHv6GS8NZULIjKY=
|
||||
modernc.org/libc v1.11.27/go.mod h1:zmWm6kcFXt/jpzeCgfvUNswM0qke8qVwxqZrnddlDiE=
|
||||
modernc.org/libc v1.11.28/go.mod h1:Ii4V0fTFcbq3qrv3CNn+OGHAvzqMBvC7dBNyC4vHZlg=
|
||||
modernc.org/libc v1.11.31/go.mod h1:FpBncUkEAtopRNJj8aRo29qUiyx5AvAlAxzlx9GNaVM=
|
||||
modernc.org/libc v1.11.34/go.mod h1:+Tzc4hnb1iaX/SKAutJmfzES6awxfU1BPvrrJO0pYLg=
|
||||
modernc.org/libc v1.11.37/go.mod h1:dCQebOwoO1046yTrfUE5nX1f3YpGZQKNcITUYWlrAWo=
|
||||
modernc.org/libc v1.11.39/go.mod h1:mV8lJMo2S5A31uD0k1cMu7vrJbSA3J3waQJxpV4iqx8=
|
||||
modernc.org/libc v1.11.42/go.mod h1:yzrLDU+sSjLE+D4bIhS7q1L5UwXDOw99PLSX0BlZvSQ=
|
||||
modernc.org/libc v1.11.44/go.mod h1:KFq33jsma7F5WXiYelU8quMJasCCTnHK0mkri4yPHgA=
|
||||
modernc.org/libc v1.11.45/go.mod h1:Y192orvfVQQYFzCNsn+Xt0Hxt4DiO4USpLNXBlXg/tM=
|
||||
modernc.org/libc v1.11.47/go.mod h1:tPkE4PzCTW27E6AIKIR5IwHAQKCAtudEIeAV1/SiyBg=
|
||||
modernc.org/libc v1.11.49/go.mod h1:9JrJuK5WTtoTWIFQ7QjX2Mb/bagYdZdscI3xrvHbXjE=
|
||||
modernc.org/libc v1.11.51/go.mod h1:R9I8u9TS+meaWLdbfQhq2kFknTW0O3aw3kEMqDDxMaM=
|
||||
modernc.org/libc v1.11.53/go.mod h1:5ip5vWYPAoMulkQ5XlSJTy12Sz5U6blOQiYasilVPsU=
|
||||
modernc.org/libc v1.11.54/go.mod h1:S/FVnskbzVUrjfBqlGFIPA5m7UwB3n9fojHhCNfSsnw=
|
||||
modernc.org/libc v1.11.55/go.mod h1:j2A5YBRm6HjNkoSs/fzZrSxCuwWqcMYTDPLNx0URn3M=
|
||||
modernc.org/libc v1.11.56/go.mod h1:pakHkg5JdMLt2OgRadpPOTnyRXm/uzu+Yyg/LSLdi18=
|
||||
modernc.org/libc v1.11.58/go.mod h1:ns94Rxv0OWyoQrDqMFfWwka2BcaF6/61CqJRK9LP7S8=
|
||||
modernc.org/libc v1.11.71/go.mod h1:DUOmMYe+IvKi9n6Mycyx3DbjfzSKrdr/0Vgt3j7P5gw=
|
||||
modernc.org/libc v1.11.75/go.mod h1:dGRVugT6edz361wmD9gk6ax1AbDSe0x5vji0dGJiPT0=
|
||||
modernc.org/libc v1.11.82/go.mod h1:NF+Ek1BOl2jeC7lw3a7Jj5PWyHPwWD4aq3wVKxqV1fI=
|
||||
modernc.org/libc v1.11.86/go.mod h1:ePuYgoQLmvxdNT06RpGnaDKJmDNEkV7ZPKI2jnsvZoE=
|
||||
modernc.org/libc v1.11.87/go.mod h1:Qvd5iXTeLhI5PS0XSyqMY99282y+3euapQFxM7jYnpY=
|
||||
modernc.org/libc v1.11.88/go.mod h1:h3oIVe8dxmTcchcFuCcJ4nAWaoiwzKCdv82MM0oiIdQ=
|
||||
modernc.org/libc v1.11.98/go.mod h1:ynK5sbjsU77AP+nn61+k+wxUGRx9rOFcIqWYYMaDZ4c=
|
||||
modernc.org/libc v1.11.101/go.mod h1:wLLYgEiY2D17NbBOEp+mIJJJBGSiy7fLL4ZrGGZ+8jI=
|
||||
modernc.org/libc v1.12.0/go.mod h1:2MH3DaF/gCU8i/UBiVE1VFRos4o523M7zipmwH8SIgQ=
|
||||
modernc.org/libc v1.14.1/go.mod h1:npFeGWjmZTjFeWALQLrvklVmAxv4m80jnG3+xI8FdJk=
|
||||
modernc.org/libc v1.14.2/go.mod h1:MX1GBLnRLNdvmK9azU9LCxZ5lMyhrbEMK8rG3X/Fe34=
|
||||
modernc.org/libc v1.14.3/go.mod h1:GPIvQVOVPizzlqyRX3l756/3ppsAgg1QgPxjr5Q4agQ=
|
||||
modernc.org/libc v1.14.5/go.mod h1:2PJHINagVxO4QW/5OQdRrvMYo+bm5ClpUFfyXCYl9ak=
|
||||
modernc.org/libc v1.14.6/go.mod h1:2PJHINagVxO4QW/5OQdRrvMYo+bm5ClpUFfyXCYl9ak=
|
||||
modernc.org/libc v1.14.7/go.mod h1:f8xfWXW8LW41qb4X5+huVQo5dcfPlq7Cbny2TDheMv0=
|
||||
modernc.org/libc v1.14.8/go.mod h1:9+JCLb1MWSY23smyOpIPbd5ED+rSS/ieiDWUpdyO3mo=
|
||||
modernc.org/libc v1.14.10/go.mod h1:y1MtIWhwpJFpLYm6grAThtuXJKEsY6xkdZmXbRngIdo=
|
||||
modernc.org/libc v1.14.12 h1:pUBZTYoISfbb4pCf4PECENpbvwDBxeKc+/dS9LyOWFM=
|
||||
modernc.org/libc v1.14.12/go.mod h1:fJdoe23MHu2ruPQkFPPqCpToDi5cckzsbmkI6Ez0LqQ=
|
||||
modernc.org/mathutil v1.1.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
|
||||
modernc.org/mathutil v1.2.2/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
|
||||
modernc.org/mathutil v1.4.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
|
||||
modernc.org/mathutil v1.4.1 h1:ij3fYGe8zBF4Vu+g0oT7mB06r8sqGWKuJu1yXeR4by8=
|
||||
modernc.org/mathutil v1.4.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
|
||||
modernc.org/memory v1.0.4/go.mod h1:nV2OApxradM3/OVbs2/0OsP6nPfakXpi50C7dcoHXlc=
|
||||
modernc.org/memory v1.0.5/go.mod h1:B7OYswTRnfGg+4tDH1t1OeUNnsy2viGTdME4tzd+IjM=
|
||||
modernc.org/memory v1.0.6/go.mod h1:/0wo5ibyrQiaoUoH7f9D8dnglAmILJ5/cxZlRECf+Nw=
|
||||
modernc.org/memory v1.0.7 h1:UE3cxTRFa5tfUibAV7Jqq8P7zRY0OlJg+yWVIIaluEE=
|
||||
modernc.org/memory v1.0.7/go.mod h1:/0wo5ibyrQiaoUoH7f9D8dnglAmILJ5/cxZlRECf+Nw=
|
||||
modernc.org/opt v0.1.1/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
|
||||
modernc.org/strutil v1.1.1/go.mod h1:DE+MQQ/hjKBZS2zNInV5hhcipt5rLPWkmpbGeW5mmdw=
|
||||
modernc.org/token v1.0.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
honnef.co/go/tools v0.1.3/go.mod h1:NgwopIslSNH47DimFoV78dnkksY2EFtX0ajyb3K/las=
|
||||
modernc.org/cc/v4 v4.21.2 h1:dycHFB/jDc3IyacKipCNSDrjIC0Lm1hyoWOZTRR20Lk=
|
||||
modernc.org/cc/v4 v4.21.2/go.mod h1:HM7VJTZbUCR3rV8EYBi9wxnJ0ZBRiGE5OeGXNA0IsLQ=
|
||||
modernc.org/ccgo/v4 v4.17.8 h1:yyWBf2ipA0Y9GGz/MmCmi3EFpKgeS7ICrAFes+suEbs=
|
||||
modernc.org/ccgo/v4 v4.17.8/go.mod h1:buJnJ6Fn0tyAdP/dqePbrrvLyr6qslFfTbFrCuaYvtA=
|
||||
modernc.org/fileutil v1.3.0 h1:gQ5SIzK3H9kdfai/5x41oQiKValumqNTDXMvKo62HvE=
|
||||
modernc.org/fileutil v1.3.0/go.mod h1:XatxS8fZi3pS8/hKG2GH/ArUogfxjpEKs3Ku3aK4JyQ=
|
||||
modernc.org/gc/v2 v2.4.1 h1:9cNzOqPyMJBvrUipmynX0ZohMhcxPtMccYgGOJdOiBw=
|
||||
modernc.org/gc/v2 v2.4.1/go.mod h1:wzN5dK1AzVGoH6XOzc3YZ+ey/jPgYHLuVckd62P0GYU=
|
||||
modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU=
|
||||
modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg=
|
||||
modernc.org/memory v1.11.0 h1:o4QC8aMQzmcwCK3t3Ux/ZHmwFPzE6hf2Y5LbkRs+hbI=
|
||||
modernc.org/memory v1.11.0/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw=
|
||||
modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4=
|
||||
modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
|
||||
modernc.org/sortutil v1.2.0 h1:jQiD3PfS2REGJNzNCMMaLSp/wdMNieTbKX920Cqdgqc=
|
||||
modernc.org/sortutil v1.2.0/go.mod h1:TKU2s7kJMf1AE84OoiGppNHJwvB753OYfNl2WRb++Ss=
|
||||
modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA=
|
||||
modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0=
|
||||
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
|
||||
modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
|
||||
|
||||
287
gomod2nix.toml
Normal file
287
gomod2nix.toml
Normal file
@@ -0,0 +1,287 @@
|
||||
schema = 3
|
||||
|
||||
[mod]
|
||||
[mod."github.com/Baidu-AIP/golang-sdk"]
|
||||
version = "v1.1.1"
|
||||
hash = "sha256-hKshA0K92bKuK92mmtM0osVmqLJcSbeobeWSDpQoRCo="
|
||||
[mod."github.com/FloatTech/AnimeAPI"]
|
||||
version = "v1.7.1-0.20251028071248-0c948e3db65c"
|
||||
hash = "sha256-EkC8QYgmXElKGM8GzcQ4r/c+lM1ysJOccfoeqhMtAvs="
|
||||
[mod."github.com/FloatTech/floatbox"]
|
||||
version = "v0.0.0-20251002074805-f95cbc7edb31"
|
||||
hash = "sha256-c50unGhF0JVPHN8geZM/YYQKgGqJgCtVksh4Ij1Pg+4="
|
||||
[mod."github.com/FloatTech/gg"]
|
||||
version = "v1.1.3"
|
||||
hash = "sha256-7K/R2mKjUHVnoJ3b1wDObJ5Un2Htj59Y97G1Ja1tuPo="
|
||||
[mod."github.com/FloatTech/imgfactory"]
|
||||
version = "v0.2.2-0.20230413152719-e101cc3606ef"
|
||||
hash = "sha256-2okFyPQSYIxrc8hxICsbjEM9xq25a3I2A4wmDIYFCg8="
|
||||
[mod."github.com/FloatTech/rendercard"]
|
||||
version = "v0.2.0"
|
||||
hash = "sha256-fgntEYGh2mEl618hM13kb0GGeQEXdP+lochYX8F2OXs="
|
||||
[mod."github.com/FloatTech/sqlite"]
|
||||
version = "v1.7.2"
|
||||
hash = "sha256-R9QaP5FQwtWpHdbCoNX/rYOS/CgkIeRdFB9cwJ4n/JM="
|
||||
[mod."github.com/FloatTech/ttl"]
|
||||
version = "v0.0.0-20250224045156-012b1463287d"
|
||||
hash = "sha256-C5xBt0roPgahradCOTgkhL+j5bvoSXmGwdqcu0aSczc="
|
||||
[mod."github.com/FloatTech/zbpctrl"]
|
||||
version = "v1.7.1"
|
||||
hash = "sha256-wkeiaUTpPVbpH7fcXeoLtG+aGIMJbvoc/9sbi2IXK0I="
|
||||
[mod."github.com/FloatTech/zbputils"]
|
||||
version = "v1.7.2-0.20251223092310-25b804fef625"
|
||||
hash = "sha256-iidCYw0+eBr4PLRxpAcxGKKBdQNUCLF7UBcd959FIe0="
|
||||
[mod."github.com/PuerkitoBio/goquery"]
|
||||
version = "v1.10.3"
|
||||
hash = "sha256-Mth7nYm/MtcOhPMbHj7gXF+Mot7eDUBVN570RitGR/c="
|
||||
[mod."github.com/RomiChan/syncx"]
|
||||
version = "v0.0.0-20240418144900-b7402ffdebc7"
|
||||
hash = "sha256-L1j1vgiwqXpF9pjMoRRlrQUHzoULisw/01plaEAwxs4="
|
||||
[mod."github.com/RomiChan/websocket"]
|
||||
version = "v1.4.3-0.20251002072000-d3eb41798438"
|
||||
hash = "sha256-vLu9Va+9AbOIdh1LEetz5JlJK0P2IXKsYRvCdAO8tYw="
|
||||
[mod."github.com/Tnze/go-mc"]
|
||||
version = "v1.20.2"
|
||||
hash = "sha256-Nu4PXNxeARH0itm6yIIplFaywL2yQnPJFksmmuyIptI="
|
||||
[mod."github.com/adamzy/cedar-go"]
|
||||
version = "v0.0.0-20170805034717-80a9c64b256d"
|
||||
hash = "sha256-N19KTxh70IUBqnchFuWkrJD8uuFOIVqv1iSuN3YFIT0="
|
||||
[mod."github.com/ajstarks/svgo"]
|
||||
version = "v0.0.0-20211024235047-1546f124cd8b"
|
||||
hash = "sha256-sPwt5sImKFk949TzUeYEF2UiJDqHxXFJKRL2Y7JWJ6Y="
|
||||
[mod."github.com/andybalholm/cascadia"]
|
||||
version = "v1.3.3"
|
||||
hash = "sha256-jv7ZshpSd7FZzKKN6hqlUgiR8C3y85zNIS/hq7g76Ho="
|
||||
[mod."github.com/antchfx/htmlquery"]
|
||||
version = "v1.3.4"
|
||||
hash = "sha256-nrtIgRgdOvo0iIQyrhHOFKOmoT8e2gduUsct3f5zDNA="
|
||||
[mod."github.com/antchfx/xpath"]
|
||||
version = "v1.3.5"
|
||||
hash = "sha256-AVM0rR81hgVAI0QVzlz4WijFUjByf6Zew3ZwuikKw2Q="
|
||||
[mod."github.com/corona10/goimagehash"]
|
||||
version = "v1.1.1-0.20240121134706-d8115886f360"
|
||||
hash = "sha256-/corDVYILmy/DMWtgM1D1MK6I7pejA5JNfaxeojwkkA="
|
||||
[mod."github.com/davidscholberg/go-durationfmt"]
|
||||
version = "v0.0.0-20170122144659-64843a2083d3"
|
||||
hash = "sha256-0rdbpBf3AAjMpxvVEGFb2ImgB2i7vdEhIwCyqJs1iHE="
|
||||
[mod."github.com/disintegration/imaging"]
|
||||
version = "v1.6.2"
|
||||
hash = "sha256-pSeMTPvSkxlthh65LjNYYhPLvCZDkBgVgAGYWW0Aguo="
|
||||
[mod."github.com/dustin/go-humanize"]
|
||||
version = "v1.0.1"
|
||||
hash = "sha256-yuvxYYngpfVkUg9yAmG99IUVmADTQA0tMbBXe0Fq0Mc="
|
||||
[mod."github.com/ebitengine/oto/v3"]
|
||||
version = "v3.4.0"
|
||||
hash = "sha256-8JU4iu+2pUKWVWMpEe8EAZ8FVo3MZdILu82vVVmDSVY="
|
||||
[mod."github.com/ebitengine/purego"]
|
||||
version = "v0.9.1"
|
||||
hash = "sha256-iVfU8vaJ7IPa92dUeHeuW+yKvUbe59F/eV7GlDRIAcE="
|
||||
[mod."github.com/ericpauley/go-quantize"]
|
||||
version = "v0.0.0-20200331213906-ae555eb2afa4"
|
||||
hash = "sha256-sMN6D7IlDpDqUWM8ppoE5Sdb7DvLAJaN6qAucBWJ3rs="
|
||||
[mod."github.com/fumiama/ahsai"]
|
||||
version = "v0.1.1"
|
||||
hash = "sha256-knYw0R5fhjE/asc/TwlGJDzVr+Oaj8sH7kr7x6Mqs3E="
|
||||
[mod."github.com/fumiama/cron"]
|
||||
version = "v1.3.0"
|
||||
hash = "sha256-/sN7X8dKXQgv8J+EDzVUB+o+AY9gBC8e1C6sYhaTy1k="
|
||||
[mod."github.com/fumiama/deepinfra"]
|
||||
version = "v0.0.0-20251221163610-e98ee3ba437a"
|
||||
hash = "sha256-XubewUDOLaN7fgv9E8PAfP7DtADdLm+M2sgSn5OE6eQ="
|
||||
[mod."github.com/fumiama/go-base16384"]
|
||||
version = "v1.7.1"
|
||||
hash = "sha256-Fd1QaeYx+3q4C3XQXlPFnDmKPsoZH6837fN/7rn8i9s="
|
||||
[mod."github.com/fumiama/go-onebot-agent"]
|
||||
version = "v0.0.0-20251221163750-c11c679e4636"
|
||||
hash = "sha256-U0aZeAo1jNo2u75HRItGhA8xH2a+2FWPf9cGXcAH0fU="
|
||||
[mod."github.com/fumiama/go-registry"]
|
||||
version = "v0.2.7"
|
||||
hash = "sha256-Rjl+z0Hlp2LMi8+pnFe5HrxctyHMi7UPiK33g/OgLdA="
|
||||
[mod."github.com/fumiama/go-simple-protobuf"]
|
||||
version = "v0.2.0"
|
||||
hash = "sha256-2kULBi1sXsFDX2g/KRFmCGkwF60o/UXacNUbIYa/cvw="
|
||||
[mod."github.com/fumiama/gofastTEA"]
|
||||
version = "v0.1.3"
|
||||
hash = "sha256-/Qu57mkkFt7aFufhlkMYPgrWj5XCGbuM28EHDD8w4XY="
|
||||
[mod."github.com/fumiama/gotracemoe"]
|
||||
version = "v0.0.3"
|
||||
hash = "sha256-O3cDkVXu5NG1ZtzubxhH+S91zfgu4uH1L+OiSGYSNXQ="
|
||||
[mod."github.com/fumiama/imgsz"]
|
||||
version = "v0.0.4"
|
||||
hash = "sha256-rrGx+v41OEl0ATwL6u5TNcpfkCQbj3jFNnGiQUNu2qs="
|
||||
[mod."github.com/fumiama/jieba"]
|
||||
version = "v0.0.0-20221203025406-36c17a10b565"
|
||||
hash = "sha256-DvDx1pdldkdaSszrbadM/VwqT9TTSmWl6G6a+ysXYEM="
|
||||
[mod."github.com/fumiama/orbyte"]
|
||||
version = "v0.0.0-20251002065953-3bb358367eb5"
|
||||
hash = "sha256-mRQwhR0v922UXlJ7lXo/osv21K8kZDaHx3DsBCjmzoo="
|
||||
[mod."github.com/fumiama/slowdo"]
|
||||
version = "v0.0.0-20241001074058-27c4fe5259a4"
|
||||
hash = "sha256-rsV3MKRCSOBMIgJXFCGbCHRY2aBAb32ftU49hT3GjqY="
|
||||
[mod."github.com/fumiama/terasu"]
|
||||
version = "v1.0.2"
|
||||
hash = "sha256-tYHvvfJT1/R4koqGFPjdU211Dcez2XWuvpRke/tFfQ8="
|
||||
[mod."github.com/fumiama/unibase2n"]
|
||||
version = "v0.0.0-20240530074540-ec743fd5a6d6"
|
||||
hash = "sha256-I3xNzjrj5y0fy0dfa75V57GanfmHIHmubEn9/y0BBHw="
|
||||
[mod."github.com/gabriel-vasile/mimetype"]
|
||||
version = "v1.4.12"
|
||||
hash = "sha256-vY2g58yUrkT//8fttRKhS9rbg89YSae/BzOARS5uH30="
|
||||
[mod."github.com/go-ole/go-ole"]
|
||||
version = "v1.3.0"
|
||||
hash = "sha256-tF8t3VcV71jQ4jbPL91BwR59AKDpUAFV1waIKzkXJu8="
|
||||
[mod."github.com/golang/freetype"]
|
||||
version = "v0.0.0-20170609003504-e2365dfdc4a0"
|
||||
hash = "sha256-AHAFBd20/tqxohkWyQkui2bUef9i1HWYgk9LOIFErvA="
|
||||
[mod."github.com/golang/groupcache"]
|
||||
version = "v0.0.0-20241129210726-2c02b8208cf8"
|
||||
hash = "sha256-AdLZ3dJLe/yduoNvZiXugZxNfmwJjNQyQGsIdzYzH74="
|
||||
[mod."github.com/google/uuid"]
|
||||
version = "v1.6.0"
|
||||
hash = "sha256-VWl9sqUzdOuhW0KzQlv0gwwUQClYkmZwSydHG2sALYw="
|
||||
[mod."github.com/gopxl/beep/v2"]
|
||||
version = "v2.1.1"
|
||||
hash = "sha256-JLCUJCG+VvNlVF296JWIOUvvUFHlqEAJvZfw853qwwU="
|
||||
[mod."github.com/jfreymuth/oggvorbis"]
|
||||
version = "v1.0.5"
|
||||
hash = "sha256-jphTCaPr34ZT9Id4ZZ6zU9Vnxzy6cTjCwjpQ819eGV0="
|
||||
[mod."github.com/jfreymuth/vorbis"]
|
||||
version = "v1.0.2"
|
||||
hash = "sha256-gVS+/PZ5pDnswpTQNZILcrx5ZNq9ShXd6vXn7Jabes4="
|
||||
[mod."github.com/jinzhu/gorm"]
|
||||
version = "v1.9.16"
|
||||
hash = "sha256-qKEwgNE8NxcX1uzT20LwC1TKVmve/nIy+oxdAKlxAuc="
|
||||
[mod."github.com/jinzhu/inflection"]
|
||||
version = "v1.0.0"
|
||||
hash = "sha256-3h3pHib5MaCXKyKLIMyQnSptDJ16kPjCOQPoEBoQsZg="
|
||||
[mod."github.com/jozsefsallai/gophersauce"]
|
||||
version = "v1.0.1"
|
||||
hash = "sha256-29DsfnGmK51DPunR/leRBKCcokN/yLoB7S2HxCsqtgY="
|
||||
[mod."github.com/json-iterator/go"]
|
||||
version = "v1.1.12"
|
||||
hash = "sha256-To8A0h+lbfZ/6zM+2PpRpY3+L6725OPC66lffq6fUoM="
|
||||
[mod."github.com/kanrichan/resvg-go"]
|
||||
version = "v0.0.2-0.20231001163256-63db194ca9f5"
|
||||
hash = "sha256-plRZ3yhyCafCXmAD4vnFUoCTRsHmLp7Jn9gFKcEKbds="
|
||||
[mod."github.com/lithammer/fuzzysearch"]
|
||||
version = "v1.1.8"
|
||||
hash = "sha256-aMMRcrlUc9CBiiNkcnWWn4hfNMNyVhrAt67kvP4D4Do="
|
||||
[mod."github.com/liuzl/cedar-go"]
|
||||
version = "v0.0.0-20170805034717-80a9c64b256d"
|
||||
hash = "sha256-N19KTxh70IUBqnchFuWkrJD8uuFOIVqv1iSuN3YFIT0="
|
||||
[mod."github.com/liuzl/da"]
|
||||
version = "v0.0.0-20180704015230-14771aad5b1d"
|
||||
hash = "sha256-J43kwDFmB6LzDhS3Ig/4ddZUTXz1cKztbTA3hILScs8="
|
||||
[mod."github.com/liuzl/gocc"]
|
||||
version = "v0.0.0-20231231122217-0372e1059ca5"
|
||||
hash = "sha256-Dr1xDbO+eR4Y/EpPgQ/S6g6C5etRFKWr8de77skcJR8="
|
||||
[mod."github.com/lufia/plan9stats"]
|
||||
version = "v0.0.0-20251013123823-9fd1530e3ec3"
|
||||
hash = "sha256-N760qPHHaMcxICyA3Ap/b/3exi40AStu7458VPvC9GI="
|
||||
[mod."github.com/mattn/go-isatty"]
|
||||
version = "v0.0.20"
|
||||
hash = "sha256-qhw9hWtU5wnyFyuMbKx+7RB8ckQaFQ8D+8GKPkN3HHQ="
|
||||
[mod."github.com/mmcdole/gofeed"]
|
||||
version = "v1.3.0"
|
||||
hash = "sha256-GHpqGZvNg+3RSIkVKXrWg6/e8dJD8Y5v2Sx6MzmRlQ0="
|
||||
[mod."github.com/mmcdole/goxpp"]
|
||||
version = "v1.1.1"
|
||||
hash = "sha256-LtZDUtflL06HnDuQiCS6cpoF3VPk+gmABkYXBvdQOz0="
|
||||
[mod."github.com/modern-go/concurrent"]
|
||||
version = "v0.0.0-20180306012644-bacd9c7ef1dd"
|
||||
hash = "sha256-OTySieAgPWR4oJnlohaFTeK1tRaVp/b0d1rYY8xKMzo="
|
||||
[mod."github.com/modern-go/reflect2"]
|
||||
version = "v1.0.2"
|
||||
hash = "sha256-+W9EIW7okXIXjWEgOaMh58eLvBZ7OshW2EhaIpNLSBU="
|
||||
[mod."github.com/mroth/weightedrand"]
|
||||
version = "v1.0.0"
|
||||
hash = "sha256-bP+yIaBUY5+oI455mNM8zh14z/SNPaQg44L3RJ0/v/c="
|
||||
[mod."github.com/ncruces/go-strftime"]
|
||||
version = "v1.0.0"
|
||||
hash = "sha256-GYIwYDONuv/yTE0AEugCHQbtV3oiBaco93xUNYFcVBQ="
|
||||
[mod."github.com/nfnt/resize"]
|
||||
version = "v0.0.0-20180221191011-83c6a9932646"
|
||||
hash = "sha256-yvPV+HlDOyJsiwAcVHQkmtw8DHSXyw+cXHkigXm8rAA="
|
||||
[mod."github.com/notnil/chess"]
|
||||
version = "v1.10.0"
|
||||
hash = "sha256-hsUOS4rVuMW+UCPJzhsZh3PHCi1Lol12BwKujcICayo="
|
||||
[mod."github.com/pbnjay/memory"]
|
||||
version = "v0.0.0-20210728143218-7b4eea64cf58"
|
||||
hash = "sha256-QI+F1oPLOOtwNp8+m45OOoSfYFs3QVjGzE0rFdpF/IA="
|
||||
[mod."github.com/pkg/errors"]
|
||||
version = "v0.9.1"
|
||||
hash = "sha256-mNfQtcrQmu3sNg/7IwiieKWOgFQOVVe2yXgKBpe/wZw="
|
||||
[mod."github.com/pkumza/numcn"]
|
||||
version = "v1.0.0"
|
||||
hash = "sha256-cPxqj5tb10+MurN1Lehkk/v8KjaxXpL08+pVgL4x4Hg="
|
||||
[mod."github.com/power-devops/perfstat"]
|
||||
version = "v0.0.0-20240221224432-82ca36839d55"
|
||||
hash = "sha256-ujzuJ1ttQgjHQJEij4O/2+I8DZaUVZQCQgA4ysfqulI="
|
||||
[mod."github.com/remyoudompheng/bigfft"]
|
||||
version = "v0.0.0-20230129092748-24d4a6f8daec"
|
||||
hash = "sha256-vYmpyCE37eBYP/navhaLV4oX4/nu0Z/StAocLIFqrmM="
|
||||
[mod."github.com/shirou/gopsutil/v4"]
|
||||
version = "v4.25.11"
|
||||
hash = "sha256-2Hzp/8m/otBxC32lvVqFz94FivWP3ApNlvJ/iX85v4Y="
|
||||
[mod."github.com/sirupsen/logrus"]
|
||||
version = "v1.9.3"
|
||||
hash = "sha256-EnxsWdEUPYid+aZ9H4/iMTs1XMvCLbXZRDyvj89Ebms="
|
||||
[mod."github.com/tetratelabs/wazero"]
|
||||
version = "v1.9.0"
|
||||
hash = "sha256-b8D0cDMuDgyjvJ6LFY8REdcL95BIjM27SeOEQWfB0+0="
|
||||
[mod."github.com/tidwall/gjson"]
|
||||
version = "v1.18.0"
|
||||
hash = "sha256-CO6hqDu8Y58Po6A01e5iTpwiUBQ5khUZsw7czaJHw0I="
|
||||
[mod."github.com/tidwall/match"]
|
||||
version = "v1.2.0"
|
||||
hash = "sha256-O2wTU0SmNIEEOxfncl2BW2czgWeIW5vqR6+A7dtNtXI="
|
||||
[mod."github.com/tidwall/pretty"]
|
||||
version = "v1.2.1"
|
||||
hash = "sha256-S0uTDDGD8qr415Ut7QinyXljCp0TkL4zOIrlJ+9OMl8="
|
||||
[mod."github.com/tklauser/go-sysconf"]
|
||||
version = "v0.3.16"
|
||||
hash = "sha256-hNVbsk0G+M9bLtHNywPD0nCW0z/9pM4jQV53nnDo/MY="
|
||||
[mod."github.com/tklauser/numcpus"]
|
||||
version = "v0.11.0"
|
||||
hash = "sha256-ObydGqAvRiHwVfO2ytmL28pRHyuCWYWwjH4hWdgI/Vs="
|
||||
[mod."github.com/wcharczuk/go-chart/v2"]
|
||||
version = "v2.1.2"
|
||||
hash = "sha256-GXWWea/u6BezTsPPrWhTYiTetPP/YW6P+Sj4YdocPaM="
|
||||
[mod."github.com/wdvxdr1123/ZeroBot"]
|
||||
version = "v1.8.2"
|
||||
hash = "sha256-/GrCbvSCFQgIDAyg1MKCms2FIjyY4/YAQkucfbdud3k="
|
||||
[mod."github.com/yusufpapurcu/wmi"]
|
||||
version = "v1.2.4"
|
||||
hash = "sha256-N+YDBjOW59YOsZ2lRBVtFsEEi48KhNQRb63/0ZSU3bA="
|
||||
[mod."gitlab.com/gomidi/midi/v2"]
|
||||
version = "v2.3.16"
|
||||
hash = "sha256-o+6UtQH+TRSQlcX8J53esAA/b2c9e7BY7gcO5iSeOy0="
|
||||
[mod."golang.org/x/image"]
|
||||
version = "v0.34.0"
|
||||
hash = "sha256-7V1bDhd++dCw11DCXOAUb5f1Hj51EfS0DZ03pq0V/p0="
|
||||
[mod."golang.org/x/net"]
|
||||
version = "v0.48.0"
|
||||
hash = "sha256-oZpddsiJwWCH3Aipa+XXpy7G/xHY5fEagUSok7T0bXE="
|
||||
[mod."golang.org/x/sys"]
|
||||
version = "v0.39.0"
|
||||
hash = "sha256-dxTBu/JAWUkPbjFIXXRFdhQWyn+YyEpIC+tWqGo0Y6U="
|
||||
[mod."golang.org/x/text"]
|
||||
version = "v0.32.0"
|
||||
hash = "sha256-9PXtWBKKY9rG4AgjSP4N+I1DhepXhy8SF/vWSIDIoWs="
|
||||
[mod."gopkg.in/yaml.v3"]
|
||||
version = "v3.0.1"
|
||||
hash = "sha256-FqL9TKYJ0XkNwJFnq9j0VvJ5ZUU1RvH/52h/f5bkYAU="
|
||||
[mod."modernc.org/libc"]
|
||||
version = "v0.0.0-20240530081950-6f6d8586b5c5"
|
||||
hash = "sha256-SJYYRaiDUmIbqy9l/IgiT/4VkFsPYsaslqGEowut34w="
|
||||
replaced = "github.com/fumiama/libc"
|
||||
[mod."modernc.org/mathutil"]
|
||||
version = "v1.7.1"
|
||||
hash = "sha256-COZ5rF2GhQVR1r6a0DanJ8qwQ94JSKdQxTMWrDzE0Cc="
|
||||
[mod."modernc.org/memory"]
|
||||
version = "v1.11.0"
|
||||
hash = "sha256-MkybF8vvrxXS5j7O8w3skwTo0aMo1yjWS0K440rYcHM="
|
||||
[mod."modernc.org/sqlite"]
|
||||
version = "v1.29.10-simp"
|
||||
hash = "sha256-HCUVN6gZDG0g2WIsQ4ksqE1+XR1IjxvnqEBEU2MO1eE="
|
||||
replaced = "github.com/fumiama/sqlite3"
|
||||
@@ -1,45 +0,0 @@
|
||||
package kanban
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/fumiama/go-registry"
|
||||
)
|
||||
|
||||
var (
|
||||
info = [...]string{
|
||||
"* OneBot + ZeroBot + Golang",
|
||||
"* Version 1.3.2 - 2022-03-13 13:21:15 +0800 CST",
|
||||
"* Copyright © 2020 - 2022 FloatTech. All Rights Reserved.",
|
||||
"* Project: https://github.com/FloatTech/ZeroBot-Plugin",
|
||||
}
|
||||
// Banner ...
|
||||
Banner = strings.Join(info[:], "\n")
|
||||
reg = registry.NewRegReader("reilia.westeurope.cloudapp.azure.com:32664", "fumiama")
|
||||
)
|
||||
|
||||
// PrintBanner ...
|
||||
func PrintBanner() {
|
||||
fmt.Print(
|
||||
"\n======================[ZeroBot-Plugin]======================",
|
||||
"\n", Banner, "\n",
|
||||
"----------------------[ZeroBot-公告栏]----------------------",
|
||||
"\n", Kanban(), "\n",
|
||||
"============================================================\n\n",
|
||||
)
|
||||
}
|
||||
|
||||
// Kanban ...
|
||||
func Kanban() string {
|
||||
err := reg.Connect()
|
||||
if err != nil {
|
||||
return err.Error()
|
||||
}
|
||||
defer reg.Close()
|
||||
text, err := reg.Get("ZeroBot-Plugin/kanban")
|
||||
if err != nil {
|
||||
return err.Error()
|
||||
}
|
||||
return text
|
||||
}
|
||||
15
kanban/banner/banner.go
Normal file
15
kanban/banner/banner.go
Normal file
@@ -0,0 +1,15 @@
|
||||
// Code generated by kanban/gen. DO NOT EDIT.
|
||||
|
||||
package banner
|
||||
|
||||
// Version ...
|
||||
var Version = "v1.10.5"
|
||||
|
||||
// Copyright ...
|
||||
var Copyright = "© 2020 - 2025 FloatTech"
|
||||
|
||||
// Banner ...
|
||||
var Banner = "* OneBot + ZeroBot + Golang\n" +
|
||||
"* Version " + Version + " - 2025-12-23 17:25:19 +0800 CST\n" +
|
||||
"* Copyright " + Copyright + ". All Rights Reserved.\n" +
|
||||
"* Project: https://github.com/FloatTech/ZeroBot-Plugin"
|
||||
51
kanban/gen/banner.go
Normal file
51
kanban/gen/banner.go
Normal file
@@ -0,0 +1,51 @@
|
||||
// Package main generates banner.go
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
const banner = `// Code generated by kanban/gen. DO NOT EDIT.
|
||||
|
||||
package banner
|
||||
|
||||
// Version ...
|
||||
var Version = "%s"
|
||||
|
||||
// Copyright ...
|
||||
var Copyright = "© 2020 - %d FloatTech"
|
||||
|
||||
// Banner ...
|
||||
var Banner = "* OneBot + ZeroBot + Golang\n" +
|
||||
"* Version " + Version + " - %s\n" +
|
||||
"* Copyright " + Copyright + ". All Rights Reserved.\n" +
|
||||
"* Project: https://github.com/FloatTech/ZeroBot-Plugin"
|
||||
`
|
||||
|
||||
const timeformat = `2006-01-02 15:04:05 +0800 CST`
|
||||
|
||||
func main() {
|
||||
f, err := os.Create("banner/banner.go")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer f.Close()
|
||||
vartag := bytes.NewBuffer(nil)
|
||||
vartagcmd := exec.Command("git", "tag", "--sort=committerdate")
|
||||
vartagcmd.Stdout = vartag
|
||||
err = vartagcmd.Run()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
s := strings.Split(vartag.String(), "\n")
|
||||
now := time.Now()
|
||||
_, err = fmt.Fprintf(f, banner, s[len(s)-2], now.Year(), now.Format(timeformat))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,44 @@
|
||||
// Package kanban 打印版本信息
|
||||
package kanban
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/FloatTech/zbputils/control"
|
||||
"github.com/fumiama/go-registry"
|
||||
|
||||
"github.com/FloatTech/ZeroBot-Plugin/kanban/banner"
|
||||
)
|
||||
|
||||
//go:generate go run github.com/FloatTech/ZeroBot-Plugin/kanban/gen
|
||||
|
||||
func init() {
|
||||
PrintBanner()
|
||||
}
|
||||
|
||||
var reg = registry.NewRegReader("reilia.fumiama.top:32664", control.Md5File, "fumiama")
|
||||
|
||||
// PrintBanner ...
|
||||
func PrintBanner() {
|
||||
fmt.Print(
|
||||
"\n======================[ZeroBot-Plugin]======================",
|
||||
"\n", banner.Banner, "\n",
|
||||
"----------------------[ZeroBot-公告栏]----------------------",
|
||||
"\n", Kanban(), "\n",
|
||||
"============================================================\n\n",
|
||||
)
|
||||
}
|
||||
|
||||
// Kanban ...
|
||||
func Kanban() string {
|
||||
err := reg.Connect()
|
||||
if err != nil {
|
||||
return err.Error()
|
||||
}
|
||||
defer reg.Close()
|
||||
text, err := reg.Get("ZeroBot-Plugin/kanban")
|
||||
if err != nil {
|
||||
return err.Error()
|
||||
}
|
||||
return text
|
||||
}
|
||||
|
||||
319
main.go
319
main.go
@@ -1,13 +1,22 @@
|
||||
// Package main ZeroBot-Plugin main file
|
||||
package main
|
||||
|
||||
//go:generate go run github.com/FloatTech/ZeroBot-Plugin/abineundo/ref -r .
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"os"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/FloatTech/ZeroBot-Plugin/kanban" // 在最前打印 banner
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/abineundo" // 设置插件优先级
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/console" // 更改控制台属性
|
||||
"github.com/FloatTech/ZeroBot-Plugin/kanban" // 打印 banner
|
||||
|
||||
// ---------以下插件均可通过前面加 // 注释,注释后停用并不加载插件--------- //
|
||||
// ----------------------插件优先级按顺序从高到低---------------------- //
|
||||
@@ -23,18 +32,20 @@ import (
|
||||
// vvvvvvvvvvvvvv //
|
||||
// vvvv //
|
||||
|
||||
// webctrl "github.com/FloatTech/zbputils/control/web" // web 后端控制
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/antiabuse" // 违禁词
|
||||
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/chat" // 基础词库
|
||||
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/sleep_manage" // 统计睡眠时间
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/chatcount" // 聊天时长统计
|
||||
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/sleepmanage" // 统计睡眠时间
|
||||
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/airecord" // 群应用:AI声聊
|
||||
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/atri" // ATRI词库
|
||||
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/manager" // 群管
|
||||
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/thesaurus" // 词典匹配回复
|
||||
|
||||
_ "github.com/FloatTech/zbputils/job" // 定时指令触发器
|
||||
|
||||
// ^^^^ //
|
||||
@@ -55,65 +66,100 @@ import (
|
||||
// vvvvvvvvvvvvvv //
|
||||
// vvvv //
|
||||
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/acgimage" // 随机图片与AI点评
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/ai_false" // 服务器监控
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/aiwife" // 随机老婆
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/b14" // base16384加解密
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/baidu" // 百度一下
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/bilibili" // 查询b站用户信息
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/bilibili_parse" // b站视频链接解析
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/book_review" // 哀伤雪刃吧推书记录
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/cangtoushi" // 藏头诗
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/choose" // 选择困难症帮手
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/chouxianghua" // 说抽象话
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/coser" // 三次元小姐姐
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/cpstory" // cp短打
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/danbooru" // DeepDanbooru二次元图标签识别
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/diana" // 嘉心糖发病
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/drift_bottle" // 漂流瓶
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/emojimix" // 合成emoji
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/epidemic" // 城市疫情查询
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/font" // 渲染任意文字到图片
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/fortune" // 运势
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/funny" // 笑话
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/genshin" // 原神抽卡
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/gif" // 制图
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/github" // 搜索GitHub仓库
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/hs" // 炉石
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/hyaku" // 百人一首
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/image_finder" // 关键字搜图
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/inject" // 注入指令
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/jandan" // 煎蛋网无聊图
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/juejuezi" // 绝绝子生成器
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/lolicon" // lolicon 随机图片
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/moyu" // 摸鱼
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/moyu_calendar" // 摸鱼人日历
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/music" // 点歌
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/nativesetu" // 本地涩图
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/nativewife" // 本地老婆
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/nbnhhsh" // 拼音首字母缩写释义工具
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/novel" // 铅笔小说网搜索
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/nsfw" // nsfw图片识别
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/omikuji" // 浅草寺求签
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/reborn" // 投胎
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/runcode" // 在线运行代码
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/saucenao" // 以图搜图
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/scale" // 叔叔的AI二次元图片放大
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/score" // 分数
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/setutime" // 来份涩图
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/shadiao" // 沙雕app
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/shindan" // 测定
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/tiangou" // 舔狗日记
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/tracemoe" // 搜番
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/translation" // 翻译
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/vtb_quotation" // vtb语录
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/wangyiyun" // 网易云音乐热评
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/wordle" // 猜单词
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/ymgal" // 月幕galgame
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/zaobao" // 早报
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/custom" // 自定义插件合集
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/ahsai" // ahsai tts
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/aifalse" // 服务器监控
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/aiimage" // AI画图
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/aiwife" // 随机老婆
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/alipayvoice" // 支付宝到账语音
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/animetrace" // AnimeTrace 动画/Galgame识别
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/autowithdraw" // 触发者撤回时也自动撤回
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/baiduaudit" // 百度内容审核
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/base16384" // base16384加解密
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/base64gua" // base64卦加解密
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/baseamasiro" // base天城文加解密
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/bilibili" // b站相关
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/bilibiliparse" // b站相关
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/bilibilipush" // b站相关
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/bookreview" // 哀伤雪刃吧推书记录
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/chess" // 国际象棋
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/choose" // 选择困难症帮手
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/chouxianghua" // 说抽象话
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/chrev" // 英文字符翻转
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/coser" // 三次元小姐姐
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/cpstory" // cp短打
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/crypter" // 奇怪语言加解密
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/dailynews" // 今日早报
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/danbooru" // DeepDanbooru二次元图标签识别
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/diana" // 嘉心糖发病
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/dish" // 程序员做饭指南
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/drawlots" // 多功能抽签
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/driftbottle" // 漂流瓶
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/emojimix" // 合成emoji
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/emozi" // 颜文字抽象转写
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/event" // 好友申请群聊邀请事件处理
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/font" // 渲染任意文字到图片
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/fortune" // 运势
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/funny" // 笑话
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/genshin" // 原神抽卡
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/gif" // 制图
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/github" // 搜索GitHub仓库
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/guessmusic" // 猜歌
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/hitokoto" // 一言
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/hs" // 炉石
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/hyaku" // 百人一首
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/inject" // 注入指令
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/jandan" // 煎蛋网无聊图
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/jptingroom" // 日语听力学习材料
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/kfccrazythursday" // 疯狂星期四
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/lolicon" // lolicon 随机图片
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/lolimi" // 桑帛云 API
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/magicprompt" // magicprompt吟唱提示
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/mcfish" // 钓鱼模拟器
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/midicreate" // 简易midi音乐制作
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/minecraftobserver" // Minecraft服务器监控&订阅
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/movies" // 电影插件
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/moyu" // 摸鱼
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/moyucalendar" // 摸鱼人日历
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/music" // 点歌
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/nativesetu" // 本地涩图
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/nbnhhsh" // 拼音首字母缩写释义工具
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/nihongo" // 日语语法学习
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/niuniu" // 牛牛大作战
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/novel" // 铅笔小说网搜索
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/nsfw" // nsfw图片识别
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/nwife" // 本地老婆
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/omikuji" // 浅草寺求签
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/poker" // 抽扑克
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/qqwife" // 一群一天一夫一妻制群老婆
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/qzone" // qq空间表白墙
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/realcugan" // realcugan清晰术
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/reborn" // 投胎
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/robbery" // 打劫群友的ATRI币
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/rsshub" // RSSHub订阅姬
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/runcode" // 在线运行代码
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/saucenao" // 以图搜图
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/score" // 分数
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/setutime" // 来份涩图
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/shadiao" // 沙雕app
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/shindan" // 测定
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/steam" // steam相关
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/tarot" // 抽塔罗牌
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/tiangou" // 舔狗日记
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/tracemoe" // 搜番
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/translation" // 翻译
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/wallet" // 钱包
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/wantquotes" // 据意查句
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/warframeapi" // warframeAPI插件
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/wife" // 抽老婆
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/wordcount" // 聊天热词
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/wordle" // 猜单词
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/ygocdb" // 游戏王白鸽API卡查
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/ygotrade" // 游戏王集换社卡价查询
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/ymgal" // 月幕galgame
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/yujn" // 遇见API
|
||||
|
||||
// _ "github.com/FloatTech/ZeroBot-Plugin/plugin/wtf" // 鬼东西
|
||||
// _ "github.com/FloatTech/ZeroBot-Plugin/plugin/bilibili_push" // b站推送
|
||||
// _ "github.com/FloatTech/ZeroBot-Plugin/plugin/wtf" // 鬼东西
|
||||
|
||||
// ^^^^ //
|
||||
// ^^^^^^^^^^^^^^ //
|
||||
@@ -133,9 +179,13 @@ import (
|
||||
// vvvvvvvvvvvvvv //
|
||||
// vvvv //
|
||||
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/aichat" // AI聊天
|
||||
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/curse" // 骂人
|
||||
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/ai_reply" // 人工智能回复
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/thesaurus" // 词典匹配回复
|
||||
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/breakrepeat" // 打断复读
|
||||
|
||||
// ^^^^ //
|
||||
// ^^^^^^^^^^^^^^ //
|
||||
@@ -149,77 +199,138 @@ import (
|
||||
// //
|
||||
// //
|
||||
// -----------------------以下为内置依赖,勿动------------------------ //
|
||||
"github.com/FloatTech/zbputils/process"
|
||||
"github.com/FloatTech/floatbox/file"
|
||||
"github.com/FloatTech/floatbox/process"
|
||||
"github.com/sirupsen/logrus"
|
||||
zero "github.com/wdvxdr1123/ZeroBot"
|
||||
"github.com/wdvxdr1123/ZeroBot/driver"
|
||||
"github.com/wdvxdr1123/ZeroBot/message"
|
||||
|
||||
// webctrl "github.com/FloatTech/zbputils/control/web"
|
||||
|
||||
"github.com/FloatTech/ZeroBot-Plugin/kanban/banner"
|
||||
// -----------------------以上为内置依赖,勿动------------------------ //
|
||||
)
|
||||
|
||||
var (
|
||||
nicks = []string{"ATRI", "atri", "亚托莉", "アトリ"}
|
||||
token *string
|
||||
url *string
|
||||
adana *string
|
||||
prefix *string
|
||||
)
|
||||
type zbpcfg struct {
|
||||
Z zero.Config `json:"zero"`
|
||||
W []*driver.WSClient `json:"ws"`
|
||||
S []*driver.WSServer `json:"wss"`
|
||||
}
|
||||
|
||||
var config zbpcfg
|
||||
|
||||
func init() {
|
||||
sus := make([]int64, 0, 16)
|
||||
// 解析命令行参数
|
||||
d := flag.Bool("d", false, "Enable debug level log and higher.")
|
||||
w := flag.Bool("w", false, "Enable warning level log and higher.")
|
||||
h := flag.Bool("h", false, "Display this help.")
|
||||
// 解析命令行参数,输入 `-g 监听地址:端口` 指定 gui 访问地址,默认 127.0.0.1:3000
|
||||
// g := flag.String("g", "127.0.0.1:3000", "Set web gui listening address.")
|
||||
|
||||
// g := flag.String("g", "127.0.0.1:3000", "Set webui url.")
|
||||
// 直接写死 AccessToken 时,请更改下面第二个参数
|
||||
token = flag.String("t", "", "Set AccessToken of WSClient.")
|
||||
token := flag.String("t", "", "Set AccessToken of WSClient.")
|
||||
// 直接写死 URL 时,请更改下面第二个参数
|
||||
url = flag.String("u", "ws://127.0.0.1:6700", "Set Url of WSClient.")
|
||||
url := flag.String("u", "ws://127.0.0.1:6700", "Set Url of WSClient.")
|
||||
// 默认昵称
|
||||
adana = flag.String("n", "椛椛", "Set default nickname.")
|
||||
prefix = flag.String("p", "/", "Set command prefix.")
|
||||
adana := flag.String("n", "椛椛", "Set default nickname.")
|
||||
prefix := flag.String("p", "/", "Set command prefix.")
|
||||
runcfg := flag.String("c", "", "Run from config file.")
|
||||
save := flag.String("s", "", "Save default config to file and exit.")
|
||||
late := flag.Uint("l", 233, "Response latency (ms).")
|
||||
rsz := flag.Uint("r", 4096, "Receiving buffer ring size.")
|
||||
maxpt := flag.Uint("x", 4, "Max process time (min).")
|
||||
markmsg := flag.Bool("m", false, "Don't mark message as read automatically")
|
||||
flag.BoolVar(&file.SkipOriginal, "mirror", false, "Use mirrored lazy data at first")
|
||||
|
||||
flag.Parse()
|
||||
|
||||
if *h {
|
||||
kanban.PrintBanner()
|
||||
fmt.Println("Usage:")
|
||||
flag.PrintDefaults()
|
||||
os.Exit(0)
|
||||
} else {
|
||||
if *d && !*w {
|
||||
logrus.SetLevel(logrus.DebugLevel)
|
||||
}
|
||||
if *w {
|
||||
logrus.SetLevel(logrus.WarnLevel)
|
||||
}
|
||||
}
|
||||
if *d && !*w {
|
||||
logrus.SetLevel(logrus.DebugLevel)
|
||||
}
|
||||
if *w {
|
||||
logrus.SetLevel(logrus.WarnLevel)
|
||||
}
|
||||
|
||||
// 启用 gui
|
||||
// webctrl.InitGui(*g)
|
||||
for _, s := range flag.Args() {
|
||||
i, err := strconv.ParseInt(s, 10, 64)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
sus = append(sus, i)
|
||||
}
|
||||
|
||||
// 通过代码写死的方式添加主人账号
|
||||
// sus = append(sus, 12345678)
|
||||
// sus = append(sus, 87654321)
|
||||
|
||||
// 启用 webui
|
||||
// go webctrl.RunGui(*g)
|
||||
|
||||
if *runcfg != "" {
|
||||
f, err := os.Open(*runcfg)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
config.W = make([]*driver.WSClient, 0, 2)
|
||||
err = json.NewDecoder(f).Decode(&config)
|
||||
f.Close()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
config.Z.Driver = make([]zero.Driver, len(config.W)+len(config.S))
|
||||
for i, w := range config.W {
|
||||
config.Z.Driver[i] = w
|
||||
}
|
||||
for i, s := range config.S {
|
||||
config.Z.Driver[i+len(config.W)] = s
|
||||
}
|
||||
logrus.Infoln("[main] 从", *runcfg, "读取配置文件")
|
||||
return
|
||||
}
|
||||
config.W = []*driver.WSClient{driver.NewWebSocketClient(*url, *token)}
|
||||
config.Z = zero.Config{
|
||||
NickName: append([]string{*adana}, "ATRI", "atri", "亚托莉", "アトリ"),
|
||||
CommandPrefix: *prefix,
|
||||
SuperUsers: sus,
|
||||
RingLen: *rsz,
|
||||
Latency: time.Duration(*late) * time.Millisecond,
|
||||
MaxProcessTime: time.Duration(*maxpt) * time.Minute,
|
||||
MarkMessage: !*markmsg,
|
||||
Driver: []zero.Driver{config.W[0]},
|
||||
}
|
||||
|
||||
if *save != "" {
|
||||
f, err := os.Create(*save)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
err = json.NewEncoder(f).Encode(&config)
|
||||
f.Close()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
logrus.Infoln("[main] 配置文件已保存到", *save)
|
||||
os.Exit(0)
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
rand.Seed(time.Now().UnixNano()) // 全局 seed,其他插件无需再 seed
|
||||
if !strings.Contains(runtime.Version(), "go1.2") { // go1.20之前版本需要全局 seed,其他插件无需再 seed
|
||||
rand.Seed(time.Now().UnixNano()) //nolint: staticcheck
|
||||
}
|
||||
// 帮助
|
||||
zero.OnFullMatchGroup([]string{"/help", ".help", "菜单"}, zero.OnlyToMe).SetBlock(true).
|
||||
zero.OnFullMatchGroup([]string{"help", "/help", ".help", "菜单"}, zero.OnlyToMe).SetBlock(true).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
ctx.SendChain(message.Text(kanban.Banner, "\n可发送\"/服务列表\"查看 bot 功能"))
|
||||
ctx.SendChain(message.Text(banner.Banner, "\n管理发送\"/服务列表\"查看 bot 功能\n发送\"/用法name\"查看功能用法"))
|
||||
})
|
||||
zero.OnFullMatch("查看zbp公告", zero.OnlyToMe, zero.AdminPermission).SetBlock(true).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
ctx.SendChain(message.Text(kanban.Kanban()))
|
||||
ctx.SendChain(message.Text(strings.ReplaceAll(kanban.Kanban(), "\t", "")))
|
||||
})
|
||||
zero.RunAndBlock(
|
||||
zero.Config{
|
||||
NickName: append([]string{*adana}, nicks...),
|
||||
CommandPrefix: *prefix,
|
||||
// SuperUsers 某些功能需要主人权限,可通过以下两种方式修改
|
||||
// SuperUsers: []string{"12345678", "87654321"}, // 通过代码写死的方式添加主人账号
|
||||
SuperUsers: flag.Args(), // 通过命令行参数的方式添加主人账号
|
||||
Driver: []zero.Driver{driver.NewWebSocketClient(*url, *token)},
|
||||
},
|
||||
process.GlobalInitMutex.Unlock,
|
||||
)
|
||||
zero.RunAndBlock(&config.Z, process.GlobalInitMutex.Unlock)
|
||||
}
|
||||
|
||||
70
main_win.go
70
main_win.go
@@ -1,70 +0,0 @@
|
||||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"strings"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func init() {
|
||||
// windows 带颜色 log 自定义格式
|
||||
logrus.SetFormatter(&LogFormat{})
|
||||
}
|
||||
|
||||
const (
|
||||
colorCodePanic = "\x1b[1;31m" // color.Style{color.Bold, color.Red}.String()
|
||||
colorCodeFatal = "\x1b[1;31m" // color.Style{color.Bold, color.Red}.String()
|
||||
colorCodeError = "\x1b[31m" // color.Style{color.Red}.String()
|
||||
colorCodeWarn = "\x1b[33m" // color.Style{color.Yellow}.String()
|
||||
colorCodeInfo = "\x1b[37m" // color.Style{color.White}.String()
|
||||
colorCodeDebug = "\x1b[32m" // color.Style{color.Green}.String()
|
||||
colorCodeTrace = "\x1b[36m" // color.Style{color.Cyan}.String()
|
||||
colorReset = "\x1b[0m"
|
||||
)
|
||||
|
||||
// LogFormat specialize for zbp
|
||||
type LogFormat struct{}
|
||||
|
||||
// Format implements logrus.Formatter
|
||||
func (f LogFormat) Format(entry *logrus.Entry) ([]byte, error) {
|
||||
buf := new(bytes.Buffer)
|
||||
|
||||
buf.WriteString(getLogLevelColorCode(entry.Level))
|
||||
|
||||
buf.WriteByte('[')
|
||||
buf.WriteString(strings.ToUpper(entry.Level.String()))
|
||||
buf.WriteString("] ")
|
||||
buf.WriteString(entry.Message)
|
||||
buf.WriteString(" \n")
|
||||
|
||||
buf.WriteString(colorReset)
|
||||
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
// getLogLevelColorCode 获取日志等级对应色彩code
|
||||
func getLogLevelColorCode(level logrus.Level) string {
|
||||
switch level {
|
||||
case logrus.PanicLevel:
|
||||
return colorCodePanic
|
||||
case logrus.FatalLevel:
|
||||
return colorCodeFatal
|
||||
case logrus.ErrorLevel:
|
||||
return colorCodeError
|
||||
case logrus.WarnLevel:
|
||||
return colorCodeWarn
|
||||
case logrus.InfoLevel:
|
||||
return colorCodeInfo
|
||||
case logrus.DebugLevel:
|
||||
return colorCodeDebug
|
||||
case logrus.TraceLevel:
|
||||
return colorCodeTrace
|
||||
|
||||
default:
|
||||
return colorCodeInfo
|
||||
}
|
||||
}
|
||||
@@ -1,173 +0,0 @@
|
||||
// Package acgimage 随机图片与AI点评
|
||||
package acgimage
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
zero "github.com/wdvxdr1123/ZeroBot"
|
||||
"github.com/wdvxdr1123/ZeroBot/message"
|
||||
|
||||
"github.com/FloatTech/AnimeAPI/classify"
|
||||
|
||||
"github.com/FloatTech/zbputils/control"
|
||||
"github.com/FloatTech/zbputils/control/order"
|
||||
"github.com/FloatTech/zbputils/ctxext"
|
||||
"github.com/FloatTech/zbputils/img/pool"
|
||||
"github.com/FloatTech/zbputils/web"
|
||||
)
|
||||
|
||||
const (
|
||||
lolipxy = "https://sayuri.fumiama.top/dice?class=0&loli=true&r18=true"
|
||||
apihead = "https://sayuri.fumiama.top/img?path="
|
||||
apiheadv6 = "http://aikae.v6.army:62002/img?arg=get&name="
|
||||
)
|
||||
|
||||
var (
|
||||
// r18有一定保护,一般不会发出图片
|
||||
randapi = "&loli=true&r18=true"
|
||||
msgof = make(map[int64]message.MessageID)
|
||||
block = false
|
||||
)
|
||||
|
||||
func init() { // 插件主体
|
||||
engine := control.Register("acgimage", order.AcquirePrio(), &control.Options{
|
||||
DisableOnDefault: false,
|
||||
Help: "随机图片与AI点评\n" +
|
||||
"- 随机图片(评级大于6的图将私发)\n" +
|
||||
"- 直接随机(无r18检测,务必小心,仅管理可用)\n" +
|
||||
"- 设置随机图片网址[url]\n" +
|
||||
"- 太涩了(撤回最近发的图)\n" +
|
||||
"- 评价图片(发送一张图片让bot评分)",
|
||||
}).ApplySingle(ctxext.DefaultSingle)
|
||||
engine.OnRegex(`^设置随机图片网址(.*)$`, zero.OnlyPrivate, zero.SuperUserPermission).SetBlock(true).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
url := ctx.State["regex_matched"].([]string)[1]
|
||||
if !strings.HasPrefix(url, "http") {
|
||||
ctx.SendChain(message.Text("URL非法!"))
|
||||
} else {
|
||||
randapi = url
|
||||
ctx.SendChain(message.Text("设置好啦"))
|
||||
}
|
||||
})
|
||||
// 有保护的随机图片
|
||||
engine.OnFullMatch("随机图片", zero.OnlyGroup).Limit(ctxext.LimitByUser).SetBlock(true).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
class, dhash, _, err := classify.Classify(randapi, true)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR:", err))
|
||||
return
|
||||
}
|
||||
err = reply(ctx, class, dhash, classify.Comments[class])
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR:", err))
|
||||
return
|
||||
}
|
||||
})
|
||||
// 直接随机图片,无r18保护,后果自负。如果出r18图可尽快通过发送"太涩了"撤回
|
||||
engine.OnFullMatch("直接随机", zero.UserOrGrpAdmin).SetBlock(true).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
if block {
|
||||
ctx.SendChain(message.Text("请稍后再试哦"))
|
||||
} else if randapi != "" {
|
||||
block = true
|
||||
var url string
|
||||
if randapi[0] == '&' {
|
||||
url = lolipxy
|
||||
} else {
|
||||
url = randapi
|
||||
}
|
||||
setLastMsg(ctx.Event.GroupID, message.NewMessageID(
|
||||
ctx.SendGroupForwardMessage(ctx.Event.GroupID,
|
||||
message.Message{
|
||||
ctxext.FakeSenderForwardNode(ctx,
|
||||
message.Image(url).Add("cache", "0"),
|
||||
),
|
||||
}).Get("message_id").String()))
|
||||
block = false
|
||||
}
|
||||
})
|
||||
// 撤回最后的直接随机图片
|
||||
engine.OnFullMatch("太涩了", zero.OnlyGroup).SetBlock(true).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
msg, ok := msgof[ctx.Event.GroupID]
|
||||
if ok {
|
||||
ctx.DeleteMessage(msg)
|
||||
delete(msgof, ctx.Event.GroupID)
|
||||
}
|
||||
})
|
||||
// 上传一张图进行评价
|
||||
engine.OnKeywordGroup([]string{"评价图片"}, zero.OnlyGroup, zero.MustProvidePicture).SetBlock(true).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
ctx.SendChain(message.Text("少女祈祷中..."))
|
||||
url := ctx.State["image_url"].([]string)[0]
|
||||
class, _, _, err := classify.Classify(url, true)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR:", err))
|
||||
return
|
||||
}
|
||||
ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text(classify.Comments[class]))
|
||||
})
|
||||
engine.OnRegex(`^给你点提示哦:(.*)$`, zero.OnlyPrivate).SetBlock(true).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
dhash := ctx.State["regex_matched"].([]string)[1]
|
||||
if len(dhash) == 5*3 {
|
||||
var u string
|
||||
if web.IsSupportIPv6 {
|
||||
u = apiheadv6 + dhash + ".webp"
|
||||
} else {
|
||||
u = apihead + dhash
|
||||
}
|
||||
|
||||
err := pool.SendRemoteImageFromPool(dhash, u, ctxext.Send(ctx), ctxext.GetMessage(ctx))
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR:", err))
|
||||
return
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func setLastMsg(id int64, msg message.MessageID) {
|
||||
msgof[id] = msg
|
||||
}
|
||||
|
||||
func reply(ctx *zero.Ctx, class int, dhash string, comment string) error {
|
||||
b14, err := url.QueryUnescape(dhash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var u string
|
||||
if web.IsSupportIPv6 {
|
||||
u = apiheadv6 + dhash + ".webp"
|
||||
} else {
|
||||
u = apihead + dhash
|
||||
}
|
||||
|
||||
var send ctxext.NoCtxSendMsg
|
||||
if class > 5 {
|
||||
send = ctxext.SendTo(ctx, ctx.Event.UserID)
|
||||
if dhash != "" {
|
||||
ctx.SendChain(message.Text(comment + "\n给你点提示哦:" + b14))
|
||||
} else {
|
||||
ctx.SendChain(message.Text(comment))
|
||||
}
|
||||
} else {
|
||||
send = func(msg interface{}) int64 {
|
||||
return ctx.SendGroupForwardMessage(ctx.Event.GroupID, message.Message{
|
||||
ctxext.FakeSenderForwardNode(ctx, append(
|
||||
msg.(message.Message),
|
||||
message.Text(comment))...,
|
||||
),
|
||||
}).Get("message_id").Int()
|
||||
}
|
||||
}
|
||||
|
||||
return pool.SendRemoteImageFromPool(b14, u, send, func(i int64) zero.Message {
|
||||
if class > 5 {
|
||||
return ctxext.GetMessage(ctx)(i)
|
||||
}
|
||||
return ctxext.GetFirstMessageInForward(ctx)(i)
|
||||
})
|
||||
}
|
||||
97
plugin/ahsai/ahsai.go
Normal file
97
plugin/ahsai/ahsai.go
Normal file
@@ -0,0 +1,97 @@
|
||||
// Package ahsai AH Soft フリーテキスト音声合成 demo API
|
||||
package ahsai
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"os"
|
||||
"sort"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/FloatTech/floatbox/file"
|
||||
ctrl "github.com/FloatTech/zbpctrl"
|
||||
"github.com/FloatTech/zbputils/control"
|
||||
ahsaitts "github.com/fumiama/ahsai"
|
||||
zero "github.com/wdvxdr1123/ZeroBot"
|
||||
"github.com/wdvxdr1123/ZeroBot/message"
|
||||
)
|
||||
|
||||
var (
|
||||
namelist = [...]string{"伊織弓鶴", "紲星あかり", "結月ゆかり", "京町セイカ", "東北きりたん", "東北イタコ", "ついなちゃん標準語", "ついなちゃん関西弁", "音街ウナ", "琴葉茜", "吉田くん", "民安ともえ", "桜乃そら", "月読アイ", "琴葉葵", "東北ずん子", "月読ショウタ", "水奈瀬コウ"}
|
||||
namesort = func() []string {
|
||||
nl := namelist[:]
|
||||
sort.Strings(nl)
|
||||
return nl
|
||||
}()
|
||||
)
|
||||
|
||||
func init() {
|
||||
engine := control.AutoRegister(&ctrl.Options[*zero.Ctx]{
|
||||
DisableOnDefault: false,
|
||||
Brief: "フリーテキスト音声合成",
|
||||
Help: "- 使[伊織弓鶴|紲星あかり|結月ゆかり|京町セイカ|東北きりたん|東北イタコ|ついなちゃん標準語|ついなちゃん関西弁|音街ウナ|琴葉茜|吉田くん|民安ともえ|桜乃そら|月読アイ|琴葉葵|東北ずん子|月読ショウタ|水奈瀬コウ]说(日语)",
|
||||
PrivateDataFolder: "ahsai",
|
||||
})
|
||||
cachePath := engine.DataFolder() + "cache/"
|
||||
_ = os.RemoveAll(cachePath)
|
||||
_ = os.MkdirAll(cachePath, 0755)
|
||||
engine.OnRegex("^使(.{0,10})说([A-Za-z\\s\\d\u3005\u3040-\u30ff\u4e00-\u9fff\uff11-\uff19\uff21-\uff3a\uff41-\uff5a\uff66-\uff9d\\pP]+)$", selectName).SetBlock(true).Handle(func(ctx *zero.Ctx) {
|
||||
ctx.SendChain(message.Text("少女祈祷中..."))
|
||||
uid := ctx.Event.UserID
|
||||
today := time.Now().Format("20060102150405")
|
||||
ahsaiFile := cachePath + strconv.FormatInt(uid, 10) + today + "ahsai.wav"
|
||||
s := ahsaitts.NewSpeaker()
|
||||
err := s.SetName(ctx.State["ahsainame"].(string))
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return
|
||||
}
|
||||
u, err := s.Speak(ctx.State["ahsaitext"].(string))
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return
|
||||
}
|
||||
err = ahsaitts.SaveOggToFile(u, ahsaiFile)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return
|
||||
}
|
||||
ctx.SendChain(message.Record("file:///" + file.BOTPATH + "/" + ahsaiFile))
|
||||
})
|
||||
}
|
||||
|
||||
func selectName(ctx *zero.Ctx) bool {
|
||||
regexMatched := ctx.State["regex_matched"].([]string)
|
||||
ctx.State["ahsaitext"] = regexMatched[2]
|
||||
name := regexMatched[1]
|
||||
index := sort.SearchStrings(namesort, name)
|
||||
if index < len(namelist) && namesort[index] == name {
|
||||
ctx.State["ahsainame"] = name
|
||||
return true
|
||||
}
|
||||
speaktext := ""
|
||||
for i, v := range namelist {
|
||||
speaktext += fmt.Sprintf("%d. %s\n", i, v)
|
||||
}
|
||||
ctx.SendChain(message.Text("输入的音源为空, 请输入音源序号\n", speaktext))
|
||||
next, cancel := zero.NewFutureEvent("message", 999, false, ctx.CheckSession(), zero.RegexRule(`\d{0,2}`)).Repeat()
|
||||
defer cancel()
|
||||
for {
|
||||
select {
|
||||
case <-time.After(time.Second * 10):
|
||||
ctx.State["ahsainame"] = namelist[rand.Intn(len(namelist))]
|
||||
ctx.SendChain(message.Text("时间太久啦!", zero.BotConfig.NickName[0], "帮你选择", ctx.State["ahsainame"]))
|
||||
return true
|
||||
case c := <-next:
|
||||
msg := c.Event.Message.ExtractPlainText()
|
||||
num, _ := strconv.Atoi(msg)
|
||||
if num < 0 || num >= len(namelist) {
|
||||
ctx.SendChain(message.Text("序号非法!"))
|
||||
continue
|
||||
}
|
||||
ctx.State["ahsainame"] = namelist[num]
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,81 +0,0 @@
|
||||
// Package aifalse 暂时只有服务器监控
|
||||
package aifalse
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
control "github.com/FloatTech/zbputils/control"
|
||||
"github.com/shirou/gopsutil/v3/cpu"
|
||||
"github.com/shirou/gopsutil/v3/disk"
|
||||
"github.com/shirou/gopsutil/v3/mem"
|
||||
|
||||
"github.com/FloatTech/zbputils/control/order"
|
||||
|
||||
zero "github.com/wdvxdr1123/ZeroBot"
|
||||
"github.com/wdvxdr1123/ZeroBot/message"
|
||||
)
|
||||
|
||||
func init() { // 插件主体
|
||||
engine := control.Register("aifalse", order.AcquirePrio(), &control.Options{
|
||||
DisableOnDefault: false,
|
||||
Help: "AIfalse\n" +
|
||||
"- 查询计算机当前活跃度: [检查身体 | 自检 | 启动自检 | 系统状态]",
|
||||
})
|
||||
engine.OnFullMatchGroup([]string{"检查身体", "自检", "启动自检", "系统状态"}, zero.AdminPermission).SetBlock(true).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
ctx.SendChain(message.Text(
|
||||
"* CPU占用: ", cpuPercent(), "%\n",
|
||||
"* RAM占用: ", memPercent(), "%\n",
|
||||
"* 硬盘使用: ", diskPercent(),
|
||||
),
|
||||
)
|
||||
})
|
||||
engine.OnFullMatch("清理缓存", zero.SuperUserPermission).SetBlock(true).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
err := os.RemoveAll("data/cache/*")
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("错误: ", err.Error()))
|
||||
} else {
|
||||
ctx.SendChain(message.Text("成功!"))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func cpuPercent() float64 {
|
||||
percent, err := cpu.Percent(time.Second, false)
|
||||
if err != nil {
|
||||
return -1
|
||||
}
|
||||
return math.Round(percent[0])
|
||||
}
|
||||
|
||||
func memPercent() float64 {
|
||||
memInfo, err := mem.VirtualMemory()
|
||||
if err != nil {
|
||||
return -1
|
||||
}
|
||||
return math.Round(memInfo.UsedPercent)
|
||||
}
|
||||
|
||||
func diskPercent() string {
|
||||
parts, err := disk.Partitions(true)
|
||||
if err != nil {
|
||||
return err.Error()
|
||||
}
|
||||
msg := ""
|
||||
for _, p := range parts {
|
||||
diskInfo, err := disk.Usage(p.Mountpoint)
|
||||
if err != nil {
|
||||
msg += "\n - " + err.Error()
|
||||
continue
|
||||
}
|
||||
pc := uint(math.Round(diskInfo.UsedPercent))
|
||||
if pc > 0 {
|
||||
msg += fmt.Sprintf("\n - %s(%dM) %d%%", p.Mountpoint, diskInfo.Total/1024/1024, pc)
|
||||
}
|
||||
}
|
||||
return msg
|
||||
}
|
||||
@@ -1,171 +0,0 @@
|
||||
package aireply
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"sync"
|
||||
|
||||
"github.com/pkumza/numcn"
|
||||
log "github.com/sirupsen/logrus"
|
||||
zero "github.com/wdvxdr1123/ZeroBot"
|
||||
"github.com/wdvxdr1123/ZeroBot/message"
|
||||
|
||||
"github.com/FloatTech/AnimeAPI/aireply"
|
||||
"github.com/FloatTech/AnimeAPI/tts"
|
||||
"github.com/FloatTech/AnimeAPI/tts/baidutts"
|
||||
"github.com/FloatTech/AnimeAPI/tts/mockingbird"
|
||||
"github.com/FloatTech/zbputils/control"
|
||||
"github.com/FloatTech/zbputils/ctxext"
|
||||
|
||||
"github.com/FloatTech/zbputils/control/order"
|
||||
)
|
||||
|
||||
const ttsServiceName = "tts"
|
||||
|
||||
var re = regexp.MustCompile(`(\-|\+)?\d+(\.\d+)?`)
|
||||
|
||||
type ttsInstances struct {
|
||||
sync.RWMutex
|
||||
m map[string]tts.TTS
|
||||
l []string
|
||||
}
|
||||
|
||||
func (t *ttsInstances) List() []string {
|
||||
t.RLock()
|
||||
cl := make([]string, len(t.l))
|
||||
_ = copy(cl, t.l)
|
||||
t.RUnlock()
|
||||
return cl
|
||||
}
|
||||
|
||||
func init() {
|
||||
t := &ttsInstances{
|
||||
m: map[string]tts.TTS{
|
||||
"百度女声": baidutts.NewBaiduTTS(0),
|
||||
"百度男声": baidutts.NewBaiduTTS(1),
|
||||
"百度度逍遥": baidutts.NewBaiduTTS(3),
|
||||
"百度度丫丫": baidutts.NewBaiduTTS(4),
|
||||
"拟声鸟阿梓": nil,
|
||||
"拟声鸟药水哥": nil,
|
||||
},
|
||||
l: []string{"拟声鸟阿梓", "拟声鸟药水哥", "百度女声", "百度男声", "百度度逍遥", "百度度丫丫"},
|
||||
}
|
||||
engine := control.Register(ttsServiceName, order.AcquirePrio(), &control.Options{
|
||||
DisableOnDefault: true,
|
||||
Help: "语音回复(包括拟声鸟和百度)\n" +
|
||||
"- @Bot 任意文本(任意一句话回复)\n" +
|
||||
"- 设置语音模式[拟声鸟阿梓 | 拟声鸟药水哥 | 百度女声 | 百度男声| 百度度逍遥 | 百度度丫丫]\n" +
|
||||
"- 设置默认语音模式[拟声鸟阿梓 | 拟声鸟药水哥 | 百度女声 | 百度男声| 百度度逍遥 | 百度度丫丫]\n",
|
||||
})
|
||||
engine.OnMessage(zero.OnlyToMe).SetBlock(true).Limit(ctxext.LimitByUser).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
msg := ctx.ExtractPlainText()
|
||||
r := aireply.NewAIReply(getReplyMode(ctx))
|
||||
tts := t.new(t.getSoundMode(ctx))
|
||||
if tts != nil {
|
||||
ctx.SendChain(message.Record(tts.Speak(ctx.Event.UserID, func() string {
|
||||
reply := r.TalkPlain(msg, zero.BotConfig.NickName[0])
|
||||
reply = re.ReplaceAllStringFunc(reply, func(s string) string {
|
||||
f, err := strconv.ParseFloat(s, 64)
|
||||
if err != nil {
|
||||
log.Errorln("[tts]:", err)
|
||||
return s
|
||||
}
|
||||
return numcn.EncodeFromFloat64(f)
|
||||
})
|
||||
log.Println("[tts]:", reply)
|
||||
return reply
|
||||
})))
|
||||
}
|
||||
})
|
||||
engine.OnRegex(`^设置语音模式(.*)$`, ctxext.FirstValueInList(t)).SetBlock(true).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
param := ctx.State["regex_matched"].([]string)[1]
|
||||
err := t.setSoundMode(ctx, param)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text(err))
|
||||
return
|
||||
}
|
||||
ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("设置成功,当前模式为", param))
|
||||
})
|
||||
engine.OnRegex(`^设置默认语音模式(.*)$`, ctxext.FirstValueInList(t)).SetBlock(true).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
param := ctx.State["regex_matched"].([]string)[1]
|
||||
t.setDefaultSoundMode(param)
|
||||
ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("设置成功,默认模式为", param))
|
||||
})
|
||||
}
|
||||
|
||||
// new 语音简单工厂
|
||||
func (t *ttsInstances) new(name string) (ts tts.TTS) {
|
||||
t.RLock()
|
||||
ts = t.m[name]
|
||||
t.RUnlock()
|
||||
if ts == nil {
|
||||
switch name {
|
||||
case "拟声鸟阿梓":
|
||||
t.Lock()
|
||||
ts, _ = mockingbird.NewMockingBirdTTS(0)
|
||||
t.Unlock()
|
||||
case "拟声鸟药水哥":
|
||||
t.Lock()
|
||||
ts, _ = mockingbird.NewMockingBirdTTS(1)
|
||||
t.Unlock()
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (t *ttsInstances) setSoundMode(ctx *zero.Ctx, name string) error {
|
||||
gid := ctx.Event.GroupID
|
||||
if gid == 0 {
|
||||
gid = -ctx.Event.UserID
|
||||
}
|
||||
var index int64
|
||||
t.RLock()
|
||||
for i, s := range t.l {
|
||||
if s == name {
|
||||
index = int64(i)
|
||||
break
|
||||
}
|
||||
}
|
||||
t.RUnlock()
|
||||
m, ok := control.Lookup(ttsServiceName)
|
||||
if !ok {
|
||||
return errors.New("no such plugin")
|
||||
}
|
||||
return m.SetData(gid, index)
|
||||
}
|
||||
|
||||
func (t *ttsInstances) getSoundMode(ctx *zero.Ctx) (name string) {
|
||||
gid := ctx.Event.GroupID
|
||||
if gid == 0 {
|
||||
gid = -ctx.Event.UserID
|
||||
}
|
||||
m, ok := control.Lookup(ttsServiceName)
|
||||
if ok {
|
||||
t.RLock()
|
||||
defer t.RUnlock()
|
||||
index := m.GetData(gid)
|
||||
if int(index) < len(t.l) {
|
||||
return t.l[index]
|
||||
}
|
||||
}
|
||||
return "拟声鸟阿梓"
|
||||
}
|
||||
|
||||
func (t *ttsInstances) setDefaultSoundMode(name string) {
|
||||
var index int
|
||||
t.RLock()
|
||||
for _, s := range t.l {
|
||||
if s == name {
|
||||
break
|
||||
}
|
||||
index++
|
||||
}
|
||||
t.RUnlock()
|
||||
t.Lock()
|
||||
t.l[0], t.l[index] = t.l[index], t.l[0]
|
||||
t.Unlock()
|
||||
}
|
||||
@@ -1,92 +0,0 @@
|
||||
// Package aireply AI 回复
|
||||
package aireply
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/FloatTech/AnimeAPI/aireply"
|
||||
control "github.com/FloatTech/zbputils/control"
|
||||
"github.com/FloatTech/zbputils/ctxext"
|
||||
zero "github.com/wdvxdr1123/ZeroBot"
|
||||
"github.com/wdvxdr1123/ZeroBot/message"
|
||||
|
||||
"github.com/FloatTech/zbputils/control/order"
|
||||
)
|
||||
|
||||
const (
|
||||
replyServiceName = "aireply"
|
||||
)
|
||||
|
||||
var replyModes = [...]string{"青云客", "小爱"}
|
||||
|
||||
func init() { // 插件主体
|
||||
engine := control.Register(replyServiceName, order.AcquirePrio(), &control.Options{
|
||||
DisableOnDefault: false,
|
||||
Help: "人工智能回复\n" +
|
||||
"- @Bot 任意文本(任意一句话回复)\n- 设置回复模式[青云客 | 小爱]\n- ",
|
||||
})
|
||||
// 回复 @和包括名字
|
||||
engine.OnMessage(zero.OnlyToMe).SetBlock(true).Limit(ctxext.LimitByUser).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
aireply := aireply.NewAIReply(getReplyMode(ctx))
|
||||
reply := message.ParseMessageFromString(aireply.Talk(ctx.ExtractPlainText(), zero.BotConfig.NickName[0]))
|
||||
// 回复
|
||||
time.Sleep(time.Second * 1)
|
||||
if zero.OnlyPublic(ctx) {
|
||||
reply = append(reply, message.Reply(ctx.Event.MessageID))
|
||||
ctx.Send(reply)
|
||||
return
|
||||
}
|
||||
ctx.Send(reply)
|
||||
})
|
||||
engine.OnPrefix(`设置回复模式`).SetBlock(true).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
param := ctx.State["args"].(string)
|
||||
err := setReplyMode(ctx, param)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text(err))
|
||||
return
|
||||
}
|
||||
ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("成功"))
|
||||
})
|
||||
}
|
||||
|
||||
func setReplyMode(ctx *zero.Ctx, name string) error {
|
||||
gid := ctx.Event.GroupID
|
||||
if gid == 0 {
|
||||
gid = -ctx.Event.UserID
|
||||
}
|
||||
var ok bool
|
||||
var index int64
|
||||
for i, s := range replyModes {
|
||||
if s == name {
|
||||
ok = true
|
||||
index = int64(i)
|
||||
break
|
||||
}
|
||||
}
|
||||
if !ok {
|
||||
return errors.New("no such mode")
|
||||
}
|
||||
m, ok := control.Lookup(replyServiceName)
|
||||
if !ok {
|
||||
return errors.New("no such plugin")
|
||||
}
|
||||
return m.SetData(gid, index)
|
||||
}
|
||||
|
||||
func getReplyMode(ctx *zero.Ctx) (name string) {
|
||||
gid := ctx.Event.GroupID
|
||||
if gid == 0 {
|
||||
gid = -ctx.Event.UserID
|
||||
}
|
||||
m, ok := control.Lookup(replyServiceName)
|
||||
if ok {
|
||||
index := m.GetData(gid)
|
||||
if int(index) < len(replyModes) {
|
||||
return replyModes[index]
|
||||
}
|
||||
}
|
||||
return "青云客"
|
||||
}
|
||||
302
plugin/aichat/cfg.go
Normal file
302
plugin/aichat/cfg.go
Normal file
@@ -0,0 +1,302 @@
|
||||
package aichat
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
ctrl "github.com/FloatTech/zbpctrl"
|
||||
"github.com/FloatTech/zbputils/chat"
|
||||
"github.com/fumiama/deepinfra"
|
||||
"github.com/fumiama/deepinfra/model"
|
||||
"github.com/sirupsen/logrus"
|
||||
zero "github.com/wdvxdr1123/ZeroBot"
|
||||
"github.com/wdvxdr1123/ZeroBot/message"
|
||||
)
|
||||
|
||||
var (
|
||||
cfg = newconfig()
|
||||
)
|
||||
|
||||
var (
|
||||
apitypes = map[string]uint8{
|
||||
"OpenAI": 0,
|
||||
"OLLaMA": 1,
|
||||
"GenAI": 2,
|
||||
}
|
||||
apilist = [3]string{"OpenAI", "OLLaMA", "GenAI"}
|
||||
)
|
||||
|
||||
// ModelType 支持打印 string 并生产 protocal
|
||||
type ModelType int
|
||||
|
||||
func newModelType(typ string) (ModelType, error) {
|
||||
t, ok := apitypes[typ]
|
||||
if !ok {
|
||||
return 0, errors.New("未知类型 " + typ)
|
||||
}
|
||||
return ModelType(t), nil
|
||||
}
|
||||
|
||||
func (mt ModelType) String() string {
|
||||
return apilist[mt]
|
||||
}
|
||||
|
||||
func (mt ModelType) protocol(modn string, temp float32, topp float32, maxn uint) (mod model.Protocol, err error) {
|
||||
switch cfg.Type {
|
||||
case 0:
|
||||
mod = model.NewOpenAI(
|
||||
modn, cfg.Separator,
|
||||
temp, topp, maxn,
|
||||
)
|
||||
case 1:
|
||||
mod = model.NewOLLaMA(
|
||||
modn, cfg.Separator,
|
||||
temp, topp, maxn,
|
||||
)
|
||||
case 2:
|
||||
mod = model.NewGenAI(
|
||||
modn,
|
||||
temp, topp, maxn,
|
||||
)
|
||||
default:
|
||||
err = errors.New("unsupported model type " + strconv.Itoa(int(cfg.Type)))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// ModelBool 支持打印成 "是/否"
|
||||
type ModelBool bool
|
||||
|
||||
func (mb ModelBool) String() string {
|
||||
if mb {
|
||||
return "是"
|
||||
}
|
||||
return "否"
|
||||
}
|
||||
|
||||
// ModelKey 支持隐藏密钥
|
||||
type ModelKey string
|
||||
|
||||
func (mk ModelKey) String() string {
|
||||
if len(mk) == 0 {
|
||||
return "未设置"
|
||||
}
|
||||
if len(mk) <= 4 {
|
||||
return "****"
|
||||
}
|
||||
key := string(mk)
|
||||
return key[:2] + strings.Repeat("*", len(key)-4) + key[len(key)-2:]
|
||||
}
|
||||
|
||||
type config struct {
|
||||
ModelName string
|
||||
ImageModelName string
|
||||
AgentModelName string
|
||||
Type ModelType
|
||||
ImageType ModelType
|
||||
AgentType ModelType
|
||||
MaxN uint
|
||||
TopP float32
|
||||
SystemP string
|
||||
API string
|
||||
ImageAPI string
|
||||
AgentAPI string
|
||||
Key ModelKey
|
||||
ImageKey ModelKey
|
||||
AgentKey ModelKey
|
||||
Separator string
|
||||
NoSystemP ModelBool
|
||||
}
|
||||
|
||||
func newconfig() config {
|
||||
return config{
|
||||
ModelName: model.ModelDeepDeek,
|
||||
SystemP: chat.SystemPrompt,
|
||||
API: deepinfra.OpenAIDeepInfra,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *config) String() string {
|
||||
topp, maxn := c.mparams()
|
||||
sb := strings.Builder{}
|
||||
sb.WriteString(fmt.Sprintf("• 模型名:%s\n", c.ModelName))
|
||||
sb.WriteString(fmt.Sprintf("• 图像模型名:%s\n", c.ImageModelName))
|
||||
sb.WriteString(fmt.Sprintf("• Agent模型名:%s\n", c.AgentModelName))
|
||||
sb.WriteString(fmt.Sprintf("• 接口类型:%v\n", c.Type))
|
||||
sb.WriteString(fmt.Sprintf("• 图像接口类型:%v\n", c.ImageType))
|
||||
sb.WriteString(fmt.Sprintf("• Agent接口类型:%v\n", c.AgentType))
|
||||
sb.WriteString(fmt.Sprintf("• 最大长度:%d\n", maxn))
|
||||
sb.WriteString(fmt.Sprintf("• TopP:%.1f\n", topp))
|
||||
sb.WriteString(fmt.Sprintf("• 系统提示词:%s\n", c.SystemP))
|
||||
sb.WriteString(fmt.Sprintf("• 接口地址:%s\n", c.API))
|
||||
sb.WriteString(fmt.Sprintf("• 图像接口地址:%s\n", c.ImageAPI))
|
||||
sb.WriteString(fmt.Sprintf("• Agent接口地址:%s\n", c.AgentAPI))
|
||||
sb.WriteString(fmt.Sprintf("• 密钥:%v\n", c.Key))
|
||||
sb.WriteString(fmt.Sprintf("• 图像密钥:%v\n", c.ImageKey))
|
||||
sb.WriteString(fmt.Sprintf("• Agent密钥:%v\n", c.AgentKey))
|
||||
sb.WriteString(fmt.Sprintf("• 分隔符:%s\n", c.Separator))
|
||||
sb.WriteString(fmt.Sprintf("• 支持系统提示词:%v\n", !c.NoSystemP))
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
func (c *config) isvalid() bool {
|
||||
return c.ModelName != "" && c.API != "" && c.Key != ""
|
||||
}
|
||||
|
||||
// 获取全局模型参数:TopP和最大长度
|
||||
func (c *config) mparams() (topp float32, maxn uint) {
|
||||
// 处理TopP参数
|
||||
topp = c.TopP
|
||||
if topp == 0 {
|
||||
topp = 0.9
|
||||
}
|
||||
|
||||
// 处理最大长度参数
|
||||
maxn = c.MaxN
|
||||
if maxn == 0 {
|
||||
maxn = 4096
|
||||
}
|
||||
|
||||
return topp, maxn
|
||||
}
|
||||
|
||||
func ensureconfig(ctx *zero.Ctx) bool {
|
||||
c, ok := ctx.State["manager"].(*ctrl.Control[*zero.Ctx])
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
if !cfg.isvalid() {
|
||||
err := c.GetExtra(&cfg)
|
||||
if err != nil {
|
||||
logrus.Warnln("ERROR: get extra err:", err)
|
||||
}
|
||||
if !cfg.isvalid() {
|
||||
cfg = newconfig()
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func newextrasetstr[T ~string](ptr *T) func(ctx *zero.Ctx) {
|
||||
return func(ctx *zero.Ctx) {
|
||||
args := strings.TrimSpace(ctx.State["args"].(string))
|
||||
if args == "" {
|
||||
ctx.SendChain(message.Text("ERROR: empty args"))
|
||||
return
|
||||
}
|
||||
c, ok := ctx.State["manager"].(*ctrl.Control[*zero.Ctx])
|
||||
if !ok {
|
||||
ctx.SendChain(message.Text("ERROR: no such plugin"))
|
||||
return
|
||||
}
|
||||
*ptr = T(args)
|
||||
err := c.SetExtra(&cfg)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: set extra err: ", err))
|
||||
return
|
||||
}
|
||||
ctx.SendChain(message.Text("成功"))
|
||||
}
|
||||
}
|
||||
|
||||
func newextrasetbool[T ~bool](ptr *T) func(ctx *zero.Ctx) {
|
||||
return func(ctx *zero.Ctx) {
|
||||
args := ctx.State["regex_matched"].([]string)
|
||||
isno := args[1] == "不"
|
||||
c, ok := ctx.State["manager"].(*ctrl.Control[*zero.Ctx])
|
||||
if !ok {
|
||||
ctx.SendChain(message.Text("ERROR: no such plugin"))
|
||||
return
|
||||
}
|
||||
*ptr = T(isno)
|
||||
err := c.SetExtra(&cfg)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: set extra err: ", err))
|
||||
return
|
||||
}
|
||||
ctx.SendChain(message.Text("成功"))
|
||||
}
|
||||
}
|
||||
|
||||
func newextrasetuint(ptr *uint) func(ctx *zero.Ctx) {
|
||||
return func(ctx *zero.Ctx) {
|
||||
args := strings.TrimSpace(ctx.State["args"].(string))
|
||||
if args == "" {
|
||||
ctx.SendChain(message.Text("ERROR: empty args"))
|
||||
return
|
||||
}
|
||||
c, ok := ctx.State["manager"].(*ctrl.Control[*zero.Ctx])
|
||||
if !ok {
|
||||
ctx.SendChain(message.Text("ERROR: no such plugin"))
|
||||
return
|
||||
}
|
||||
n, err := strconv.ParseUint(args, 10, 64)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: parse args err: ", err))
|
||||
return
|
||||
}
|
||||
*ptr = uint(n)
|
||||
err = c.SetExtra(&cfg)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: set extra err: ", err))
|
||||
return
|
||||
}
|
||||
ctx.SendChain(message.Text("成功"))
|
||||
}
|
||||
}
|
||||
|
||||
func newextrasetfloat32(ptr *float32) func(ctx *zero.Ctx) {
|
||||
return func(ctx *zero.Ctx) {
|
||||
args := strings.TrimSpace(ctx.State["args"].(string))
|
||||
if args == "" {
|
||||
ctx.SendChain(message.Text("ERROR: empty args"))
|
||||
return
|
||||
}
|
||||
c, ok := ctx.State["manager"].(*ctrl.Control[*zero.Ctx])
|
||||
if !ok {
|
||||
ctx.SendChain(message.Text("ERROR: no such plugin"))
|
||||
return
|
||||
}
|
||||
n, err := strconv.ParseFloat(args, 32)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: parse args err: ", err))
|
||||
return
|
||||
}
|
||||
*ptr = float32(n)
|
||||
err = c.SetExtra(&cfg)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: set extra err: ", err))
|
||||
return
|
||||
}
|
||||
ctx.SendChain(message.Text("成功"))
|
||||
}
|
||||
}
|
||||
|
||||
func newextrasetmodeltype(ptr *ModelType) func(ctx *zero.Ctx) {
|
||||
return func(ctx *zero.Ctx) {
|
||||
args := strings.TrimSpace(ctx.State["args"].(string))
|
||||
if args == "" {
|
||||
ctx.SendChain(message.Text("ERROR: empty args"))
|
||||
return
|
||||
}
|
||||
c, ok := ctx.State["manager"].(*ctrl.Control[*zero.Ctx])
|
||||
if !ok {
|
||||
ctx.SendChain(message.Text("ERROR: no such plugin"))
|
||||
return
|
||||
}
|
||||
typ, err := newModelType(args)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return
|
||||
}
|
||||
*ptr = typ
|
||||
err = c.SetExtra(&cfg)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: set extra err: ", err))
|
||||
return
|
||||
}
|
||||
ctx.SendChain(message.Text("成功"))
|
||||
}
|
||||
}
|
||||
488
plugin/aichat/main.go
Normal file
488
plugin/aichat/main.go
Normal file
@@ -0,0 +1,488 @@
|
||||
// Package aichat OpenAI聊天和群聊总结
|
||||
package aichat
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"math/rand"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/fumiama/deepinfra"
|
||||
"github.com/fumiama/deepinfra/model"
|
||||
goba "github.com/fumiama/go-onebot-agent"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/tidwall/gjson"
|
||||
|
||||
zero "github.com/wdvxdr1123/ZeroBot"
|
||||
"github.com/wdvxdr1123/ZeroBot/extension/single"
|
||||
"github.com/wdvxdr1123/ZeroBot/message"
|
||||
|
||||
"github.com/FloatTech/AnimeAPI/airecord"
|
||||
"github.com/FloatTech/floatbox/process"
|
||||
ctrl "github.com/FloatTech/zbpctrl"
|
||||
"github.com/FloatTech/zbputils/chat"
|
||||
"github.com/FloatTech/zbputils/control"
|
||||
"github.com/FloatTech/zbputils/ctxext"
|
||||
)
|
||||
|
||||
var (
|
||||
// en data [8 temp] [8 rate] LSB
|
||||
en = control.AutoRegister(&ctrl.Options[*zero.Ctx]{
|
||||
DisableOnDefault: false,
|
||||
Extra: control.ExtraFromString("aichat"),
|
||||
Brief: "OpenAI聊天",
|
||||
Help: "- 设置AI聊天触发概率10\n" +
|
||||
"- 设置AI聊天温度80\n" +
|
||||
"- 设置AI聊天(|识图|Agent)接口类型[OpenAI|OLLaMA|GenAI]\n" +
|
||||
"- 设置AI聊天(不)使用Agent模式\n" +
|
||||
"- 设置AI聊天(不)支持系统提示词\n" +
|
||||
"- 设置AI聊天(|识图|Agent)接口地址https://api.siliconflow.cn/v1/chat/completions\n" +
|
||||
"- 设置AI聊天(|识图|Agent)密钥xxx\n" +
|
||||
"- 设置AI聊天(|识图|Agent)模型名Qwen/Qwen3-8B\n" +
|
||||
"- 查看AI聊天系统提示词\n" +
|
||||
"- 重置AI聊天系统提示词\n" +
|
||||
"- 设置AI聊天系统提示词xxx\n" +
|
||||
"- 设置AI聊天分隔符</think>(留空则清除)\n" +
|
||||
"- 设置AI聊天(不)响应AT\n" +
|
||||
"- 设置AI聊天最大长度4096\n" +
|
||||
"- 设置AI聊天TopP 0.9\n" +
|
||||
"- 设置AI聊天(不)以AI语音输出\n" +
|
||||
"- 查看AI聊天配置\n" +
|
||||
"- 重置AI聊天\n" +
|
||||
"- 群聊总结 [消息数目]|群聊总结 1000\n" +
|
||||
"- /gpt [内容] (使用大模型聊天)\n",
|
||||
|
||||
PrivateDataFolder: "aichat",
|
||||
}).ApplySingle(single.New(
|
||||
single.WithKeyFn(func(ctx *zero.Ctx) int64 {
|
||||
if ctx.Event.GroupID == 0 {
|
||||
return -ctx.Event.UserID
|
||||
}
|
||||
return ctx.Event.GroupID
|
||||
}),
|
||||
// no post option, silently quit
|
||||
))
|
||||
)
|
||||
|
||||
var (
|
||||
limit = ctxext.NewLimiterManager(time.Second*30, 1)
|
||||
)
|
||||
|
||||
func init() {
|
||||
en.OnMessage(ensureconfig, func(ctx *zero.Ctx) bool {
|
||||
gid := ctx.Event.GroupID
|
||||
if gid == 0 {
|
||||
gid = -ctx.Event.UserID
|
||||
}
|
||||
stor, err := newstorage(ctx, gid)
|
||||
if err != nil {
|
||||
logrus.Warnln("ERROR: ", err)
|
||||
return false
|
||||
}
|
||||
ctx.State["__aichat_stor__"] = stor
|
||||
return ctx.ExtractPlainText() != "" &&
|
||||
(!stor.noreplyat() || (stor.noreplyat() && !ctx.Event.IsToMe))
|
||||
}).SetBlock(false).Handle(func(ctx *zero.Ctx) {
|
||||
gid := ctx.Event.GroupID
|
||||
if gid == 0 {
|
||||
gid = -ctx.Event.UserID
|
||||
}
|
||||
stor := ctx.State["__aichat_stor__"].(storage)
|
||||
rate := stor.rate()
|
||||
if !ctx.Event.IsToMe && rand.Intn(100) >= int(rate) {
|
||||
return
|
||||
}
|
||||
if ctx.Event.IsToMe {
|
||||
ctx.Block()
|
||||
}
|
||||
if cfg.Key == "" {
|
||||
logrus.Warnln("ERROR: get extra err: empty key")
|
||||
return
|
||||
}
|
||||
temperature := stor.temp()
|
||||
topp, maxn := cfg.mparams()
|
||||
|
||||
if !stor.noagent() && cfg.AgentAPI != "" && cfg.AgentModelName != "" {
|
||||
x := deepinfra.NewAPI(cfg.AgentAPI, string(cfg.AgentKey))
|
||||
mod, err := cfg.Type.protocol(cfg.AgentModelName, temperature, topp, maxn)
|
||||
if err != nil {
|
||||
logrus.Warnln("ERROR: ", err)
|
||||
return
|
||||
}
|
||||
role := goba.PermRoleUser
|
||||
if zero.AdminPermission(ctx) {
|
||||
role = goba.PermRoleAdmin
|
||||
if zero.SuperUserPermission(ctx) {
|
||||
role = goba.PermRoleOwner
|
||||
}
|
||||
}
|
||||
ag := chat.AgentOf(ctx.Event.SelfID)
|
||||
if cfg.ImageAPI != "" && !ag.CanViewImage() {
|
||||
mod, err := cfg.ImageType.protocol(cfg.ImageModelName, temperature, topp, maxn)
|
||||
if err != nil {
|
||||
logrus.Warnln("ERROR: ", err)
|
||||
return
|
||||
}
|
||||
ag.SetViewImageAPI(deepinfra.NewAPI(cfg.ImageAPI, string(cfg.ImageKey)), mod)
|
||||
}
|
||||
ctx.NoTimeout()
|
||||
hasresp := false
|
||||
for i := 0; i < 8; i++ { // 最大运行 8 轮因为问答上下文只有 16
|
||||
reqs := chat.CallAgent(ag, zero.SuperUserPermission(ctx), x, mod, gid, role)
|
||||
if len(reqs) == 0 {
|
||||
break
|
||||
}
|
||||
hasresp = true
|
||||
for _, req := range reqs {
|
||||
resp := ctx.CallAction(req.Action, req.Params)
|
||||
logrus.Infoln("[aichat] agent get resp:", reqs)
|
||||
ag.AddResponse(gid, &goba.APIResponse{
|
||||
Status: resp.Status,
|
||||
Data: json.RawMessage(resp.Data.Raw),
|
||||
Message: resp.Message,
|
||||
Wording: resp.Wording,
|
||||
RetCode: resp.RetCode,
|
||||
})
|
||||
}
|
||||
}
|
||||
if hasresp {
|
||||
ag.AddTerminus(gid)
|
||||
return
|
||||
}
|
||||
// no response, fall back to normal chat
|
||||
}
|
||||
|
||||
x := deepinfra.NewAPI(cfg.API, string(cfg.Key))
|
||||
mod, err := cfg.Type.protocol(cfg.ModelName, temperature, topp, maxn)
|
||||
if err != nil {
|
||||
logrus.Warnln("ERROR: ", err)
|
||||
return
|
||||
}
|
||||
data, err := x.Request(chat.GetChatContext(mod, gid, cfg.SystemP, bool(cfg.NoSystemP)))
|
||||
if err != nil {
|
||||
logrus.Warnln("[aichat] post err:", err)
|
||||
return
|
||||
}
|
||||
|
||||
txt := chat.Sanitize(strings.Trim(data, "\n "))
|
||||
if len(txt) > 0 {
|
||||
chat.AddChatReply(gid, txt)
|
||||
nick := zero.BotConfig.NickName[rand.Intn(len(zero.BotConfig.NickName))]
|
||||
txt = strings.ReplaceAll(txt, "{name}", ctx.CardOrNickName(ctx.Event.UserID))
|
||||
txt = strings.ReplaceAll(txt, "{me}", nick)
|
||||
id := any(nil)
|
||||
if ctx.Event.IsToMe {
|
||||
id = ctx.Event.MessageID
|
||||
}
|
||||
for _, t := range strings.Split(txt, "{segment}") {
|
||||
if t == "" {
|
||||
continue
|
||||
}
|
||||
logrus.Infoln("[aichat] 回复内容:", t)
|
||||
recCfg := airecord.GetConfig()
|
||||
record := ""
|
||||
if !fastfailnorecord && !stor.norecord() {
|
||||
record = ctx.GetAIRecord(recCfg.ModelID, recCfg.Customgid, t)
|
||||
if record != "" {
|
||||
ctx.SendChain(message.Record(record))
|
||||
continue
|
||||
}
|
||||
fastfailnorecord = true
|
||||
}
|
||||
if id != nil {
|
||||
id = ctx.SendChain(message.Reply(id), message.Text(t))
|
||||
} else {
|
||||
id = ctx.SendChain(message.Text(t))
|
||||
}
|
||||
process.SleepAbout1sTo2s()
|
||||
}
|
||||
}
|
||||
})
|
||||
en.OnPrefix("设置AI聊天触发概率", zero.AdminPermission).SetBlock(true).
|
||||
Handle(ctxext.NewStorageSaveBitmapHandler(bitmaprate, 0, 100))
|
||||
en.OnPrefix("设置AI聊天温度", zero.AdminPermission).SetBlock(true).
|
||||
Handle(ctxext.NewStorageSaveBitmapHandler(bitmaptemp, 0, 100))
|
||||
en.OnPrefix("设置AI聊天接口类型", ensureconfig, zero.OnlyPrivate, zero.SuperUserPermission).SetBlock(true).
|
||||
Handle(newextrasetmodeltype(&cfg.Type))
|
||||
en.OnPrefix("设置AI聊天识图接口类型", ensureconfig, zero.OnlyPrivate, zero.SuperUserPermission).SetBlock(true).
|
||||
Handle(newextrasetmodeltype(&cfg.ImageType))
|
||||
en.OnPrefix("设置AI聊天Agent接口类型", ensureconfig, zero.OnlyPrivate, zero.SuperUserPermission).SetBlock(true).
|
||||
Handle(newextrasetmodeltype(&cfg.AgentType))
|
||||
en.OnPrefix("设置AI聊天接口地址", ensureconfig, zero.OnlyPrivate, zero.SuperUserPermission).SetBlock(true).
|
||||
Handle(newextrasetstr(&cfg.API))
|
||||
en.OnPrefix("设置AI聊天识图接口地址", ensureconfig, zero.OnlyPrivate, zero.SuperUserPermission).SetBlock(true).
|
||||
Handle(newextrasetstr(&cfg.ImageAPI))
|
||||
en.OnPrefix("设置AI聊天Agent接口地址", ensureconfig, zero.OnlyPrivate, zero.SuperUserPermission).SetBlock(true).
|
||||
Handle(newextrasetstr(&cfg.AgentAPI))
|
||||
en.OnPrefix("设置AI聊天密钥", ensureconfig, zero.OnlyPrivate, zero.SuperUserPermission).SetBlock(true).
|
||||
Handle(newextrasetstr(&cfg.Key))
|
||||
en.OnPrefix("设置AI聊天识图密钥", ensureconfig, zero.OnlyPrivate, zero.SuperUserPermission).SetBlock(true).
|
||||
Handle(newextrasetstr(&cfg.ImageKey))
|
||||
en.OnPrefix("设置AI聊天Agent密钥", ensureconfig, zero.OnlyPrivate, zero.SuperUserPermission).SetBlock(true).
|
||||
Handle(newextrasetstr(&cfg.AgentKey))
|
||||
en.OnPrefix("设置AI聊天模型名", ensureconfig, zero.OnlyPrivate, zero.SuperUserPermission).SetBlock(true).
|
||||
Handle(newextrasetstr(&cfg.ModelName))
|
||||
en.OnPrefix("设置AI聊天识图模型名", ensureconfig, zero.OnlyPrivate, zero.SuperUserPermission).SetBlock(true).
|
||||
Handle(newextrasetstr(&cfg.ImageModelName))
|
||||
en.OnPrefix("设置AI聊天Agent模型名", ensureconfig, zero.OnlyPrivate, zero.SuperUserPermission).SetBlock(true).
|
||||
Handle(newextrasetstr(&cfg.AgentModelName))
|
||||
en.OnPrefix("设置AI聊天系统提示词", ensureconfig, zero.OnlyPrivate, zero.SuperUserPermission).SetBlock(true).
|
||||
Handle(newextrasetstr(&cfg.SystemP))
|
||||
en.OnFullMatch("查看AI聊天系统提示词", ensureconfig, zero.OnlyPrivate, zero.SuperUserPermission).SetBlock(true).Handle(func(ctx *zero.Ctx) {
|
||||
ctx.SendChain(message.Text(cfg.SystemP))
|
||||
})
|
||||
en.OnFullMatch("重置AI聊天系统提示词", ensureconfig, zero.OnlyPrivate, zero.SuperUserPermission).SetBlock(true).Handle(func(ctx *zero.Ctx) {
|
||||
c, ok := ctx.State["manager"].(*ctrl.Control[*zero.Ctx])
|
||||
if !ok {
|
||||
ctx.SendChain(message.Text("ERROR: no such plugin"))
|
||||
return
|
||||
}
|
||||
cfg.SystemP = chat.SystemPrompt
|
||||
err := c.SetExtra(&cfg)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: set extra err: ", err))
|
||||
return
|
||||
}
|
||||
ctx.SendChain(message.Text("成功"))
|
||||
})
|
||||
en.OnPrefix("设置AI聊天分隔符", ensureconfig, zero.OnlyPrivate, zero.SuperUserPermission).SetBlock(true).
|
||||
Handle(newextrasetstr(&cfg.Separator))
|
||||
en.OnRegex("^设置AI聊天(不)?响应AT$", ensureconfig, zero.SuperUserPermission).SetBlock(true).
|
||||
Handle(ctxext.NewStorageSaveBoolHandler(bitmapnrat))
|
||||
en.OnRegex("^设置AI聊天(不)?支持系统提示词$", ensureconfig, zero.OnlyPrivate, zero.SuperUserPermission).SetBlock(true).
|
||||
Handle(newextrasetbool(&cfg.NoSystemP))
|
||||
en.OnRegex("^设置AI聊天(不)?使用Agent模式$", ensureconfig, zero.SuperUserPermission).SetBlock(true).
|
||||
Handle(ctxext.NewStorageSaveBoolHandler(bitmapnagt))
|
||||
en.OnPrefix("设置AI聊天最大长度", ensureconfig, zero.OnlyPrivate, zero.SuperUserPermission).SetBlock(true).
|
||||
Handle(newextrasetuint(&cfg.MaxN))
|
||||
en.OnPrefix("设置AI聊天TopP", ensureconfig, zero.OnlyPrivate, zero.SuperUserPermission).SetBlock(true).
|
||||
Handle(newextrasetfloat32(&cfg.TopP))
|
||||
en.OnRegex("^设置AI聊天(不)?以AI语音输出$", ensureconfig, zero.AdminPermission).SetBlock(true).
|
||||
Handle(ctxext.NewStorageSaveBoolHandler(bitmapnrec))
|
||||
en.OnFullMatch("查看AI聊天配置", ensureconfig, zero.SuperUserPermission).SetBlock(true).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
gid := ctx.Event.GroupID
|
||||
stor, err := newstorage(ctx, gid)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return
|
||||
}
|
||||
ctx.SendChain(
|
||||
message.Text(
|
||||
"【当前AI聊天本群配置】\n",
|
||||
"• 触发概率:", int(stor.rate()), "\n",
|
||||
"• 温度:", stor.temp(), "\n",
|
||||
"• 以AI语音输出:", ModelBool(!stor.norecord()), "\n",
|
||||
"• 使用Agent:", ModelBool(!stor.noagent()), "\n",
|
||||
"• 响应@:", ModelBool(!stor.noreplyat()), "\n",
|
||||
),
|
||||
message.Text("【当前AI聊天全局配置】\n", &cfg),
|
||||
)
|
||||
})
|
||||
en.OnFullMatch("重置AI聊天", ensureconfig, zero.SuperUserPermission).SetBlock(true).Handle(func(ctx *zero.Ctx) {
|
||||
chat.ResetChat()
|
||||
ctx.SendChain(message.Text("成功"))
|
||||
})
|
||||
|
||||
// 添加群聊总结功能
|
||||
en.OnRegex(`^群聊总结\s?(\d*)$`, ensureconfig, zero.OnlyGroup, zero.AdminPermission).SetBlock(true).Limit(limit.LimitByGroup).Handle(func(ctx *zero.Ctx) {
|
||||
ctx.SendChain(message.Text("少女思考中..."))
|
||||
gid := ctx.Event.GroupID
|
||||
if gid == 0 {
|
||||
gid = -ctx.Event.UserID
|
||||
}
|
||||
p, _ := strconv.ParseInt(ctx.State["regex_matched"].([]string)[1], 10, 64)
|
||||
if p > 1000 {
|
||||
p = 1000
|
||||
}
|
||||
if p == 0 {
|
||||
p = 200
|
||||
}
|
||||
group := ctx.GetGroupInfo(gid, false)
|
||||
if group.MemberCount == 0 {
|
||||
ctx.SendChain(message.Text(zero.BotConfig.NickName[0], "未加入", group.Name, "(", gid, "),无法获取总结"))
|
||||
return
|
||||
}
|
||||
|
||||
var messages []string
|
||||
|
||||
h := ctx.GetGroupMessageHistory(gid, 0, p, false)
|
||||
h.Get("messages").ForEach(func(_, msgObj gjson.Result) bool {
|
||||
nickname := msgObj.Get("sender.nickname").Str
|
||||
text := strings.TrimSpace(message.ParseMessageFromString(msgObj.Get("raw_message").Str).ExtractPlainText())
|
||||
if text != "" {
|
||||
messages = append(messages, nickname+": "+text)
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
if len(messages) == 0 {
|
||||
ctx.SendChain(message.Text("ERROR: 历史消息为空或者无法获得历史消息"))
|
||||
return
|
||||
}
|
||||
|
||||
// 构造总结请求提示 (使用通用版省流提示词)
|
||||
// 使用反引号定义多行字符串,更清晰
|
||||
promptTemplate := `请对以下群聊对话进行【极简总结】。
|
||||
要求:
|
||||
1. 剔除客套与废话,直击主题。
|
||||
2. 使用 Markdown 列表格式。
|
||||
3. 按以下结构输出:
|
||||
- 🎯 核心议题:(一句话概括)
|
||||
- 💡 关键观点/结论:(提取3-5个重点)
|
||||
- ✅ 下一步/待办:(如果有,明确谁做什么)
|
||||
|
||||
群聊对话内容如下:
|
||||
`
|
||||
summaryPrompt := promptTemplate + strings.Join(messages, "\n")
|
||||
|
||||
stor, err := newstorage(ctx, gid)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return
|
||||
}
|
||||
// 调用大模型API进行总结
|
||||
summary, err := llmchat(summaryPrompt, stor.temp())
|
||||
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return
|
||||
}
|
||||
|
||||
var b strings.Builder
|
||||
b.WriteString("群 ")
|
||||
b.WriteString(group.Name)
|
||||
b.WriteByte('(')
|
||||
b.WriteString(strconv.FormatInt(gid, 10))
|
||||
b.WriteString(") 的 ")
|
||||
b.WriteString(strconv.FormatInt(p, 10))
|
||||
b.WriteString(" 条消息总结:\n\n")
|
||||
b.WriteString(summary)
|
||||
|
||||
// 分割总结内容为多段(按1000字符长度切割)
|
||||
summaryText := b.String()
|
||||
msg := make(message.Message, 0)
|
||||
for len(summaryText) > 0 {
|
||||
if len(summaryText) <= 1000 {
|
||||
msg = append(msg, ctxext.FakeSenderForwardNode(ctx, message.Text(summaryText)))
|
||||
break
|
||||
}
|
||||
|
||||
// 查找1000字符内的最后一个换行符,尽量在换行处分割
|
||||
chunk := summaryText[:1000]
|
||||
lastNewline := strings.LastIndex(chunk, "\n")
|
||||
if lastNewline > 0 {
|
||||
chunk = summaryText[:lastNewline+1]
|
||||
}
|
||||
|
||||
msg = append(msg, ctxext.FakeSenderForwardNode(ctx, message.Text(chunk)))
|
||||
summaryText = summaryText[len(chunk):]
|
||||
}
|
||||
if len(msg) > 0 {
|
||||
ctx.Send(msg)
|
||||
}
|
||||
})
|
||||
|
||||
// 添加 /gpt 命令处理(同时支持回复消息和直接使用)
|
||||
en.OnKeyword("/gpt", ensureconfig).SetBlock(true).Handle(func(ctx *zero.Ctx) {
|
||||
gid := ctx.Event.GroupID
|
||||
if gid == 0 {
|
||||
gid = -ctx.Event.UserID
|
||||
}
|
||||
text := ctx.MessageString()
|
||||
|
||||
var query string
|
||||
var replyContent string
|
||||
|
||||
// 检查是否是回复消息 (使用MessageElement检查而不是CQ码)
|
||||
for _, elem := range ctx.Event.Message {
|
||||
if elem.Type == "reply" {
|
||||
// 提取被回复的消息ID
|
||||
replyIDStr := elem.Data["id"]
|
||||
replyID, err := strconv.ParseInt(replyIDStr, 10, 64)
|
||||
if err == nil {
|
||||
// 获取被回复的消息内容
|
||||
replyMsg := ctx.GetMessage(replyID)
|
||||
if replyMsg.Elements != nil {
|
||||
replyContent = replyMsg.Elements.ExtractPlainText()
|
||||
}
|
||||
}
|
||||
break // 找到回复元素后退出循环
|
||||
}
|
||||
}
|
||||
|
||||
// 提取 /gpt 后面的内容
|
||||
parts := strings.SplitN(text, "/gpt", 2)
|
||||
|
||||
var gContent string
|
||||
if len(parts) > 1 {
|
||||
gContent = strings.TrimSpace(parts[1])
|
||||
}
|
||||
|
||||
// 组合内容:优先使用回复内容,如果同时有/gpt内容则拼接
|
||||
switch {
|
||||
case replyContent != "" && gContent != "":
|
||||
query = replyContent + "\n" + gContent
|
||||
case replyContent != "":
|
||||
query = replyContent
|
||||
case gContent != "":
|
||||
query = gContent
|
||||
default:
|
||||
return
|
||||
}
|
||||
|
||||
stor, err := newstorage(ctx, gid)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return
|
||||
}
|
||||
// 调用大模型API进行聊天
|
||||
reply, err := llmchat(query, stor.temp())
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return
|
||||
}
|
||||
|
||||
// 分割总结内容为多段(按1000字符长度切割)
|
||||
msg := make(message.Message, 0)
|
||||
for len(reply) > 0 {
|
||||
if len(reply) <= 1000 {
|
||||
msg = append(msg, ctxext.FakeSenderForwardNode(ctx, message.Text(reply)))
|
||||
break
|
||||
}
|
||||
|
||||
// 查找1000字符内的最后一个换行符,尽量在换行处分割
|
||||
chunk := reply[:1000]
|
||||
lastNewline := strings.LastIndex(chunk, "\n")
|
||||
if lastNewline > 0 {
|
||||
chunk = reply[:lastNewline+1]
|
||||
}
|
||||
|
||||
msg = append(msg, ctxext.FakeSenderForwardNode(ctx, message.Text(chunk)))
|
||||
reply = reply[len(chunk):]
|
||||
}
|
||||
if len(msg) > 0 {
|
||||
ctx.Send(msg)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// llmchat 调用大模型API包装
|
||||
func llmchat(prompt string, temp float32) (string, error) {
|
||||
topp, maxn := cfg.mparams()
|
||||
|
||||
x := deepinfra.NewAPI(cfg.API, string(cfg.Key))
|
||||
|
||||
mod, err := cfg.Type.protocol(cfg.ModelName, temp, topp, maxn)
|
||||
if err != nil {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
data, err := x.Request(mod.User(model.NewContentText(prompt)))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return strings.TrimSpace(data), nil
|
||||
}
|
||||
53
plugin/aichat/storage.go
Normal file
53
plugin/aichat/storage.go
Normal file
@@ -0,0 +1,53 @@
|
||||
package aichat
|
||||
|
||||
import (
|
||||
"github.com/FloatTech/zbputils/ctxext"
|
||||
zero "github.com/wdvxdr1123/ZeroBot"
|
||||
)
|
||||
|
||||
const (
|
||||
bitmaprate = 0x0000ff
|
||||
bitmaptemp = 0x00ff00
|
||||
bitmapnagt = 0x010000
|
||||
bitmapnrec = 0x020000
|
||||
bitmapnrat = 0x040000
|
||||
)
|
||||
|
||||
var (
|
||||
fastfailnorecord = false
|
||||
)
|
||||
|
||||
type storage ctxext.Storage
|
||||
|
||||
func newstorage(ctx *zero.Ctx, gid int64) (storage, error) {
|
||||
s, err := ctxext.NewStorage(ctx, gid)
|
||||
return storage(s), err
|
||||
}
|
||||
|
||||
func (s storage) rate() uint8 {
|
||||
return uint8((ctxext.Storage)(s).Get(bitmaprate))
|
||||
}
|
||||
|
||||
func (s storage) temp() float32 {
|
||||
temp := int8((ctxext.Storage)(s).Get(bitmaptemp))
|
||||
// 处理温度参数
|
||||
if temp <= 0 {
|
||||
temp = 70 // default setting
|
||||
}
|
||||
if temp > 100 {
|
||||
temp = 100
|
||||
}
|
||||
return float32(temp) / 100
|
||||
}
|
||||
|
||||
func (s storage) noagent() bool {
|
||||
return (ctxext.Storage)(s).GetBool(bitmapnagt)
|
||||
}
|
||||
|
||||
func (s storage) norecord() bool {
|
||||
return (ctxext.Storage)(s).GetBool(bitmapnrec)
|
||||
}
|
||||
|
||||
func (s storage) noreplyat() bool {
|
||||
return (ctxext.Storage)(s).GetBool(bitmapnrat)
|
||||
}
|
||||
149
plugin/aichat/storage_test.go
Normal file
149
plugin/aichat/storage_test.go
Normal file
@@ -0,0 +1,149 @@
|
||||
package aichat
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/FloatTech/zbputils/ctxext"
|
||||
)
|
||||
|
||||
func TestStorage_rate(t *testing.T) {
|
||||
s := storage(ctxext.Storage(0))
|
||||
|
||||
// 测试默认值
|
||||
if rate := s.rate(); rate != 0 {
|
||||
t.Errorf("default rate() = %v, want 0", rate)
|
||||
}
|
||||
|
||||
// 设置值并测试
|
||||
s = storage((ctxext.Storage)(s).Set(int64(100), bitmaprate))
|
||||
if rate := s.rate(); rate != 100 {
|
||||
t.Errorf("rate() after set = %v, want 100", rate)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStorage_temp(t *testing.T) {
|
||||
s := storage(ctxext.Storage(0))
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
setValue int64
|
||||
expected float32
|
||||
}{
|
||||
{"default temp (0)", 0, 0.70}, // 默认值 70/100
|
||||
{"valid temp 50", 50, 0.50}, // 50/100 = 0.50
|
||||
{"valid temp 80", 80, 0.80}, // 80/100 = 0.80
|
||||
{"max temp 100", 100, 1.00}, // 100/100 = 1.00
|
||||
{"over max temp", 127, 1.00}, // 限制为 100/100 = 1.00
|
||||
{"negative temp", -10, 0.70}, // 默认值 70/100
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
s = storage((ctxext.Storage)(s).Set(tt.setValue, bitmaptemp))
|
||||
|
||||
result := s.temp()
|
||||
if result != tt.expected {
|
||||
t.Errorf("temp() = %v, want %v", result, tt.expected)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestStorage_noagent(t *testing.T) {
|
||||
s := storage(ctxext.Storage(0))
|
||||
|
||||
// 测试默认值
|
||||
if noagent := s.noagent(); noagent != false {
|
||||
t.Errorf("default noagent() = %v, want false", noagent)
|
||||
}
|
||||
|
||||
// 设置为 true 并测试
|
||||
s = storage((ctxext.Storage)(s).Set(1, bitmapnagt))
|
||||
if noagent := s.noagent(); noagent != true {
|
||||
t.Errorf("noagent() after set true = %v, want true", noagent)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStorage_norecord(t *testing.T) {
|
||||
s := storage(ctxext.Storage(0))
|
||||
|
||||
// 测试默认值
|
||||
if norecord := s.norecord(); norecord != false {
|
||||
t.Errorf("default norecord() = %v, want false", norecord)
|
||||
}
|
||||
|
||||
// 设置为 true 并测试
|
||||
s = storage((ctxext.Storage)(s).Set(1, bitmapnrec))
|
||||
if norecord := s.norecord(); norecord != true {
|
||||
t.Errorf("norecord() after set true = %v, want true", norecord)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStorage_noreplyat(t *testing.T) {
|
||||
s := storage(ctxext.Storage(0))
|
||||
|
||||
// 测试默认值
|
||||
if noreplyat := s.noreplyat(); noreplyat != false {
|
||||
t.Errorf("default noreplyat() = %v, want false", noreplyat)
|
||||
}
|
||||
|
||||
// 设置为 true 并测试
|
||||
s = storage((ctxext.Storage)(s).Set(1, bitmapnrat))
|
||||
if noreplyat := s.noreplyat(); noreplyat != true {
|
||||
t.Errorf("noreplyat() after set true = %v, want true", noreplyat)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStorage_Integration(t *testing.T) {
|
||||
s := storage(ctxext.Storage(0))
|
||||
|
||||
// 设置各种值
|
||||
s = storage((ctxext.Storage)(s).Set(int64(75), bitmaprate))
|
||||
s = storage((ctxext.Storage)(s).Set(int64(85), bitmaptemp))
|
||||
s = storage((ctxext.Storage)(s).Set(1, bitmapnagt))
|
||||
s = storage((ctxext.Storage)(s).Set(0, bitmapnrec))
|
||||
s = storage((ctxext.Storage)(s).Set(1, bitmapnrat))
|
||||
|
||||
// 验证所有方法
|
||||
if rate := s.rate(); rate != 75 {
|
||||
t.Errorf("rate() = %v, want 75", rate)
|
||||
}
|
||||
|
||||
if temp := s.temp(); temp != 0.85 {
|
||||
t.Errorf("temp() = %v, want 0.85", temp)
|
||||
}
|
||||
|
||||
if noagent := s.noagent(); !noagent {
|
||||
t.Errorf("noagent() = %v, want true", noagent)
|
||||
}
|
||||
|
||||
if norecord := s.norecord(); norecord {
|
||||
t.Errorf("norecord() = %v, want false", norecord)
|
||||
}
|
||||
|
||||
if noreplyat := s.noreplyat(); !noreplyat {
|
||||
t.Errorf("noreplyat() = %v, want true", noreplyat)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkStorage_rate(b *testing.B) {
|
||||
s := storage(ctxext.Storage(0))
|
||||
|
||||
s = storage((ctxext.Storage)(s).Set(int64(100), bitmaprate))
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
s.rate()
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkStorage_temp(b *testing.B) {
|
||||
s := storage(ctxext.Storage(0))
|
||||
|
||||
s = storage((ctxext.Storage)(s).Set(int64(80), bitmaptemp))
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
s.temp()
|
||||
}
|
||||
}
|
||||
722
plugin/aifalse/main.go
Normal file
722
plugin/aifalse/main.go
Normal file
@@ -0,0 +1,722 @@
|
||||
// Package aifalse 暂时只有服务器监控
|
||||
package aifalse
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"image"
|
||||
"image/color"
|
||||
"math"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
"github.com/FloatTech/AnimeAPI/bilibili"
|
||||
"github.com/FloatTech/floatbox/file"
|
||||
"github.com/FloatTech/floatbox/web"
|
||||
"github.com/FloatTech/gg"
|
||||
"github.com/FloatTech/imgfactory"
|
||||
ctrl "github.com/FloatTech/zbpctrl"
|
||||
"github.com/FloatTech/zbputils/control"
|
||||
"github.com/FloatTech/zbputils/ctxext"
|
||||
"github.com/FloatTech/zbputils/img/text"
|
||||
"github.com/disintegration/imaging"
|
||||
"github.com/shirou/gopsutil/v4/cpu"
|
||||
"github.com/shirou/gopsutil/v4/disk"
|
||||
"github.com/shirou/gopsutil/v4/host"
|
||||
"github.com/shirou/gopsutil/v4/mem"
|
||||
"github.com/sirupsen/logrus"
|
||||
"golang.org/x/text/cases"
|
||||
"golang.org/x/text/language"
|
||||
|
||||
"github.com/FloatTech/ZeroBot-Plugin/kanban/banner"
|
||||
|
||||
zero "github.com/wdvxdr1123/ZeroBot"
|
||||
"github.com/wdvxdr1123/ZeroBot/message"
|
||||
)
|
||||
|
||||
const (
|
||||
backgroundURL = "https://pic.re/image"
|
||||
referer = "https://weibo.com/"
|
||||
)
|
||||
|
||||
var (
|
||||
boottime = time.Now()
|
||||
bgdata *[]byte
|
||||
bgcount uintptr
|
||||
isday bool
|
||||
lightcolor = [3][4]uint8{{255, 70, 0, 255}, {255, 165, 0, 255}, {145, 240, 145, 255}}
|
||||
darkcolor = [3][4]uint8{{215, 50, 0, 255}, {205, 135, 0, 255}, {115, 200, 115, 255}}
|
||||
)
|
||||
|
||||
func init() { // 插件主体
|
||||
engine := control.AutoRegister(&ctrl.Options[*zero.Ctx]{
|
||||
DisableOnDefault: false,
|
||||
Brief: "自检, 全局限速",
|
||||
Help: "- 查询计算机当前活跃度: [检查身体 | 自检 | 启动自检 | 系统状态]\n" +
|
||||
"- 设置默认限速为每 m [分钟 | 秒] n 次触发",
|
||||
})
|
||||
c, ok := control.Lookup("aifalse")
|
||||
if !ok {
|
||||
panic("register aifalse error")
|
||||
}
|
||||
m := c.GetData(0)
|
||||
n := (m >> 16) & 0xffff
|
||||
m &= 0xffff
|
||||
if m != 0 || n != 0 {
|
||||
ctxext.SetDefaultLimiterManagerParam(time.Duration(m)*time.Second, int(n))
|
||||
logrus.Infoln("设置默认限速为每", m, "秒触发", n, "次")
|
||||
}
|
||||
engine.OnFullMatchGroup([]string{"检查身体", "自检", "启动自检", "系统状态"}, zero.AdminPermission).SetBlock(true).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
now := time.Now().Hour()
|
||||
isday = now > 7 && now < 19
|
||||
|
||||
botrunstatus := ctx.CallAction("get_status", zero.Params{}).Data
|
||||
botverisoninfo := ctx.GetVersionInfo()
|
||||
sb := &strings.Builder{}
|
||||
sb.WriteString("在线(")
|
||||
sb.WriteString(botverisoninfo.Get("app_name").String())
|
||||
sb.WriteString("-")
|
||||
sb.WriteString(botverisoninfo.Get("app_version").String())
|
||||
sb.WriteString(") | 收")
|
||||
sb.WriteString(botrunstatus.Get("stat").Get("message_received").String())
|
||||
sb.WriteString(" | 发")
|
||||
sb.WriteString(botrunstatus.Get("stat").Get("message_sent").String())
|
||||
sb.WriteString(" | 群")
|
||||
sb.WriteString(strconv.Itoa(len(ctx.GetGroupList().Array())))
|
||||
sb.WriteString(" | 好友")
|
||||
sb.WriteString(strconv.Itoa(len(ctx.GetFriendList().Array())))
|
||||
|
||||
img, err := drawstatus(ctx.State["manager"].(*ctrl.Control[*zero.Ctx]), ctx.Event.SelfID, zero.BotConfig.NickName[0], sb.String())
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return
|
||||
}
|
||||
sendimg, err := imgfactory.ToBytes(img)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return
|
||||
}
|
||||
if id := ctx.SendChain(message.ImageBytes(sendimg)); id.ID() == 0 {
|
||||
ctx.SendChain(message.Text("ERROR: 可能被风控了"))
|
||||
}
|
||||
})
|
||||
engine.OnRegex(`^设置默认限速为每\s*(\d+)\s*(分钟|秒)\s*(\d+)\s*次触发$`, zero.SuperUserPermission).SetBlock(true).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
c, ok := ctx.State["manager"].(*ctrl.Control[*zero.Ctx])
|
||||
if !ok {
|
||||
ctx.SendChain(message.Text("ERROR: no such plugin"))
|
||||
return
|
||||
}
|
||||
m, err := strconv.ParseInt(ctx.State["regex_matched"].([]string)[1], 10, 64)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return
|
||||
}
|
||||
if ctx.State["regex_matched"].([]string)[2] == "分钟" {
|
||||
m *= 60
|
||||
}
|
||||
if m >= 65536 || m <= 0 {
|
||||
ctx.SendChain(message.Text("ERROR: interval too big"))
|
||||
return
|
||||
}
|
||||
n, err := strconv.ParseInt(ctx.State["regex_matched"].([]string)[3], 10, 64)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return
|
||||
}
|
||||
if n >= 65536 || n <= 0 {
|
||||
ctx.SendChain(message.Text("ERROR: burst too big"))
|
||||
return
|
||||
}
|
||||
ctxext.SetDefaultLimiterManagerParam(time.Duration(m)*time.Second, int(n))
|
||||
err = c.SetData(0, (m&0xffff)|((n<<16)&0xffff0000))
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return
|
||||
}
|
||||
ctx.SendChain(message.Text("设置默认限速为每", m, "秒触发", n, "次"))
|
||||
})
|
||||
}
|
||||
|
||||
func drawstatus(m *ctrl.Control[*zero.Ctx], uid int64, botname string, botrunstatus string) (sendimg image.Image, err error) {
|
||||
diskstate, err := diskstate()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
diskcardh := 40 + (20+50)*len(diskstate) + 40 - 20
|
||||
|
||||
moreinfo, err := moreinfo(m)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
moreinfocardh := 30 + (20+32*72/96)*len(moreinfo) + 30 - 20
|
||||
|
||||
basicstate, err := basicstate()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
dldata := (*[]byte)(atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(&bgdata))))
|
||||
if dldata == (*[]byte)(nil) || uintptr(time.Since(boottime).Hours()/24) >= atomic.LoadUintptr(&bgcount) {
|
||||
url, err1 := bilibili.GetRealURL(backgroundURL)
|
||||
if err1 != nil {
|
||||
return nil, err1
|
||||
}
|
||||
data, err1 := web.RequestDataWith(web.NewDefaultClient(), url, "", referer, "", nil)
|
||||
if err1 != nil {
|
||||
return nil, err1
|
||||
}
|
||||
atomic.AddUintptr(&bgcount, 1)
|
||||
atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(&bgdata)), unsafe.Pointer(&data))
|
||||
dldata = &data
|
||||
}
|
||||
data := *dldata
|
||||
|
||||
back, _, err := image.Decode(bytes.NewReader(data))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
data, err = web.GetData("https://q4.qlogo.cn/g?b=qq&nk=" + strconv.FormatInt(uid, 10) + "&s=640")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
avatar, _, err := image.Decode(bytes.NewReader(data))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
avatarf := imgfactory.Size(avatar, 200, 200)
|
||||
|
||||
fontbyte, err := file.GetLazyData(text.GlowSansFontFile, control.Md5File, true)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
canvas := gg.NewContext(1280, 70+250+40+380+diskcardh+40+moreinfocardh+40+70)
|
||||
|
||||
bh, bw, ch, cw := float64(back.Bounds().Dy()), float64(back.Bounds().Dx()), float64(canvas.H()), float64(canvas.W())
|
||||
|
||||
if bh/bw < ch/cw {
|
||||
back = imgfactory.Size(back, int(bw*ch/bh), int(bh*ch/bh)).Image()
|
||||
canvas.DrawImageAnchored(back, canvas.W()/2, canvas.H()/2, 0.5, 0.5)
|
||||
} else {
|
||||
back = imgfactory.Size(back, int(bw*cw/bw), int(bh*cw/bw)).Image()
|
||||
canvas.DrawImage(back, 0, 0)
|
||||
}
|
||||
var blurback image.Image
|
||||
bwg := &sync.WaitGroup{}
|
||||
bwg.Add(1)
|
||||
go func() {
|
||||
defer bwg.Done()
|
||||
blurback = imaging.Blur(canvas.Image(), 8)
|
||||
}()
|
||||
|
||||
if !isday {
|
||||
canvas.SetRGBA255(0, 0, 0, 50)
|
||||
canvas.DrawRectangle(0, 0, cw, ch)
|
||||
canvas.Fill()
|
||||
}
|
||||
|
||||
wg := &sync.WaitGroup{}
|
||||
wg.Add(5)
|
||||
|
||||
cardw := canvas.W() - 70 - 70
|
||||
|
||||
titlecardh := 250
|
||||
basiccardh := 380
|
||||
|
||||
var titleimg, basicimg, diskimg, moreinfoimg, shadowimg image.Image
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
titlecard := gg.NewContext(cardw, titlecardh)
|
||||
bwg.Wait()
|
||||
|
||||
titlecard.DrawRoundedRectangle(1, 1, float64(titlecard.W()-1*2), float64(titlecardh-1*2), 16)
|
||||
titlecard.ClipPreserve()
|
||||
titlecard.DrawImage(blurback, -70, -70)
|
||||
titlecard.SetColor(colorswitch(140))
|
||||
titlecard.FillPreserve()
|
||||
|
||||
titlecard.SetLineWidth(3)
|
||||
titlecard.SetColor(colorswitch(100))
|
||||
titlecard.ResetClip()
|
||||
titlecard.Stroke()
|
||||
|
||||
titlecard.DrawImage(avatarf.Circle(0).Image(), (titlecardh-avatarf.H())/2, (titlecardh-avatarf.H())/2)
|
||||
|
||||
err = titlecard.ParseFontFace(fontbyte, 72)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
fw, _ := titlecard.MeasureString(botname)
|
||||
|
||||
titlecard.SetColor(fontcolorswitch())
|
||||
|
||||
titlecard.DrawStringAnchored(botname, float64(titlecardh)+fw/2, float64(titlecardh)*0.5/2, 0.5, 0.5)
|
||||
|
||||
err = titlecard.ParseFontFace(fontbyte, 24)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
titlecard.SetColor(fontcolorswitch())
|
||||
|
||||
titlecard.NewSubPath()
|
||||
titlecard.MoveTo(float64(titlecardh), float64(titlecardh)/2)
|
||||
titlecard.LineTo(float64(titlecard.W()-titlecardh), float64(titlecardh)/2)
|
||||
titlecard.Stroke()
|
||||
|
||||
fw, _ = titlecard.MeasureString(botrunstatus)
|
||||
|
||||
titlecard.DrawStringAnchored(botrunstatus, float64(titlecardh)+fw/2, float64(titlecardh)*(0.5+0.25/2), 0.5, 0.5)
|
||||
|
||||
brt, err := botruntime()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
fw, _ = titlecard.MeasureString(brt)
|
||||
|
||||
titlecard.DrawStringAnchored(brt, float64(titlecardh)+fw/2, float64(titlecardh)*(0.5+0.5/2), 0.5, 0.5)
|
||||
|
||||
bs, err := botstatus()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
fw, _ = titlecard.MeasureString(bs)
|
||||
|
||||
titlecard.DrawStringAnchored(bs, float64(titlecardh)+fw/2, float64(titlecardh)*(0.5+0.75/2), 0.5, 0.5)
|
||||
titleimg = titlecard.Image()
|
||||
}()
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
basiccard := gg.NewContext(cardw, basiccardh)
|
||||
bwg.Wait()
|
||||
|
||||
basiccard.DrawRoundedRectangle(1, 1, float64(basiccard.W()-1*2), float64(basiccardh-1*2), 16)
|
||||
basiccard.ClipPreserve()
|
||||
basiccard.DrawImage(blurback, -70, -70-titlecardh-40)
|
||||
basiccard.SetColor(colorswitch(140))
|
||||
basiccard.FillPreserve()
|
||||
|
||||
basiccard.SetLineWidth(3)
|
||||
basiccard.SetColor(colorswitch(100))
|
||||
basiccard.ResetClip()
|
||||
basiccard.Stroke()
|
||||
|
||||
bslen := len(basicstate)
|
||||
for i, v := range basicstate {
|
||||
offset := float64(i) * ((float64(basiccard.W())-200*float64(bslen))/float64(bslen+1) + 200)
|
||||
|
||||
basiccard.SetRGBA255(57, 57, 57, 255)
|
||||
if isday {
|
||||
basiccard.SetRGBA255(235, 235, 235, 255)
|
||||
}
|
||||
basiccard.DrawCircle((float64(basiccard.W())-200*float64(bslen))/float64(bslen+1)+200/2+offset, 20+200/2, 100)
|
||||
basiccard.Fill()
|
||||
|
||||
colors := darkcolor
|
||||
if isday {
|
||||
colors = lightcolor
|
||||
}
|
||||
|
||||
switch {
|
||||
case v.precent > 90:
|
||||
basiccard.SetColor(slice2color(colors[0]))
|
||||
case v.precent > 70:
|
||||
basiccard.SetColor(slice2color(colors[1]))
|
||||
default:
|
||||
basiccard.SetColor(slice2color(colors[2]))
|
||||
}
|
||||
|
||||
basiccard.NewSubPath()
|
||||
basiccard.MoveTo((float64(basiccard.W())-200*float64(bslen))/float64(bslen+1)+200/2+offset, 20+200/2)
|
||||
basiccard.DrawEllipticalArc((float64(basiccard.W())-200*float64(bslen))/float64(bslen+1)+200/2+offset, 20+200/2, 100, 100, -0.5*math.Pi, -0.5*math.Pi+2*v.precent*0.01*math.Pi)
|
||||
basiccard.Fill()
|
||||
|
||||
basiccard.SetColor(colorswitch(255))
|
||||
basiccard.DrawCircle((float64(basiccard.W())-200*float64(bslen))/float64(bslen+1)+200/2+offset, 20+200/2, 80)
|
||||
basiccard.Fill()
|
||||
|
||||
err = basiccard.ParseFontFace(fontbyte, 42)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
basiccard.SetRGBA255(213, 213, 213, 255)
|
||||
basiccard.DrawStringAnchored(strconv.FormatFloat(v.precent, 'f', 0, 64)+"%", (float64(basiccard.W())-200*float64(bslen))/float64(bslen+1)+200/2+offset, 20+200/2, 0.5, 0.5)
|
||||
|
||||
basiccard.SetColor(fontcolorswitch())
|
||||
|
||||
_, fw := basiccard.MeasureString(v.name)
|
||||
basiccard.DrawStringAnchored(v.name, (float64(basiccard.W())-200*float64(bslen))/float64(bslen+1)+200/2+offset, 20+200+15+basiccard.FontHeight()/2, 0.5, 0.5)
|
||||
|
||||
err = basiccard.ParseFontFace(fontbyte, 20)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
basiccard.SetColor(fontcolorswitch())
|
||||
|
||||
textoffsety := basiccard.FontHeight() + 10
|
||||
for k, s := range v.text {
|
||||
basiccard.DrawStringAnchored(s, (float64(basiccard.W())-200*float64(bslen))/float64(bslen+1)+200/2+offset, 20+200+15+fw+15+basiccard.FontHeight()/2+float64(k)*textoffsety, 0.5, 0.5)
|
||||
}
|
||||
}
|
||||
basicimg = basiccard.Image()
|
||||
}()
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
diskcard := gg.NewContext(cardw, diskcardh)
|
||||
bwg.Wait()
|
||||
|
||||
diskcard.DrawRoundedRectangle(1, 1, float64(diskcard.W()-1*2), float64(diskcardh-1*2), 16)
|
||||
diskcard.ClipPreserve()
|
||||
diskcard.DrawImage(blurback, -70, -70-titlecardh-40-basiccardh-40)
|
||||
diskcard.SetColor(colorswitch(140))
|
||||
diskcard.FillPreserve()
|
||||
|
||||
diskcard.SetLineWidth(3)
|
||||
diskcard.SetColor(colorswitch(100))
|
||||
diskcard.ResetClip()
|
||||
diskcard.Stroke()
|
||||
|
||||
err = diskcard.ParseFontFace(fontbyte, 32)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
dslen := len(diskstate)
|
||||
if dslen == 1 {
|
||||
diskcard.SetRGBA255(57, 57, 57, 255)
|
||||
if isday {
|
||||
diskcard.SetRGBA255(192, 192, 192, 255)
|
||||
}
|
||||
diskcard.DrawRoundedRectangle(40, 40, float64(diskcard.W())-40-100, 50, 12)
|
||||
diskcard.ClipPreserve()
|
||||
diskcard.Fill()
|
||||
|
||||
colors := darkcolor
|
||||
if isday {
|
||||
colors = lightcolor
|
||||
}
|
||||
|
||||
switch {
|
||||
case diskstate[0].precent > 90:
|
||||
diskcard.SetColor(slice2color(colors[0]))
|
||||
case diskstate[0].precent > 70:
|
||||
diskcard.SetColor(slice2color(colors[1]))
|
||||
default:
|
||||
diskcard.SetColor(slice2color(colors[2]))
|
||||
}
|
||||
|
||||
diskcard.DrawRoundedRectangle(40, 40, (float64(diskcard.W())-40-100)*diskstate[0].precent*0.01, 50, 12)
|
||||
diskcard.Fill()
|
||||
diskcard.ResetClip()
|
||||
|
||||
diskcard.SetColor(fontcolorswitch())
|
||||
|
||||
fw, _ := diskcard.MeasureString(diskstate[0].name)
|
||||
fw1, _ := diskcard.MeasureString(diskstate[0].text[0])
|
||||
|
||||
diskcard.DrawStringAnchored(diskstate[0].name, 40+10+fw/2, 40+50/2, 0.5, 0.5)
|
||||
diskcard.DrawStringAnchored(diskstate[0].text[0], (float64(diskcard.W())-100-10)-fw1/2, 40+50/2, 0.5, 0.5)
|
||||
diskcard.DrawStringAnchored(strconv.FormatFloat(diskstate[0].precent, 'f', 0, 64)+"%", float64(diskcard.W())-100/2, 40+50/2, 0.5, 0.5)
|
||||
} else {
|
||||
for i, v := range diskstate {
|
||||
offset := float64(i)*(50+20) - 20
|
||||
|
||||
diskcard.SetRGBA255(57, 57, 57, 255)
|
||||
if isday {
|
||||
diskcard.SetRGBA255(192, 192, 192, 255)
|
||||
}
|
||||
|
||||
diskcard.DrawRoundedRectangle(40, 40+(float64(diskcardh-40*2)-50*float64(dslen))/float64(dslen-1)+offset, float64(diskcard.W())-40-100, 50, 12)
|
||||
diskcard.ClipPreserve()
|
||||
diskcard.Fill()
|
||||
|
||||
colors := darkcolor
|
||||
if isday {
|
||||
colors = lightcolor
|
||||
}
|
||||
|
||||
switch {
|
||||
case v.precent > 90:
|
||||
diskcard.SetColor(slice2color(colors[0]))
|
||||
case v.precent > 70:
|
||||
diskcard.SetColor(slice2color(colors[1]))
|
||||
default:
|
||||
diskcard.SetColor(slice2color(colors[2]))
|
||||
}
|
||||
|
||||
diskcard.DrawRoundedRectangle(40, 40+(float64(diskcardh-40*2)-50*float64(dslen))/float64(dslen-1)+offset, (float64(diskcard.W())-40-100)*v.precent*0.01, 50, 12)
|
||||
diskcard.Fill()
|
||||
diskcard.ResetClip()
|
||||
|
||||
diskcard.SetColor(fontcolorswitch())
|
||||
|
||||
fw, _ := diskcard.MeasureString(v.name)
|
||||
fw1, _ := diskcard.MeasureString(v.text[0])
|
||||
|
||||
diskcard.DrawStringAnchored(v.name, 40+10+fw/2, 40+(float64(diskcardh-40*2)-50*float64(dslen))/float64(dslen-1)+50/2+offset, 0.5, 0.5)
|
||||
diskcard.DrawStringAnchored(v.text[0], (float64(diskcard.W())-100-10)-fw1/2, 40+(float64(diskcardh-40*2)-50*float64(dslen))/float64(dslen-1)+50/2+offset, 0.5, 0.5)
|
||||
diskcard.DrawStringAnchored(strconv.FormatFloat(v.precent, 'f', 0, 64)+"%", float64(diskcard.W())-100/2, 40+(float64(diskcardh-40*2)-50*float64(dslen))/float64(dslen-1)+50/2+offset, 0.5, 0.5)
|
||||
}
|
||||
}
|
||||
diskimg = diskcard.Image()
|
||||
}()
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
moreinfocard := gg.NewContext(cardw, moreinfocardh)
|
||||
bwg.Wait()
|
||||
|
||||
moreinfocard.DrawRoundedRectangle(1, 1, float64(moreinfocard.W()-1*2), float64(moreinfocard.H()-1*2), 16)
|
||||
moreinfocard.ClipPreserve()
|
||||
moreinfocard.DrawImage(blurback, -70, -70-titlecardh-40-basiccardh-40-diskcardh-40)
|
||||
moreinfocard.SetColor(colorswitch(140))
|
||||
moreinfocard.FillPreserve()
|
||||
|
||||
moreinfocard.SetLineWidth(3)
|
||||
moreinfocard.SetColor(colorswitch(100))
|
||||
moreinfocard.ResetClip()
|
||||
moreinfocard.Stroke()
|
||||
|
||||
err = moreinfocard.ParseFontFace(fontbyte, 32)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
milen := len(moreinfo)
|
||||
for i, v := range moreinfo {
|
||||
offset := float64(i)*(20+moreinfocard.FontHeight()) - 20
|
||||
|
||||
moreinfocard.SetColor(fontcolorswitch())
|
||||
|
||||
fw, _ := moreinfocard.MeasureString(v.name)
|
||||
fw1, _ := moreinfocard.MeasureString(v.text[0])
|
||||
|
||||
moreinfocard.DrawStringAnchored(v.name, 20+fw/2, 30+(float64(moreinfocardh-30*2)-moreinfocard.FontHeight()*float64(milen))/float64(milen-1)+moreinfocard.FontHeight()/2+offset, 0.5, 0.5)
|
||||
moreinfocard.DrawStringAnchored(v.text[0], float64(moreinfocard.W())-20-fw1/2, 30+(float64(moreinfocardh-30*2)-moreinfocard.FontHeight()*float64(milen))/float64(milen-1)+moreinfocard.FontHeight()/2+offset, 0.5, 0.5)
|
||||
}
|
||||
moreinfoimg = moreinfocard.Image()
|
||||
}()
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
shadow := gg.NewContext(canvas.W(), canvas.H())
|
||||
shadow.SetRGBA255(0, 0, 0, 100)
|
||||
shadow.SetLineWidth(12)
|
||||
shadow.DrawRoundedRectangle(70, 70, float64(cardw), float64(titlecardh), 16)
|
||||
shadow.Stroke()
|
||||
shadow.DrawRoundedRectangle(70, float64(70+titlecardh+40), float64(cardw), float64(basiccardh), 16)
|
||||
shadow.Stroke()
|
||||
shadow.DrawRoundedRectangle(70, float64(70+titlecardh+40+basiccardh+40), float64(cardw), float64(diskcardh), 16)
|
||||
shadow.Stroke()
|
||||
shadow.DrawRoundedRectangle(70, float64(70+titlecardh+40+basiccardh+40+diskcardh+40), float64(cardw), float64(moreinfocardh), 16)
|
||||
shadow.Stroke()
|
||||
shadowimg = imaging.Blur(shadow.Image(), 24)
|
||||
}()
|
||||
|
||||
wg.Wait()
|
||||
if shadowimg == nil || titleimg == nil || basicimg == nil || diskimg == nil || moreinfoimg == nil {
|
||||
err = errors.New("图片渲染失败")
|
||||
return
|
||||
}
|
||||
canvas.DrawImage(shadowimg, 0, 0)
|
||||
canvas.DrawImage(titleimg, 70, 70)
|
||||
canvas.DrawImage(basicimg, 70, 70+titlecardh+40)
|
||||
canvas.DrawImage(diskimg, 70, 70+titlecardh+40+basiccardh+40)
|
||||
canvas.DrawImage(moreinfoimg, 70, 70+titlecardh+40+basiccardh+40+diskcardh+40)
|
||||
|
||||
err = canvas.ParseFontFace(fontbyte, 28)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
canvas.SetRGBA255(0, 0, 0, 255)
|
||||
canvas.DrawStringAnchored("Created By ZeroBot-Plugin "+banner.Version, float64(canvas.W())/2+3, float64(canvas.H())-70/2+3, 0.5, 0.5)
|
||||
canvas.SetRGBA255(255, 255, 255, 255)
|
||||
canvas.DrawStringAnchored("Created By ZeroBot-Plugin "+banner.Version, float64(canvas.W())/2, float64(canvas.H())-70/2, 0.5, 0.5)
|
||||
|
||||
sendimg = canvas.Image()
|
||||
return
|
||||
}
|
||||
|
||||
func botruntime() (string, error) {
|
||||
hostinfo, err := host.Info()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
t := &strings.Builder{}
|
||||
t.WriteString("ZeroBot-Plugin 已运行 ")
|
||||
t.WriteString(strconv.FormatInt((time.Now().Unix()-boottime.Unix())/86400, 10))
|
||||
t.WriteString(" 天 ")
|
||||
t.WriteString(time.Unix(time.Now().Unix()-boottime.Unix(), 0).UTC().Format("15:04:05"))
|
||||
t.WriteString(" | 系统运行 ")
|
||||
t.WriteString(strconv.FormatInt(int64(hostinfo.Uptime)/86400, 10))
|
||||
t.WriteString(" 天 ")
|
||||
t.WriteString(time.Unix(int64(hostinfo.Uptime), 0).UTC().Format("15:04:05"))
|
||||
return t.String(), nil
|
||||
}
|
||||
|
||||
func botstatus() (string, error) {
|
||||
hostinfo, err := host.Info()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
t := &strings.Builder{}
|
||||
t.WriteString(time.Now().Format("2006-01-02 15:04:05"))
|
||||
t.WriteString(" | Compiled by ")
|
||||
t.WriteString(runtime.Version())
|
||||
t.WriteString(" | ")
|
||||
t.WriteString(cases.Title(language.English).String(hostinfo.OS))
|
||||
t.WriteString(" ")
|
||||
t.WriteString(runtime.GOARCH)
|
||||
return t.String(), nil
|
||||
}
|
||||
|
||||
type status struct {
|
||||
precent float64
|
||||
name string
|
||||
text []string
|
||||
}
|
||||
|
||||
func basicstate() (stateinfo [3]*status, err error) {
|
||||
percent, err := cpu.Percent(time.Second, true)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
cpuinfo, err := cpu.Info()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
cpucore, err := cpu.Counts(false)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
cputhread, err := cpu.Counts(true)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
cores := strconv.Itoa(cpucore) + "C" + strconv.Itoa(cputhread) + "T"
|
||||
times := "最大 " + strconv.FormatFloat(cpuinfo[0].Mhz/1000, 'f', 1, 64) + "Ghz"
|
||||
|
||||
stateinfo[0] = &status{
|
||||
precent: math.Round(percent[0]),
|
||||
name: "CPU",
|
||||
text: []string{cores, times},
|
||||
}
|
||||
|
||||
raminfo, err := mem.VirtualMemory()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
total := "总共 " + storagefmt(float64(raminfo.Total))
|
||||
used := "已用 " + storagefmt(float64(raminfo.Used))
|
||||
free := "剩余 " + storagefmt(float64(raminfo.Free))
|
||||
|
||||
stateinfo[1] = &status{
|
||||
precent: math.Round(raminfo.UsedPercent),
|
||||
name: "RAM",
|
||||
text: []string{total, used, free},
|
||||
}
|
||||
|
||||
swapinfo, err := mem.SwapMemory()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
total = "总共 " + storagefmt(float64(swapinfo.Total))
|
||||
used = "已用 " + storagefmt(float64(swapinfo.Used))
|
||||
free = "剩余 " + storagefmt(float64(swapinfo.Free))
|
||||
|
||||
stateinfo[2] = &status{
|
||||
precent: math.Round(swapinfo.UsedPercent),
|
||||
name: "SWAP",
|
||||
text: []string{total, used, free},
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func storagefmt(num float64) string {
|
||||
if num /= 1024; num < 1 {
|
||||
return strconv.FormatFloat(num*1024, 'f', 2, 64) + "B"
|
||||
}
|
||||
if num /= 1024; num < 1 {
|
||||
return strconv.FormatFloat(num*1024, 'f', 2, 64) + "KB"
|
||||
}
|
||||
if num /= 1024; num < 1 {
|
||||
return strconv.FormatFloat(num*1024, 'f', 2, 64) + "MB"
|
||||
}
|
||||
if num /= 1024; num < 1 {
|
||||
return strconv.FormatFloat(num*1024, 'f', 2, 64) + "GB"
|
||||
}
|
||||
return strconv.FormatFloat(num, 'f', 2, 64) + "TB"
|
||||
}
|
||||
|
||||
func diskstate() (stateinfo []*status, err error) {
|
||||
parts, err := disk.Partitions(false)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
stateinfo = make([]*status, 0, len(parts))
|
||||
for _, v := range parts {
|
||||
mp := v.Mountpoint
|
||||
if strings.HasPrefix(mp, "/snap/") || strings.HasPrefix(mp, "/apex/") {
|
||||
continue
|
||||
}
|
||||
diskusage, err := disk.Usage(mp)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
stateinfo = append(stateinfo, &status{
|
||||
precent: math.Round(diskusage.UsedPercent),
|
||||
name: mp,
|
||||
text: []string{storagefmt(float64(diskusage.Used)) + " / " + storagefmt(float64(diskusage.Total))},
|
||||
})
|
||||
}
|
||||
return stateinfo, nil
|
||||
}
|
||||
|
||||
func moreinfo(m *ctrl.Control[*zero.Ctx]) (stateinfo []*status, err error) {
|
||||
var mems runtime.MemStats
|
||||
runtime.ReadMemStats(&mems)
|
||||
fmtmem := storagefmt(float64(mems.Alloc))
|
||||
|
||||
hostinfo, err := host.Info()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
cpuinfo, err := cpu.Info()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
count := len(m.Manager.M)
|
||||
stateinfo = []*status{
|
||||
{name: "OS", text: []string{hostinfo.Platform}},
|
||||
{name: "CPU", text: []string{strings.TrimSpace(cpuinfo[0].ModelName)}},
|
||||
{name: "Version", text: []string{hostinfo.PlatformVersion}},
|
||||
{name: "Plugin", text: []string{"共 " + strconv.Itoa(count) + " 个"}},
|
||||
{name: "Memory", text: []string{"已用 " + fmtmem}},
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func colorswitch(a uint8) color.Color {
|
||||
if isday {
|
||||
return color.NRGBA{255, 255, 255, a}
|
||||
}
|
||||
return color.NRGBA{0, 0, 0, a}
|
||||
}
|
||||
|
||||
func fontcolorswitch() color.Color {
|
||||
if isday {
|
||||
return color.NRGBA{30, 30, 30, 255}
|
||||
}
|
||||
return color.NRGBA{235, 235, 235, 255}
|
||||
}
|
||||
|
||||
func slice2color(c [4]uint8) color.Color {
|
||||
return color.NRGBA{c[0], c[1], c[2], c[3]}
|
||||
}
|
||||
56
plugin/aiimage/config.go
Normal file
56
plugin/aiimage/config.go
Normal file
@@ -0,0 +1,56 @@
|
||||
// Package aiimage 提供AI画图功能配置
|
||||
package aiimage
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
sql "github.com/FloatTech/sqlite"
|
||||
)
|
||||
|
||||
// storage 管理画图配置存储
|
||||
type storage struct {
|
||||
sync.RWMutex
|
||||
db sql.Sqlite
|
||||
}
|
||||
|
||||
// imageConfig 存储AI画图配置信息
|
||||
type imageConfig struct {
|
||||
ID int64 `db:"id"` // 主键ID
|
||||
APIKey string `db:"apiKey"` // API密钥
|
||||
APIURL string `db:"apiUrl"` // API地址
|
||||
ModelName string `db:"modelName"` // 画图模型名称
|
||||
}
|
||||
|
||||
// getConfig 获取当前配置
|
||||
func (sdb *storage) getConfig() imageConfig {
|
||||
sdb.RLock()
|
||||
defer sdb.RUnlock()
|
||||
cfg := imageConfig{}
|
||||
_ = sdb.db.Find("config", &cfg, "WHERE id = 1")
|
||||
return cfg
|
||||
}
|
||||
|
||||
// setConfig 设置AI画图配置
|
||||
func (sdb *storage) setConfig(apiKey, apiURL, modelName string) error {
|
||||
sdb.Lock()
|
||||
defer sdb.Unlock()
|
||||
return sdb.db.Insert("config", &imageConfig{
|
||||
ID: 1,
|
||||
APIKey: apiKey,
|
||||
APIURL: apiURL,
|
||||
ModelName: modelName,
|
||||
})
|
||||
}
|
||||
|
||||
// PrintConfig 返回格式化后的配置信息
|
||||
func (sdb *storage) PrintConfig() string {
|
||||
cfg := sdb.getConfig()
|
||||
var builder strings.Builder
|
||||
builder.WriteString("当前AI画图配置:\n")
|
||||
builder.WriteString(fmt.Sprintf("• 密钥: %s\n", cfg.APIKey))
|
||||
builder.WriteString(fmt.Sprintf("• 接口地址: %s\n", cfg.APIURL))
|
||||
builder.WriteString(fmt.Sprintf("• 模型名: %s\n", cfg.ModelName))
|
||||
return builder.String()
|
||||
}
|
||||
171
plugin/aiimage/main.go
Normal file
171
plugin/aiimage/main.go
Normal file
@@ -0,0 +1,171 @@
|
||||
// Package aiimage AI画图
|
||||
package aiimage
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
fcext "github.com/FloatTech/floatbox/ctxext"
|
||||
"github.com/FloatTech/floatbox/web"
|
||||
sql "github.com/FloatTech/sqlite"
|
||||
"github.com/tidwall/gjson"
|
||||
zero "github.com/wdvxdr1123/ZeroBot"
|
||||
"github.com/wdvxdr1123/ZeroBot/message"
|
||||
|
||||
ctrl "github.com/FloatTech/zbpctrl"
|
||||
"github.com/FloatTech/zbputils/control"
|
||||
"github.com/FloatTech/zbputils/ctxext"
|
||||
)
|
||||
|
||||
func init() {
|
||||
var sdb = &storage{}
|
||||
|
||||
en := control.AutoRegister(&ctrl.Options[*zero.Ctx]{
|
||||
DisableOnDefault: false,
|
||||
Extra: control.ExtraFromString("aiimage"),
|
||||
Brief: "AI画图",
|
||||
Help: "- 设置AI画图密钥xxx\n" +
|
||||
"- 设置AI画图接口地址https://api.siliconflow.cn/v1/images/generations\n" +
|
||||
"- 设置AI画图模型名Kwai-Kolors/Kolors\n" +
|
||||
"- 查看AI画图配置\n" +
|
||||
"- AI画图 [描述]",
|
||||
PrivateDataFolder: "aiimage",
|
||||
})
|
||||
|
||||
getdb := fcext.DoOnceOnSuccess(func(ctx *zero.Ctx) bool {
|
||||
sdb.db = sql.New(en.DataFolder() + "aiimage.db")
|
||||
err := sdb.db.Open(time.Hour)
|
||||
if err == nil {
|
||||
// 创建配置表
|
||||
err = sdb.db.Create("config", &imageConfig{})
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("[ERROR]:", err))
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
ctx.SendChain(message.Text("[ERROR]:", err))
|
||||
return false
|
||||
})
|
||||
|
||||
en.OnPrefix("设置AI画图密钥", getdb, zero.OnlyPrivate, zero.SuperUserPermission).SetBlock(true).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
apiKey := strings.TrimSpace(ctx.State["args"].(string))
|
||||
cfg := sdb.getConfig()
|
||||
err := sdb.setConfig(apiKey, cfg.APIURL, cfg.ModelName)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: 设置API密钥失败: ", err))
|
||||
return
|
||||
}
|
||||
ctx.SendChain(message.Text("成功设置API密钥"))
|
||||
})
|
||||
|
||||
en.OnPrefix("设置AI画图接口地址", getdb, zero.OnlyPrivate, zero.SuperUserPermission).SetBlock(true).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
apiURL := strings.TrimSpace(ctx.State["args"].(string))
|
||||
cfg := sdb.getConfig()
|
||||
err := sdb.setConfig(cfg.APIKey, apiURL, cfg.ModelName)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: 设置API地址失败: ", err))
|
||||
return
|
||||
}
|
||||
ctx.SendChain(message.Text("成功设置API地址"))
|
||||
})
|
||||
|
||||
en.OnPrefix("设置AI画图模型名", getdb, zero.OnlyPrivate, zero.SuperUserPermission).SetBlock(true).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
modelName := strings.TrimSpace(ctx.State["args"].(string))
|
||||
cfg := sdb.getConfig()
|
||||
err := sdb.setConfig(cfg.APIKey, cfg.APIURL, modelName)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: 设置模型失败: ", err))
|
||||
return
|
||||
}
|
||||
ctx.SendChain(message.Text("成功设置模型: ", modelName))
|
||||
})
|
||||
|
||||
en.OnFullMatch("查看AI画图配置", getdb, zero.OnlyPrivate, zero.SuperUserPermission).SetBlock(true).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
ctx.SendChain(message.Text(sdb.PrintConfig()))
|
||||
})
|
||||
|
||||
en.OnPrefix("AI画图", getdb).SetBlock(true).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
ctx.SendChain(message.Text("少女思考中..."))
|
||||
prompt := strings.TrimSpace(ctx.State["args"].(string))
|
||||
if prompt == "" {
|
||||
ctx.SendChain(message.Text("请输入图片描述"))
|
||||
return
|
||||
}
|
||||
|
||||
cfg := sdb.getConfig()
|
||||
if cfg.APIKey == "" || cfg.APIURL == "" || cfg.ModelName == "" {
|
||||
ctx.SendChain(message.Text("请先配置API密钥、地址和模型"))
|
||||
return
|
||||
}
|
||||
|
||||
// 准备请求数据
|
||||
reqBytes, _ := json.Marshal(map[string]interface{}{
|
||||
"model": cfg.ModelName,
|
||||
"prompt": prompt,
|
||||
"image_size": "1024x1024",
|
||||
"batch_size": 4,
|
||||
"num_inference_steps": 20,
|
||||
"guidance_scale": 7.5,
|
||||
})
|
||||
|
||||
// 发送API请求
|
||||
data, err := web.RequestDataWithHeaders(
|
||||
web.NewDefaultClient(),
|
||||
cfg.APIURL,
|
||||
"POST",
|
||||
func(req *http.Request) error {
|
||||
req.Header.Set("Authorization", "Bearer "+cfg.APIKey)
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
return nil
|
||||
},
|
||||
bytes.NewReader(reqBytes),
|
||||
)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("API请求失败: ", err))
|
||||
return
|
||||
}
|
||||
|
||||
// 解析API响应
|
||||
jsonData := gjson.ParseBytes(data)
|
||||
images := jsonData.Get("images")
|
||||
if !images.Exists() {
|
||||
images = jsonData.Get("data")
|
||||
if !images.Exists() {
|
||||
ctx.SendChain(message.Text("未获取到图片URL"))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// 发送生成的图片和相关信息
|
||||
inferenceTime := jsonData.Get("timings.inference").Float()
|
||||
seed := jsonData.Get("seed").Int()
|
||||
msg := make(message.Message, 0, 1)
|
||||
msg = append(msg, ctxext.FakeSenderForwardNode(ctx, message.Text("图片生成成功!\n",
|
||||
"提示词: ", prompt, "\n",
|
||||
"模型: ", cfg.ModelName, "\n",
|
||||
"推理时间: ", inferenceTime, "秒\n",
|
||||
"种子: ", seed)))
|
||||
|
||||
// 添加所有图片
|
||||
images.ForEach(func(_, value gjson.Result) bool {
|
||||
url := value.Get("url").String()
|
||||
if url != "" {
|
||||
msg = append(msg, ctxext.FakeSenderForwardNode(ctx, message.Image(url)))
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
if len(msg) > 0 {
|
||||
ctx.Send(msg)
|
||||
}
|
||||
})
|
||||
}
|
||||
134
plugin/airecord/record.go
Normal file
134
plugin/airecord/record.go
Normal file
@@ -0,0 +1,134 @@
|
||||
// Package airecord 群应用:AI声聊
|
||||
package airecord
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/tidwall/gjson"
|
||||
|
||||
zero "github.com/wdvxdr1123/ZeroBot"
|
||||
"github.com/wdvxdr1123/ZeroBot/message"
|
||||
|
||||
"github.com/FloatTech/AnimeAPI/airecord"
|
||||
ctrl "github.com/FloatTech/zbpctrl"
|
||||
"github.com/FloatTech/zbputils/control"
|
||||
)
|
||||
|
||||
func init() {
|
||||
en := control.AutoRegister(&ctrl.Options[*zero.Ctx]{
|
||||
DisableOnDefault: false,
|
||||
Extra: control.ExtraFromString("airecord"),
|
||||
Brief: "群应用:AI声聊",
|
||||
Help: "- 设置AI语音群号1048452984(tips:机器人任意所在群聊即可)\n" +
|
||||
"- 设置AI语音模型\n" +
|
||||
"- 查看AI语音配置\n" +
|
||||
"- 发送AI语音xxx",
|
||||
PrivateDataFolder: "airecord",
|
||||
})
|
||||
|
||||
en.OnPrefix("设置AI语音群号", zero.OnlyPrivate, zero.SuperUserPermission).SetBlock(true).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
u := strings.TrimSpace(ctx.State["args"].(string))
|
||||
num, err := strconv.ParseInt(u, 10, 64)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: parse gid err: ", err))
|
||||
return
|
||||
}
|
||||
err = airecord.SetCustomGID(num)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: set gid err: ", err))
|
||||
return
|
||||
}
|
||||
ctx.SendChain(message.Text("设置AI语音群号为", num))
|
||||
})
|
||||
en.OnFullMatch("设置AI语音模型", zero.OnlyPrivate, zero.SuperUserPermission).SetBlock(true).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
next := zero.NewFutureEvent("message", 999, false, ctx.CheckSession())
|
||||
recv, cancel := next.Repeat()
|
||||
defer cancel()
|
||||
jsonData := ctx.GetAICharacters(0, 1)
|
||||
|
||||
// 转换为字符串数组
|
||||
var names []string
|
||||
// 初始化两个映射表
|
||||
nameToID := make(map[string]string)
|
||||
nameToURL := make(map[string]string)
|
||||
characters := jsonData.Get("#.characters")
|
||||
|
||||
// 遍历每个角色对象
|
||||
characters.ForEach(func(_, group gjson.Result) bool {
|
||||
group.ForEach(func(_, character gjson.Result) bool {
|
||||
// 提取当前角色的三个字段
|
||||
name := character.Get("character_name").String()
|
||||
names = append(names, name)
|
||||
// 存入映射表(重复名称会覆盖,保留最后出现的条目)
|
||||
nameToID[name] = character.Get("character_id").String()
|
||||
nameToURL[name] = character.Get("preview_url").String()
|
||||
return true // 继续遍历
|
||||
})
|
||||
return true // 继续遍历
|
||||
})
|
||||
var builder strings.Builder
|
||||
// 写入开头文本
|
||||
builder.WriteString("请选择语音模型序号:\n")
|
||||
|
||||
// 遍历names数组,拼接序号和名称
|
||||
for i, v := range names {
|
||||
// 将数字转换为字符串(不依赖fmt)
|
||||
numStr := strconv.Itoa(i)
|
||||
// 拼接格式:"序号. 名称\n"
|
||||
builder.WriteString(numStr)
|
||||
builder.WriteString(". ")
|
||||
builder.WriteString(v)
|
||||
builder.WriteString("\n")
|
||||
}
|
||||
// 获取最终字符串
|
||||
ctx.SendChain(message.Text(builder.String()))
|
||||
for {
|
||||
select {
|
||||
case <-time.After(time.Second * 120):
|
||||
ctx.SendChain(message.Text("设置AI语音模型指令过期"))
|
||||
return
|
||||
case ct := <-recv:
|
||||
msg := ct.Event.Message.ExtractPlainText()
|
||||
num, err := strconv.Atoi(msg)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("请输入数字!"))
|
||||
continue
|
||||
}
|
||||
if num < 0 || num >= len(names) {
|
||||
ctx.SendChain(message.Text("序号非法!"))
|
||||
continue
|
||||
}
|
||||
err = airecord.SetRecordModel(names[num], nameToID[names[num]])
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: set model err: ", err))
|
||||
continue
|
||||
}
|
||||
ctx.SendChain(message.Text("已选择语音模型: ", names[num]))
|
||||
ctx.SendChain(message.Record(nameToURL[names[num]]))
|
||||
return
|
||||
}
|
||||
}
|
||||
})
|
||||
en.OnFullMatch("查看AI语音配置", zero.OnlyPrivate, zero.SuperUserPermission).SetBlock(true).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
ctx.SendChain(message.Text(airecord.PrintRecordConfig()))
|
||||
})
|
||||
en.OnPrefix("发送AI语音", zero.UserOrGrpAdmin).SetBlock(true).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
u := strings.TrimSpace(ctx.State["args"].(string))
|
||||
recCfg := airecord.GetConfig()
|
||||
record := ctx.GetAIRecord(recCfg.ModelID, recCfg.Customgid, u)
|
||||
if record == "" {
|
||||
id := ctx.SendGroupAIRecord(recCfg.ModelID, ctx.Event.GroupID, u)
|
||||
if id == "" {
|
||||
ctx.SendChain(message.Text("ERROR: get record err: empty record"))
|
||||
return
|
||||
}
|
||||
}
|
||||
ctx.SendChain(message.Record(record))
|
||||
})
|
||||
}
|
||||
@@ -5,12 +5,11 @@ import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
|
||||
control "github.com/FloatTech/zbputils/control"
|
||||
ctrl "github.com/FloatTech/zbpctrl"
|
||||
"github.com/FloatTech/zbputils/control"
|
||||
"github.com/FloatTech/zbputils/ctxext"
|
||||
zero "github.com/wdvxdr1123/ZeroBot"
|
||||
"github.com/wdvxdr1123/ZeroBot/message"
|
||||
|
||||
"github.com/FloatTech/zbputils/control/order"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -18,10 +17,10 @@ const (
|
||||
)
|
||||
|
||||
func init() { // 插件主体
|
||||
control.Register("aiwife", order.AcquirePrio(), &control.Options{
|
||||
control.AutoRegister(&ctrl.Options[*zero.Ctx]{
|
||||
DisableOnDefault: false,
|
||||
Help: "AIWife\n" +
|
||||
"- waifu | 随机waifu",
|
||||
Brief: "ai随机生成老婆",
|
||||
Help: "- waifu | 随机waifu",
|
||||
}).ApplySingle(ctxext.DefaultSingle).OnFullMatchGroup([]string{"waifu", "随机waifu"}).SetBlock(true).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
miku := rand.Intn(100000) + 1
|
||||
|
||||
32
plugin/alipayvoice/alipayvoice.go
Normal file
32
plugin/alipayvoice/alipayvoice.go
Normal file
@@ -0,0 +1,32 @@
|
||||
// Package alipayvoice 支付宝到账语音
|
||||
package alipayvoice
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
ctrl "github.com/FloatTech/zbpctrl"
|
||||
"github.com/FloatTech/zbputils/control"
|
||||
zero "github.com/wdvxdr1123/ZeroBot"
|
||||
"github.com/wdvxdr1123/ZeroBot/message"
|
||||
)
|
||||
|
||||
const (
|
||||
alipayvoiceURL = "https://mm.cqu.cc/share/zhifubaodaozhang/mp3/%v.mp3"
|
||||
)
|
||||
|
||||
func init() { // 插件主体
|
||||
engine := control.AutoRegister(&ctrl.Options[*zero.Ctx]{
|
||||
DisableOnDefault: false,
|
||||
Brief: "支付宝到账语音",
|
||||
Help: "- 支付宝到账 1",
|
||||
PrivateDataFolder: "alipayvoice",
|
||||
})
|
||||
|
||||
// 开启
|
||||
engine.OnPrefix(`支付宝到账`).SetBlock(true).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
args := ctx.State["args"].(string)
|
||||
ctx.SendChain(message.Record(fmt.Sprintf(alipayvoiceURL, strings.TrimSpace(args))))
|
||||
})
|
||||
}
|
||||
145
plugin/animetrace/main.go
Normal file
145
plugin/animetrace/main.go
Normal file
@@ -0,0 +1,145 @@
|
||||
// Package animetrace AnimeTrace 动画/Galgame识别
|
||||
package animetrace
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"image"
|
||||
"image/jpeg"
|
||||
"mime/multipart"
|
||||
"strings"
|
||||
|
||||
"github.com/FloatTech/floatbox/web"
|
||||
"github.com/FloatTech/imgfactory"
|
||||
ctrl "github.com/FloatTech/zbpctrl"
|
||||
"github.com/FloatTech/zbputils/control"
|
||||
"github.com/FloatTech/zbputils/ctxext"
|
||||
"github.com/disintegration/imaging"
|
||||
"github.com/tidwall/gjson"
|
||||
zero "github.com/wdvxdr1123/ZeroBot"
|
||||
"github.com/wdvxdr1123/ZeroBot/message"
|
||||
)
|
||||
|
||||
func init() {
|
||||
engine := control.AutoRegister(&ctrl.Options[*zero.Ctx]{
|
||||
DisableOnDefault: false,
|
||||
Brief: "AnimeTrace 动画/Galgame识别插件",
|
||||
Help: "- Gal识图\n- 动漫识图\n- 动漫识图 2\n- 动漫识图 [模型名]\n- Gal识图 [模型名]",
|
||||
})
|
||||
|
||||
engine.OnPrefix("gal识图", zero.OnlyGroup, zero.MustProvidePicture).SetBlock(true).Handle(func(ctx *zero.Ctx) {
|
||||
args := ctx.State["args"].(string)
|
||||
var model string
|
||||
switch strings.TrimSpace(args) {
|
||||
case "":
|
||||
model = "full_game_model_kira" // 默认使用的模型
|
||||
default:
|
||||
model = args // 自定义设置模型
|
||||
}
|
||||
processImageRecognition(ctx, model)
|
||||
})
|
||||
|
||||
engine.OnPrefix("动漫识图", zero.OnlyGroup, zero.MustProvidePicture).SetBlock(true).Handle(func(ctx *zero.Ctx) {
|
||||
args := ctx.State["args"].(string)
|
||||
var model string
|
||||
switch strings.TrimSpace(args) {
|
||||
case "":
|
||||
model = "anime_model_lovelive"
|
||||
case "2":
|
||||
model = "pre_stable"
|
||||
default:
|
||||
model = args
|
||||
}
|
||||
processImageRecognition(ctx, model)
|
||||
})
|
||||
}
|
||||
|
||||
// 处理图片识别
|
||||
func processImageRecognition(ctx *zero.Ctx, model string) {
|
||||
urls := ctx.State["image_url"].([]string)
|
||||
if len(urls) == 0 {
|
||||
return
|
||||
}
|
||||
imageData, err := imgfactory.Load(urls[0])
|
||||
if err != nil {
|
||||
ctx.Send(message.Text("下载图片失败: ", err))
|
||||
return
|
||||
}
|
||||
// ctx.Send(message.Text(model))
|
||||
respBody, err := createAndSendMultipartRequest("https://api.animetrace.com/v1/search", imageData, map[string]string{
|
||||
"is_multi": "0",
|
||||
"model": model,
|
||||
"ai_detect": "0",
|
||||
})
|
||||
if err != nil {
|
||||
ctx.Send(message.Text("识别请求失败: ", err))
|
||||
return
|
||||
}
|
||||
code := gjson.Get(string(respBody), "code").Int()
|
||||
if code != 0 {
|
||||
ctx.Send(message.Text("错误: ", gjson.Get(string(respBody), "zh_message").String()))
|
||||
return
|
||||
}
|
||||
dataArray := gjson.Get(string(respBody), "data").Array()
|
||||
if len(dataArray) == 0 {
|
||||
ctx.Send(message.Text("未识别到任何角色"))
|
||||
return
|
||||
}
|
||||
var sk message.Message
|
||||
sk = append(sk, ctxext.FakeSenderForwardNode(ctx, message.Text("共识别到 ", len(dataArray), " 个角色,可能是以下来源")))
|
||||
for _, value := range dataArray {
|
||||
boxArray := value.Get("box").Array()
|
||||
imgWidth, imgHeight := imageData.Bounds().Dx(), imageData.Bounds().Dy() // 你可以从 `imageData.Bounds()` 获取
|
||||
box := []int{
|
||||
int(boxArray[0].Float() * float64(imgWidth)),
|
||||
int(boxArray[1].Float() * float64(imgHeight)),
|
||||
int(boxArray[2].Float() * float64(imgWidth)),
|
||||
int(boxArray[3].Float() * float64(imgHeight)),
|
||||
}
|
||||
croppedImg := imaging.Crop(imageData, image.Rect(box[0], box[1], box[2], box[3]))
|
||||
var buf bytes.Buffer
|
||||
if err := imaging.Encode(&buf, croppedImg, imaging.JPEG, imaging.JPEGQuality(80)); err != nil {
|
||||
ctx.Send(message.Text("图片编码失败: ", err))
|
||||
continue
|
||||
}
|
||||
|
||||
base64Str := base64.StdEncoding.EncodeToString(buf.Bytes())
|
||||
var sb strings.Builder
|
||||
value.Get("character").ForEach(func(_, character gjson.Result) bool {
|
||||
sb.WriteString(fmt.Sprintf("《%s》的角色 %s\n", character.Get("work").String(), character.Get("character").String()))
|
||||
return true
|
||||
})
|
||||
sk = append(sk, ctxext.FakeSenderForwardNode(ctx, message.Image("base64://"+base64Str), message.Text(sb.String())))
|
||||
}
|
||||
ctx.SendGroupForwardMessage(ctx.Event.GroupID, sk)
|
||||
}
|
||||
|
||||
// 发送图片识别请求
|
||||
func createAndSendMultipartRequest(url string, img image.Image, formFields map[string]string) ([]byte, error) {
|
||||
body := &bytes.Buffer{}
|
||||
writer := multipart.NewWriter(body)
|
||||
|
||||
// 直接编码图片
|
||||
part, err := writer.CreateFormFile("file", "image.jpg")
|
||||
if err != nil {
|
||||
return nil, errors.New("创建文件字段失败: " + err.Error())
|
||||
}
|
||||
if err := jpeg.Encode(part, img, &jpeg.Options{Quality: 80}); err != nil {
|
||||
return nil, errors.New("图片编码失败: " + err.Error())
|
||||
}
|
||||
|
||||
// 写入其他字段
|
||||
for key, value := range formFields {
|
||||
if err := writer.WriteField(key, value); err != nil {
|
||||
return nil, errors.New("写入表单字段失败 (" + key + "): " + err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
if err := writer.Close(); err != nil {
|
||||
return nil, errors.New("关闭 multipart writer 失败: " + err.Error())
|
||||
}
|
||||
|
||||
return web.PostData(url, writer.FormDataContentType(), body)
|
||||
}
|
||||
134
plugin/antiabuse/anti.go
Normal file
134
plugin/antiabuse/anti.go
Normal file
@@ -0,0 +1,134 @@
|
||||
// Package antiabuse defines antiabuse plugin ,support abuse words check and add/remove abuse words
|
||||
package antiabuse
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/FloatTech/floatbox/binary"
|
||||
fcext "github.com/FloatTech/floatbox/ctxext"
|
||||
"github.com/FloatTech/ttl"
|
||||
ctrl "github.com/FloatTech/zbpctrl"
|
||||
"github.com/FloatTech/zbputils/control"
|
||||
"github.com/FloatTech/zbputils/img/text"
|
||||
"github.com/sirupsen/logrus"
|
||||
zero "github.com/wdvxdr1123/ZeroBot"
|
||||
"github.com/wdvxdr1123/ZeroBot/message"
|
||||
)
|
||||
|
||||
const (
|
||||
bandur time.Duration = time.Minute * 2
|
||||
add = "添加违禁词"
|
||||
del = "删除违禁词"
|
||||
list = "查看违禁词"
|
||||
)
|
||||
|
||||
var (
|
||||
managers *ctrl.Manager[*zero.Ctx] // managers lazy load
|
||||
cache = ttl.NewCacheOn(bandur, [4]func(int64, struct{}){nil, nil, onDel, nil})
|
||||
db *antidb
|
||||
)
|
||||
|
||||
func onDel(uid int64, _ struct{}) {
|
||||
if managers == nil {
|
||||
return
|
||||
}
|
||||
if err := managers.DoUnblock(uid); err != nil {
|
||||
logrus.Errorln("[antiabuse.onDel] unblock:", err)
|
||||
}
|
||||
if err := db.Del("__bantime__", "WHERE id="+strconv.FormatInt(uid, 10)); err != nil {
|
||||
logrus.Errorln("[antiabuse.onDel] db:", err)
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
engine := control.AutoRegister(&ctrl.Options[*zero.Ctx]{
|
||||
DisableOnDefault: false,
|
||||
Brief: "违禁词检测",
|
||||
Help: "- [添加|删除|查看]违禁词",
|
||||
PrivateDataFolder: "anti_abuse",
|
||||
})
|
||||
|
||||
onceRule := fcext.DoOnceOnSuccess(func(ctx *zero.Ctx) bool {
|
||||
managers = ctx.State["manager"].(*ctrl.Control[*zero.Ctx]).Manager
|
||||
var err error
|
||||
db, err = newantidb(engine.DataFolder() + "anti_abuse.db")
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return false
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
notAntiabuse := func(ctx *zero.Ctx) bool {
|
||||
if zero.PrefixRule(add)(ctx) || zero.PrefixRule(del)(ctx) || zero.PrefixRule(list)(ctx) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
engine.OnMessage(onceRule, notAntiabuse, zero.OnlyGroup, func(ctx *zero.Ctx) bool {
|
||||
uid := ctx.Event.UserID
|
||||
gid := ctx.Event.GroupID
|
||||
msg := strings.ReplaceAll(ctx.MessageString(), "\n", "")
|
||||
msg = strings.ReplaceAll(msg, "\r", "")
|
||||
msg = strings.ReplaceAll(msg, "\t", "")
|
||||
msg = strings.ReplaceAll(msg, ";", "")
|
||||
if db.isInAntiList(gid, msg) {
|
||||
if err := ctx.State["manager"].(*ctrl.Control[*zero.Ctx]).Manager.DoBlock(uid); err == nil {
|
||||
t := time.Now().Unix()
|
||||
cache.Set(uid, struct{}{})
|
||||
ctx.SetThisGroupBan(uid, int64(bandur.Seconds()))
|
||||
ctx.DeleteMessage(ctx.Event.MessageID)
|
||||
ctx.SendChain(message.Text("检测到违禁词, 已封禁/屏蔽", bandur))
|
||||
db.Lock()
|
||||
defer db.Unlock()
|
||||
err := db.Create("__bantime__", nilbt)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return false
|
||||
}
|
||||
err = db.Insert("__bantime__", &banTime{ID: uid, Time: t})
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return false
|
||||
}
|
||||
} else {
|
||||
ctx.SendChain(message.Text("ERROR: block user: ", err))
|
||||
}
|
||||
return false
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
engine.OnPrefix(add, zero.OnlyGroup, zero.AdminPermission, onceRule).SetBlock(true).Handle(
|
||||
func(ctx *zero.Ctx) {
|
||||
args := strings.TrimSpace(ctx.State["args"].(string))
|
||||
if err := db.insertWord(ctx.Event.GroupID, args); err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
} else {
|
||||
ctx.SendChain(message.Text("成功"))
|
||||
}
|
||||
})
|
||||
|
||||
engine.OnPrefix(del, zero.OnlyGroup, zero.AdminPermission, onceRule).SetBlock(true).Handle(
|
||||
func(ctx *zero.Ctx) {
|
||||
args := strings.TrimSpace(ctx.State["args"].(string))
|
||||
if err := db.deleteWord(ctx.Event.GroupID, args); err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
} else {
|
||||
ctx.SendChain(message.Text("成功"))
|
||||
}
|
||||
})
|
||||
|
||||
engine.OnPrefix(list, zero.OnlyGroup, onceRule).SetBlock(true).Handle(
|
||||
func(ctx *zero.Ctx) {
|
||||
b, err := text.RenderToBase64(db.listWords(ctx.Event.GroupID), text.FontFile, 400, 20)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return
|
||||
}
|
||||
ctx.SendChain(message.Text("本群违禁词有\n"), message.Image("base64://"+binary.BytesToString(b)))
|
||||
})
|
||||
}
|
||||
102
plugin/antiabuse/db.go
Normal file
102
plugin/antiabuse/db.go
Normal file
@@ -0,0 +1,102 @@
|
||||
package antiabuse
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
sqlite "github.com/FloatTech/sqlite"
|
||||
)
|
||||
|
||||
type antidb struct {
|
||||
sync.RWMutex
|
||||
sqlite.Sqlite
|
||||
}
|
||||
|
||||
type banWord struct {
|
||||
Word string `db:"word"`
|
||||
}
|
||||
|
||||
type banTime struct {
|
||||
ID int64 `db:"id"`
|
||||
Time int64 `db:"time"`
|
||||
}
|
||||
|
||||
var (
|
||||
nilban = &banWord{}
|
||||
nilbt = &banTime{}
|
||||
)
|
||||
|
||||
func newantidb(path string) (*antidb, error) {
|
||||
db := &antidb{Sqlite: sqlite.New(path)}
|
||||
err := db.Open(bandur)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_ = db.FindFor("__bantime__", nilbt, "", func() error {
|
||||
t := time.Unix(nilbt.Time, 0)
|
||||
ttl := time.Until(t.Add(bandur))
|
||||
if ttl < time.Minute {
|
||||
_ = managers.DoUnblock(nilbt.ID)
|
||||
return nil
|
||||
}
|
||||
cache.Set(nilbt.ID, struct{}{})
|
||||
cache.Touch(nilbt.ID, -time.Since(t))
|
||||
return nil
|
||||
})
|
||||
_ = db.Del("__bantime__", "WHERE time <= ?", strconv.FormatInt(time.Now().Add(time.Minute-bandur).Unix(), 10))
|
||||
return db, nil
|
||||
}
|
||||
|
||||
func (db *antidb) isInAntiList(gid int64, msg string) bool {
|
||||
grp := strconv.FormatInt(gid, 36)
|
||||
db.RLock()
|
||||
defer db.RUnlock()
|
||||
return db.CanFind(grp, "WHERE instr('"+msg+"', word)>0")
|
||||
}
|
||||
|
||||
func (db *antidb) insertWord(gid int64, word string) error {
|
||||
grp := strconv.FormatInt(gid, 36)
|
||||
db.Lock()
|
||||
defer db.Unlock()
|
||||
err := db.Create(grp, nilban)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return db.Insert(grp, &banWord{Word: word})
|
||||
}
|
||||
|
||||
func (db *antidb) deleteWord(gid int64, word string) error {
|
||||
grp := strconv.FormatInt(gid, 36)
|
||||
db.Lock()
|
||||
defer db.Unlock()
|
||||
if n, _ := db.Count(grp); n == 0 {
|
||||
return errors.New("本群还没有违禁词~")
|
||||
}
|
||||
return db.Del(grp, "WHERE word='"+word+"'")
|
||||
}
|
||||
|
||||
func (db *antidb) listWords(gid int64) string {
|
||||
grp := strconv.FormatInt(gid, 36)
|
||||
word := &banWord{}
|
||||
sb := strings.Builder{}
|
||||
sb.WriteByte('[')
|
||||
i := 0
|
||||
db.RLock()
|
||||
defer db.RUnlock()
|
||||
_ = db.FindFor(grp, word, "", func() error {
|
||||
if i > 0 {
|
||||
sb.WriteString(" | ")
|
||||
}
|
||||
sb.WriteString(word.Word)
|
||||
i++
|
||||
return nil
|
||||
})
|
||||
if sb.Len() <= 4 {
|
||||
return "[]"
|
||||
}
|
||||
sb.WriteByte(']')
|
||||
return sb.String()
|
||||
}
|
||||
@@ -6,77 +6,87 @@ Package atri 本文件基于 https://github.com/Kyomotoi/ATRI
|
||||
package atri
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"math/rand"
|
||||
"time"
|
||||
|
||||
zero "github.com/wdvxdr1123/ZeroBot"
|
||||
"github.com/wdvxdr1123/ZeroBot/message"
|
||||
|
||||
control "github.com/FloatTech/zbputils/control"
|
||||
"github.com/FloatTech/zbputils/process"
|
||||
|
||||
"github.com/FloatTech/zbputils/control/order"
|
||||
ctrl "github.com/FloatTech/zbpctrl"
|
||||
"github.com/FloatTech/zbputils/control"
|
||||
)
|
||||
|
||||
const (
|
||||
// 服务名
|
||||
servicename = "atri"
|
||||
// ATRI 表情的 codechina 镜像
|
||||
res = "https://gitcode.net/u011570312/zbpdata/-/raw/main/Atri/"
|
||||
)
|
||||
type datagetter func(string, bool) ([]byte, error)
|
||||
|
||||
func (dgtr datagetter) randImage(file ...string) message.Segment {
|
||||
data, err := dgtr(file[rand.Intn(len(file))], true)
|
||||
if err != nil {
|
||||
return message.Text("ERROR: ", err)
|
||||
}
|
||||
return message.ImageBytes(data)
|
||||
}
|
||||
|
||||
func (dgtr datagetter) randRecord(file ...string) message.Segment {
|
||||
data, err := dgtr(file[rand.Intn(len(file))], true)
|
||||
if err != nil {
|
||||
return message.Text("ERROR: ", err)
|
||||
}
|
||||
return message.Record("base64://" + base64.StdEncoding.EncodeToString(data))
|
||||
}
|
||||
|
||||
func randText(text ...string) message.Segment {
|
||||
return message.Text(text[rand.Intn(len(text))])
|
||||
}
|
||||
|
||||
// isAtriSleeping 凌晨0点到6点,ATRI 在睡觉,不回应任何请求
|
||||
func isAtriSleeping(*zero.Ctx) bool {
|
||||
if now := time.Now().Hour(); now >= 1 && now < 6 {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func init() { // 插件主体
|
||||
engine := control.Register(servicename, order.AcquirePrio(), &control.Options{
|
||||
engine := control.AutoRegister(&ctrl.Options[*zero.Ctx]{
|
||||
DisableOnDefault: false,
|
||||
Brief: "atri人格文本回复",
|
||||
Help: "本插件基于 ATRI ,为 Golang 移植版\n" +
|
||||
"- ATRI醒醒\n- ATRI睡吧\n- 萝卜子\n- 喜欢 | 爱你 | 爱 | suki | daisuki | すき | 好き | 贴贴 | 老婆 | 亲一个 | mua\n" +
|
||||
"- 草你妈 | 操你妈 | 脑瘫 | 废柴 | fw | 废物 | 战斗 | 爬 | 爪巴 | sb | SB | 傻B\n- 早安 | 早哇 | 早上好 | ohayo | 哦哈哟 | お早う | 早好 | 早 | 早早早\n" +
|
||||
"- 草你妈 | 操你妈 | 脑瘫 | 废柴 | fw | 废物 | 战斗 | 爬 | 爪巴 | sb | SB | 傻B\n- 早哇 | 早上好 | ohayo | 哦哈哟 | お早う | 早好 | 早 | 早早早\n" +
|
||||
"- 中午好 | 午安 | 午好\n- 晚安 | oyasuminasai | おやすみなさい | 晚好 | 晚上好\n- 高性能 | 太棒了 | すごい | sugoi | 斯国一 | よかった\n" +
|
||||
"- 没事 | 没关系 | 大丈夫 | 还好 | 不要紧 | 没出大问题 | 没伤到哪\n- 好吗 | 是吗 | 行不行 | 能不能 | 可不可以\n- 啊这\n- 我好了\n- ? | ? | ¿\n" +
|
||||
"- 离谱\n- 答应我",
|
||||
PublicDataFolder: "Atri",
|
||||
OnEnable: func(ctx *zero.Ctx) {
|
||||
ctx.SendChain(message.Text("嗯呜呜……夏生先生……?"))
|
||||
},
|
||||
OnDisable: func(ctx *zero.Ctx) {
|
||||
ctx.SendChain(message.Text("Zzz……Zzz……"))
|
||||
},
|
||||
})
|
||||
zero.OnFullMatch("ATRI醒醒", zero.AdminPermission).SetBlock(true).
|
||||
engine.UsePreHandler(isAtriSleeping)
|
||||
var dgtr datagetter = engine.GetLazyData
|
||||
engine.OnFullMatch("萝卜子").SetBlock(true).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
c, ok := control.Lookup(servicename)
|
||||
if ok && !c.IsEnabledIn(ctx.Event.GroupID) {
|
||||
c.Enable(ctx.Event.GroupID)
|
||||
process.SleepAbout1sTo2s()
|
||||
ctx.SendChain(message.Text("嗯呜呜……夏生先生……?"))
|
||||
}
|
||||
})
|
||||
engine.OnFullMatch("ATRI睡吧", zero.AdminPermission).SetBlock(true).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
c, ok := control.Lookup(servicename)
|
||||
if ok && c.IsEnabledIn(ctx.Event.GroupID) {
|
||||
c.Disable(ctx.Event.GroupID)
|
||||
process.SleepAbout1sTo2s()
|
||||
ctx.SendChain(message.Text("Zzz……Zzz……"))
|
||||
}
|
||||
})
|
||||
engine.OnFullMatch("萝卜子", atriSleep).SetBlock(true).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
process.SleepAbout1sTo2s()
|
||||
switch rand.Intn(2) {
|
||||
case 0:
|
||||
ctx.SendChain(randText("萝卜子是对机器人的蔑称!", "是亚托莉......萝卜子可是对机器人的蔑称"))
|
||||
case 1:
|
||||
ctx.SendChain(randRecord("RocketPunch.amr"))
|
||||
ctx.SendChain(dgtr.randRecord("RocketPunch.amr"))
|
||||
}
|
||||
})
|
||||
engine.OnFullMatchGroup([]string{"喜欢", "爱你", "爱", "suki", "daisuki", "すき", "好き", "贴贴", "老婆", "亲一个", "mua"}, atriSleep, zero.OnlyToMe).SetBlock(true).
|
||||
engine.OnFullMatchGroup([]string{"喜欢", "爱你", "爱", "suki", "daisuki", "すき", "好き", "贴贴", "老婆", "亲一个", "mua"}, zero.OnlyToMe).SetBlock(true).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
process.SleepAbout1sTo2s()
|
||||
ctx.SendChain(randImage("SUKI.jpg", "SUKI1.jpg", "SUKI2.png"))
|
||||
ctx.SendChain(dgtr.randImage("SUKI.jpg", "SUKI1.jpg", "SUKI2.png"))
|
||||
})
|
||||
engine.OnKeywordGroup([]string{"草你妈", "操你妈", "脑瘫", "废柴", "fw", "five", "废物", "战斗", "爬", "爪巴", "sb", "SB", "傻B"}, atriSleep, zero.OnlyToMe).SetBlock(true).
|
||||
engine.OnKeywordGroup([]string{"草你妈", "操你妈", "脑瘫", "废柴", "fw", "five", "废物", "战斗", "爬", "爪巴", "sb", "SB", "傻B"}, zero.OnlyToMe).SetBlock(true).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
process.SleepAbout1sTo2s()
|
||||
ctx.SendChain(randImage("FN.jpg", "WQ.jpg", "WQ1.jpg"))
|
||||
ctx.SendChain(dgtr.randImage("FN.jpg", "WQ.jpg", "WQ1.jpg"))
|
||||
})
|
||||
engine.OnFullMatchGroup([]string{"早安", "早哇", "早上好", "ohayo", "哦哈哟", "お早う", "早好", "早", "早早早"}).SetBlock(true).
|
||||
engine.OnFullMatchGroup([]string{"早哇", "早上好", "ohayo", "哦哈哟", "お早う", "早好", "早", "早早早"}).SetBlock(true).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
now := time.Now().Hour()
|
||||
process.SleepAbout1sTo2s()
|
||||
switch {
|
||||
case now < 6: // 凌晨
|
||||
ctx.SendChain(message.Reply(ctx.Event.MessageID), randText(
|
||||
@@ -117,7 +127,6 @@ func init() { // 插件主体
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
now := time.Now().Hour()
|
||||
if now > 11 && now < 15 { // 中午
|
||||
process.SleepAbout1sTo2s()
|
||||
ctx.SendChain(message.Reply(ctx.Event.MessageID), randText(
|
||||
"午安w",
|
||||
"午觉要好好睡哦,ATRI会陪伴在你身旁的w",
|
||||
@@ -129,7 +138,6 @@ func init() { // 插件主体
|
||||
engine.OnFullMatchGroup([]string{"晚安", "oyasuminasai", "おやすみなさい", "晚好", "晚上好"}).SetBlock(true).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
now := time.Now().Hour()
|
||||
process.SleepAbout1sTo2s()
|
||||
switch {
|
||||
case now < 6: // 凌晨
|
||||
ctx.SendChain(message.Reply(ctx.Event.MessageID), randText(
|
||||
@@ -169,9 +177,8 @@ func init() { // 插件主体
|
||||
))
|
||||
}
|
||||
})
|
||||
engine.OnKeywordGroup([]string{"高性能", "太棒了", "すごい", "sugoi", "斯国一", "よかった"}, atriSleep, zero.OnlyToMe).SetBlock(true).
|
||||
engine.OnKeywordGroup([]string{"高性能", "太棒了", "すごい", "sugoi", "斯国一", "よかった"}, zero.OnlyToMe).SetBlock(true).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
process.SleepAbout1sTo2s()
|
||||
ctx.SendChain(randText(
|
||||
"当然,我是高性能的嘛~!",
|
||||
"小事一桩,我是高性能的嘛",
|
||||
@@ -190,9 +197,8 @@ func init() { // 插件主体
|
||||
"呣......我的高性能,毫无遗憾地施展出来了......",
|
||||
))
|
||||
})
|
||||
engine.OnKeywordGroup([]string{"没事", "没关系", "大丈夫", "还好", "不要紧", "没出大问题", "没伤到哪"}, atriSleep, zero.OnlyToMe).SetBlock(true).
|
||||
engine.OnKeywordGroup([]string{"没事", "没关系", "大丈夫", "还好", "不要紧", "没出大问题", "没伤到哪"}, zero.OnlyToMe).SetBlock(true).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
process.SleepAbout1sTo2s()
|
||||
ctx.SendChain(randText(
|
||||
"当然,我是高性能的嘛~!",
|
||||
"没事没事,因为我是高性能的嘛!嗯哼!",
|
||||
@@ -205,67 +211,42 @@ func init() { // 插件主体
|
||||
))
|
||||
})
|
||||
|
||||
engine.OnKeywordGroup([]string{"好吗", "是吗", "行不行", "能不能", "可不可以"}, atriSleep).SetBlock(true).
|
||||
engine.OnKeywordGroup([]string{"好吗", "是吗", "行不行", "能不能", "可不可以"}).SetBlock(true).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
process.SleepAbout1sTo2s()
|
||||
if rand.Intn(2) == 0 {
|
||||
ctx.SendChain(randImage("YES.png", "NO.jpg"))
|
||||
ctx.SendChain(dgtr.randImage("YES.png", "NO.jpg"))
|
||||
}
|
||||
})
|
||||
engine.OnKeywordGroup([]string{"啊这"}, atriSleep).SetBlock(true).
|
||||
engine.OnKeywordGroup([]string{"啊这"}).SetBlock(true).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
process.SleepAbout1sTo2s()
|
||||
if rand.Intn(2) == 0 {
|
||||
ctx.SendChain(randImage("AZ.jpg", "AZ1.jpg"))
|
||||
ctx.SendChain(dgtr.randImage("AZ.jpg", "AZ1.jpg"))
|
||||
}
|
||||
})
|
||||
engine.OnKeywordGroup([]string{"我好了"}, atriSleep).SetBlock(true).
|
||||
engine.OnKeywordGroup([]string{"我好了"}).SetBlock(true).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
process.SleepAbout1sTo2s()
|
||||
ctx.SendChain(message.Reply(ctx.Event.MessageID), randText("不许好!", "憋回去!"))
|
||||
})
|
||||
engine.OnFullMatchGroup([]string{"?", "?", "¿"}, atriSleep).SetBlock(true).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
process.SleepAbout1sTo2s()
|
||||
switch rand.Intn(5) {
|
||||
case 0:
|
||||
ctx.SendChain(randText("?", "?", "嗯?", "(。´・ω・)ん?", "ん?"))
|
||||
case 1, 2:
|
||||
ctx.SendChain(randImage("WH.jpg", "WH1.jpg", "WH2.jpg", "WH3.jpg"))
|
||||
}
|
||||
})
|
||||
engine.OnKeyword("离谱", atriSleep).SetBlock(true).
|
||||
engine.OnFullMatchGroup([]string{"?", "?", "¿"}).SetBlock(true).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
switch rand.Intn(5) {
|
||||
case 0:
|
||||
ctx.SendChain(randText("?", "?", "嗯?", "(。´・ω・)ん?", "ん?"))
|
||||
case 1, 2:
|
||||
ctx.SendChain(randImage("WH.jpg"))
|
||||
ctx.SendChain(dgtr.randImage("WH.jpg", "WH1.jpg", "WH2.jpg", "WH3.jpg"))
|
||||
}
|
||||
})
|
||||
engine.OnKeyword("答应我", atriSleep, zero.OnlyToMe).SetBlock(true).
|
||||
engine.OnKeyword("离谱").SetBlock(true).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
switch rand.Intn(5) {
|
||||
case 0:
|
||||
ctx.SendChain(randText("?", "?", "嗯?", "(。´・ω・)ん?", "ん?"))
|
||||
case 1, 2:
|
||||
ctx.SendChain(dgtr.randImage("WH.jpg"))
|
||||
}
|
||||
})
|
||||
engine.OnKeyword("答应我", zero.OnlyToMe).SetBlock(true).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
process.SleepAbout1sTo2s()
|
||||
ctx.SendChain(randText("我无法回应你的请求"))
|
||||
})
|
||||
}
|
||||
|
||||
func randText(text ...string) message.MessageSegment {
|
||||
return message.Text(text[rand.Intn(len(text))])
|
||||
}
|
||||
|
||||
func randImage(file ...string) message.MessageSegment {
|
||||
return message.Image(res + file[rand.Intn(len(file))])
|
||||
}
|
||||
|
||||
func randRecord(file ...string) message.MessageSegment {
|
||||
return message.Record(res + file[rand.Intn(len(file))])
|
||||
}
|
||||
|
||||
// atriSleep 凌晨0点到6点,ATRI 在睡觉,不回应任何请求
|
||||
func atriSleep(ctx *zero.Ctx) bool {
|
||||
if now := time.Now().Hour(); now >= 1 && now < 6 {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
29
plugin/autowithdraw/main.go
Normal file
29
plugin/autowithdraw/main.go
Normal file
@@ -0,0 +1,29 @@
|
||||
// Package autowithdraw 触发者撤回时也自动撤回
|
||||
package autowithdraw
|
||||
|
||||
import (
|
||||
"github.com/FloatTech/floatbox/process"
|
||||
ctrl "github.com/FloatTech/zbpctrl"
|
||||
"github.com/FloatTech/zbputils/control"
|
||||
zero "github.com/wdvxdr1123/ZeroBot"
|
||||
"github.com/wdvxdr1123/ZeroBot/message"
|
||||
)
|
||||
|
||||
func init() {
|
||||
control.AutoRegister(&ctrl.Options[*zero.Ctx]{
|
||||
DisableOnDefault: false,
|
||||
Brief: "触发者撤回时也自动撤回",
|
||||
Help: "- 撤回一条消息\n",
|
||||
}).OnNotice(func(ctx *zero.Ctx) bool {
|
||||
return ctx.Event.NoticeType == "group_recall" || ctx.Event.NoticeType == "friend_recall"
|
||||
}).SetBlock(false).Handle(func(ctx *zero.Ctx) {
|
||||
id, ok := ctx.Event.MessageID.(int64)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
for _, msg := range zero.GetTriggeredMessages(message.NewMessageIDFromInteger(id)) {
|
||||
process.SleepAbout1sTo2s()
|
||||
ctx.DeleteMessage(msg)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
// Package baidu 百度一下
|
||||
package baidu
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
|
||||
zero "github.com/wdvxdr1123/ZeroBot"
|
||||
"github.com/wdvxdr1123/ZeroBot/message"
|
||||
|
||||
"github.com/FloatTech/zbputils/control"
|
||||
"github.com/FloatTech/zbputils/control/order"
|
||||
"github.com/FloatTech/zbputils/ctxext"
|
||||
)
|
||||
|
||||
func init() {
|
||||
control.Register("baidu", order.AcquirePrio(), &control.Options{
|
||||
DisableOnDefault: false,
|
||||
Help: "baidu\n" +
|
||||
"- 百度下[xxx]",
|
||||
}).OnPrefix("百度下").SetBlock(true).Limit(ctxext.LimitByGroup).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
txt := ctx.State["args"].(string)
|
||||
if txt != "" {
|
||||
ctx.SendChain(message.Text("https://buhuibaidu.me/?s=" + url.QueryEscape(txt)))
|
||||
}
|
||||
})
|
||||
}
|
||||
292
plugin/baiduaudit/audit.go
Normal file
292
plugin/baiduaudit/audit.go
Normal file
@@ -0,0 +1,292 @@
|
||||
// Package baiduaudit 百度内容审核
|
||||
package baiduaudit
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/Baidu-AIP/golang-sdk/aip/censor"
|
||||
"github.com/sirupsen/logrus"
|
||||
zero "github.com/wdvxdr1123/ZeroBot"
|
||||
"github.com/wdvxdr1123/ZeroBot/message"
|
||||
|
||||
"github.com/FloatTech/floatbox/binary"
|
||||
ctrl "github.com/FloatTech/zbpctrl"
|
||||
"github.com/FloatTech/zbputils/control"
|
||||
"github.com/FloatTech/zbputils/img/text"
|
||||
)
|
||||
|
||||
var (
|
||||
bdcli *censor.ContentCensorClient // 百度云审核服务Client
|
||||
txttyp = [...]string{
|
||||
0: "默认违禁词库",
|
||||
1: "违禁违规",
|
||||
2: "文本色情",
|
||||
3: "敏感信息",
|
||||
4: "恶意推广",
|
||||
5: "低俗辱骂",
|
||||
6: "恶意推广-联系方式",
|
||||
7: "恶意推广-软文推广",
|
||||
} // 文本类型
|
||||
config = newconfig() // 插件配置
|
||||
)
|
||||
|
||||
func init() {
|
||||
engine := control.AutoRegister(&ctrl.Options[*zero.Ctx]{
|
||||
DisableOnDefault: false,
|
||||
Brief: "百度内容审核",
|
||||
Help: "##该功能来自百度内容审核, 需购买相关服务, 并创建app##\n" +
|
||||
"- 获取BDAKey\n" +
|
||||
"- 配置BDAKey [API key] [Secret Key]\n" +
|
||||
"- 开启/关闭内容审核\n" +
|
||||
"- 开启/关闭撤回提示\n" +
|
||||
"- 开启/关闭详细提示\n" +
|
||||
"- 开启/关闭撤回禁言\n" +
|
||||
"##禁言时间设置## 禁言时间计算方式为:禁言次数*每次禁言累加时间,当达到最大禁言时间时, 再次触发按最大禁言时间计算\n" +
|
||||
"- 开启/关闭禁言累加\n" +
|
||||
"- 设置撤回禁言时间[分钟, 默认:1]\n" +
|
||||
"- 设置最大禁言时间[分钟, 默认:60,最大43200]\n" +
|
||||
"- 设置每次累加时间[分钟, 默认:1]\n" +
|
||||
"##检测类型设置## 类型编号列表:[1:违禁违规、2:文本色情、3:敏感信息、4:恶意推广、5:低俗辱骂 6:恶意推广-联系方式、7:恶意推广-软文推广]\n" +
|
||||
"- 查看检测类型\n" +
|
||||
"- 查看检测配置\n" +
|
||||
"- 设置检测类型[类型编号]\n" +
|
||||
"- 设置不检测类型[类型编号]\n" +
|
||||
"- 开启/关闭文本检测\n" +
|
||||
"- 开启/关闭图像检测\n" +
|
||||
"##测试功能##\n" +
|
||||
"- ^文本检测[文本内容]\n" +
|
||||
"- ^图像检测[图片]\n",
|
||||
PrivateDataFolder: "baiduaudit",
|
||||
})
|
||||
|
||||
configpath := engine.DataFolder() + "config.json"
|
||||
err := config.load(configpath)
|
||||
if err != nil {
|
||||
logrus.Warnln("[baiduaudit] 加载配置错误:", err)
|
||||
} else if config.Key1 != "" && config.Key2 != "" {
|
||||
bdcli = censor.NewClient(config.Key1, config.Key2)
|
||||
}
|
||||
|
||||
engine.OnFullMatch("获取BDAKey", zero.SuperUserPermission).SetBlock(true).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
ctx.SendChain(message.Text("接口key创建网址:\n" +
|
||||
"https://console.bce.baidu.com/ai/?_=1665977657185#/ai/antiporn/overview/index\n" +
|
||||
"免费8w次数领取地址:\n" +
|
||||
"https://console.bce.baidu.com/ai/?_=1665977657185#/ai/antiporn/overview/resource/getFree"))
|
||||
})
|
||||
|
||||
engine.OnRegex("^查看检测(类型|配置)$", zero.AdminPermission, hasinit).SetBlock(true).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
// 获取群配置
|
||||
group := config.groupof(ctx.Event.GroupID)
|
||||
msg := ""
|
||||
k1 := ctx.State["regex_matched"].([]string)[1]
|
||||
if k1 == "类型" {
|
||||
sb := strings.Builder{}
|
||||
sb.WriteString("本群检测类型:")
|
||||
found := false
|
||||
// 遍历群检测类型名单
|
||||
for i, v := range group.copyWhiteListType() {
|
||||
if !v {
|
||||
found = true
|
||||
sb.WriteByte('\n')
|
||||
sb.WriteString(strconv.Itoa(i))
|
||||
sb.WriteByte('.')
|
||||
sb.WriteString(txttyp[i])
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
sb.WriteString("无")
|
||||
}
|
||||
msg = sb.String()
|
||||
} else {
|
||||
// 生成配置文本
|
||||
msg = fmt.Sprintf("本群配置:\n"+
|
||||
"内容审核:%s\n"+
|
||||
"-文本:%s\n"+
|
||||
"-图像:%s\n"+
|
||||
"撤回提示:%s\n"+
|
||||
"-详细提示:%s\n"+
|
||||
"撤回禁言:%s\n"+
|
||||
"-禁言累加:%s\n"+
|
||||
"-撤回禁言时间:%v分钟\n"+
|
||||
"-每次累加时间:%v分钟\n"+
|
||||
"-最大禁言时间:%v分钟", group.Enable, group.TextAudit, group.ImageAudit, group.DMRemind, group.MoreRemind, group.DMBAN, group.BANTimeAddEnable, group.BANTime, group.BANTimeAddTime, group.MaxBANTimeAddRange)
|
||||
}
|
||||
b, err := text.RenderToBase64(msg, text.FontFile, 300, 20)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return
|
||||
}
|
||||
ctx.SendChain(message.Image("base64://" + binary.BytesToString(b)))
|
||||
})
|
||||
|
||||
engine.OnRegex("^设置(不)?检测类型([0-7])$", zero.AdminPermission, hasinit).SetBlock(true).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
k1 := ctx.State["regex_matched"].([]string)[1]
|
||||
k2 := ctx.State["regex_matched"].([]string)[2]
|
||||
group := config.groupof(ctx.Event.GroupID)
|
||||
inputType, _ := strconv.Atoi(k2)
|
||||
group.setWhiteListType(inputType, k1 == "不")
|
||||
err := config.saveto(configpath)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return
|
||||
}
|
||||
ctx.SendChain(message.At(ctx.Event.UserID), message.Text(fmt.Sprintf("本群将%s检测%s类型内容", k1, txttyp[inputType])))
|
||||
})
|
||||
|
||||
engine.OnRegex("^设置(最大|每次|撤回)(累加|禁言)时间(\\d{1,5})$", zero.AdminPermission, hasinit).SetBlock(true).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
k1 := ctx.State["regex_matched"].([]string)[1]
|
||||
k3 := ctx.State["regex_matched"].([]string)[3]
|
||||
time, _ := strconv.ParseInt(k1, 10, 64)
|
||||
config.groupof(ctx.Event.GroupID).set(func(g *group) {
|
||||
switch k1 {
|
||||
case "最大":
|
||||
g.MaxBANTimeAddRange = time
|
||||
case "每次":
|
||||
g.BANTimeAddTime = time
|
||||
case "撤回":
|
||||
g.BANTime = time
|
||||
}
|
||||
})
|
||||
err := config.saveto(configpath)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return
|
||||
}
|
||||
ctx.SendChain(message.At(ctx.Event.UserID), message.Text(fmt.Sprintf("本群%s禁言累加时间已设置为%s", k3, k1)))
|
||||
})
|
||||
|
||||
engine.OnRegex("^(开启|关闭)(内容审核|撤回提示|撤回禁言|禁言累加|详细提示|文本检测|图像检测)$", zero.AdminPermission, hasinit).SetBlock(true).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
k1 := ctx.State["regex_matched"].([]string)[1]
|
||||
k2 := ctx.State["regex_matched"].([]string)[2]
|
||||
isEnable := mark(k1 == "开启")
|
||||
config.groupof(ctx.Event.GroupID).set(func(g *group) {
|
||||
switch k2 {
|
||||
case "内容审核":
|
||||
g.Enable = isEnable
|
||||
case "撤回提示":
|
||||
g.DMRemind = isEnable
|
||||
case "撤回禁言":
|
||||
g.DMBAN = isEnable
|
||||
case "禁言累加":
|
||||
g.BANTimeAddEnable = isEnable
|
||||
case "详细提示":
|
||||
g.MoreRemind = isEnable
|
||||
case "文本检测":
|
||||
g.TextAudit = isEnable
|
||||
case "图像检测":
|
||||
g.ImageAudit = isEnable
|
||||
}
|
||||
})
|
||||
err := config.saveto(configpath)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return
|
||||
}
|
||||
ctx.SendChain(message.At(ctx.Event.UserID), message.Text(fmt.Sprintf("本群%s已%s", k2, k1)))
|
||||
})
|
||||
|
||||
engine.OnRegex(`^配置BDAKey\s(.*)\s(.*)$`, zero.SuperUserPermission).SetBlock(true).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
k1 := ctx.State["regex_matched"].([]string)[1]
|
||||
k2 := ctx.State["regex_matched"].([]string)[2]
|
||||
bdcli = censor.NewClient(k1, k2)
|
||||
config.setkey(k1, k2)
|
||||
if bdcli != nil {
|
||||
err := config.saveto(configpath)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return
|
||||
}
|
||||
ctx.SendChain(message.Text("配置成功"))
|
||||
}
|
||||
})
|
||||
|
||||
engine.OnMessage(config.isgroupexist).SetBlock(false).Handle(func(ctx *zero.Ctx) {
|
||||
group := config.groupof(ctx.Event.GroupID)
|
||||
if !bool(group.Enable) {
|
||||
return
|
||||
}
|
||||
var bdres baiduRes
|
||||
var err error
|
||||
for _, elem := range ctx.Event.Message {
|
||||
switch elem.Type {
|
||||
case "image":
|
||||
if !group.ImageAudit || elem.Data["url"] == "" {
|
||||
continue
|
||||
}
|
||||
res := bdcli.ImgCensorUrl(elem.Data["url"], nil)
|
||||
bdres, err = parse2BaiduRes(res)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
case "text":
|
||||
if !group.TextAudit || elem.Data["text"] == "" {
|
||||
continue
|
||||
}
|
||||
bdres, err = parse2BaiduRes(bdcli.TextCensor(elem.Data["text"]))
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
default:
|
||||
continue
|
||||
}
|
||||
}
|
||||
bdres.audit(ctx, configpath)
|
||||
})
|
||||
|
||||
engine.OnPrefix("^文本检测", hasinit).SetBlock(false).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
args := ctx.ExtractPlainText()
|
||||
res := bdcli.TextCensor(args)
|
||||
bdres, err := parse2BaiduRes(res)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", bdres.ErrorMsg, "(", bdres.ErrorCode, ")"))
|
||||
return
|
||||
}
|
||||
ctx.Send(config.groupof(ctx.Event.GroupID).reply(&bdres))
|
||||
})
|
||||
|
||||
engine.OnPrefix("^图像检测", hasinit).SetBlock(false).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
var urls []string
|
||||
for _, elem := range ctx.Event.Message {
|
||||
if elem.Type == "image" {
|
||||
if elem.Data["url"] != "" {
|
||||
urls = append(urls, elem.Data["url"])
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(urls) == 0 {
|
||||
return
|
||||
}
|
||||
res := bdcli.ImgCensorUrl(urls[0], nil)
|
||||
bdres, err := parse2BaiduRes(res)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", bdres.ErrorMsg, "(", bdres.ErrorCode, ")"))
|
||||
return
|
||||
}
|
||||
ctx.Send(config.groupof(ctx.Event.GroupID).reply(&bdres))
|
||||
})
|
||||
}
|
||||
|
||||
// 客户端是否初始化检测
|
||||
func hasinit(ctx *zero.Ctx) bool {
|
||||
if bdcli == nil {
|
||||
ctx.SendChain(message.Text("Key未配置"))
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func parse2BaiduRes(resjson string) (bdres baiduRes, err error) {
|
||||
err = json.Unmarshal(binary.StringToBytes(resjson), &bdres)
|
||||
return
|
||||
}
|
||||
269
plugin/baiduaudit/model.go
Normal file
269
plugin/baiduaudit/model.go
Normal file
@@ -0,0 +1,269 @@
|
||||
package baiduaudit
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"os"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/FloatTech/floatbox/file"
|
||||
zero "github.com/wdvxdr1123/ZeroBot"
|
||||
"github.com/wdvxdr1123/ZeroBot/message"
|
||||
)
|
||||
|
||||
// 服务网址:https://console.bce.baidu.com/ai/?_=1665977657185#/ai/antiporn/overview/index
|
||||
// 返回参数说明:https://cloud.baidu.com/doc/ANTIPORN/s/Nk3h6xbb2
|
||||
type baiduRes struct {
|
||||
mu sync.Mutex `json:"-"`
|
||||
// LogID int `json:"log_id"` // 请求唯一id
|
||||
Conclusion string `json:"conclusion"` // 审核结果, 可取值:合规、不合规、疑似、审核失败
|
||||
ConclusionType int `json:"conclusionType"` // 审核结果类型, 可取值1.合规, 2.不合规, 3.疑似, 4.审核失败
|
||||
Data []*auditData `json:"data"`
|
||||
ErrorCode int `json:"error_code"` // 错误提示码, 失败才返回, 成功不返回
|
||||
ErrorMsg string `json:"error_msg"` // 错误提示信息, 失败才返回, 成功不返回
|
||||
}
|
||||
|
||||
// 禁言检测
|
||||
func (bdres *baiduRes) audit(ctx *zero.Ctx, configpath string) {
|
||||
bdres.mu.Lock()
|
||||
defer bdres.mu.Unlock()
|
||||
// 如果返回类型为2(不合规), 0为合规, 3为疑似
|
||||
if bdres.ConclusionType != 2 {
|
||||
return
|
||||
}
|
||||
// 获取群配置
|
||||
group := config.groupof(ctx.Event.GroupID)
|
||||
// 检测群配置里的不检测类型白名单, 忽略掉不检测的违规类型
|
||||
for i, b := range group.copyWhiteListType() {
|
||||
if i == bdres.Data[0].SubType && b {
|
||||
return
|
||||
}
|
||||
}
|
||||
// 生成回复文本
|
||||
res := group.reply(bdres)
|
||||
// 撤回消息
|
||||
ctx.DeleteMessage(ctx.Event.MessageID)
|
||||
// 查看是否启用撤回后禁言
|
||||
if group.DMBAN {
|
||||
// 从历史违规记录中获取指定用户
|
||||
user := group.historyof(ctx.Event.UserID)
|
||||
// 用户违规次数自增
|
||||
atomic.AddInt64(&user.Count, 1)
|
||||
user.mu.Lock()
|
||||
// 用户违规原因记录
|
||||
user.ResList = append(user.ResList, bdres)
|
||||
user.mu.Unlock()
|
||||
// 保存到json
|
||||
err := config.saveto(configpath)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
}
|
||||
var bantime int64
|
||||
// 查看是否开启禁言累加功能, 并计算禁言时间
|
||||
if group.BANTimeAddEnable {
|
||||
bantime = atomic.LoadInt64(&user.Count) * group.BANTimeAddTime * 60
|
||||
} else {
|
||||
bantime = group.BANTime * 60
|
||||
}
|
||||
// 执行禁言
|
||||
ctx.SetThisGroupBan(ctx.Event.UserID, bantime)
|
||||
}
|
||||
// 查看是否开启撤回提示
|
||||
if group.DMRemind {
|
||||
res = append(res, message.At(ctx.Event.Sender.ID))
|
||||
ctx.Send(res)
|
||||
}
|
||||
}
|
||||
|
||||
type auditData struct {
|
||||
// Type int `json:"type"` // 审核主类型, 11:百度官方违禁词库、12:文本反作弊、13:自定义文本黑名单、14:自定义文本白名单
|
||||
SubType int `json:"subType"` // 审核子类型, 0:含多种类型, 具体看官方链接, 1:违禁违规、2:文本色情、3:敏感信息、4:恶意推广、5:低俗辱骂 6:恶意推广-联系方式、7:恶意推广-软文推广
|
||||
// Conclusion string `json:"conclusion"` // 审核结果, 可取值:合规、不合规、疑似、审核失败
|
||||
// ConclusionType int `json:"conclusionType"` // 审核结果类型, 可取值1.合规, 2.不合规, 3.疑似, 4.审核失败
|
||||
Msg string `json:"msg"` // 不合规项描述信息
|
||||
Hits []*hit `json:"hits"`
|
||||
} // 不合规/疑似/命中白名单项详细信息.响应成功并且conclusion为疑似或不合规或命中白名单时才返回, 响应失败或conclusion为合规且未命中白名单时不返回.
|
||||
|
||||
type auditHistory struct {
|
||||
mu sync.Mutex `json:"-"`
|
||||
Count int64 `json:"key2"` // 被禁次数
|
||||
ResList []*baiduRes `json:"reslist"` // 禁言原因
|
||||
}
|
||||
|
||||
type hit struct {
|
||||
// DatasetName string `json:"datasetName"` // 违规项目所属数据集名称
|
||||
Words []string `json:"words"` // 送检文本命中词库的关键词(备注:建议参考新字段“wordHitPositions”, 包含信息更丰富:关键词以及对应的位置及标签信息)
|
||||
// Probability float64 `json:"probability,omitempty"` // 不合规项置信度
|
||||
} // 送检文本违规原因的详细信息
|
||||
|
||||
type keyConfig struct {
|
||||
mu sync.Mutex `json:"-"`
|
||||
Key1 string `json:"key1"` // 百度云服务内容审核key存储
|
||||
Key2 string `json:"key2"` // 百度云服务内容审核key存储
|
||||
Groups map[int64]*group `json:"groups"` // 群配置存储
|
||||
}
|
||||
|
||||
func newconfig() (kc keyConfig) {
|
||||
kc.Groups = make(map[int64]*group, 64)
|
||||
return
|
||||
}
|
||||
|
||||
func (kc *keyConfig) setkey(k1, k2 string) {
|
||||
kc.mu.Lock()
|
||||
defer kc.mu.Unlock()
|
||||
kc.Key1 = k1
|
||||
kc.Key2 = k2
|
||||
}
|
||||
|
||||
// 加载JSON配置文件
|
||||
func (kc *keyConfig) load(filename string) error {
|
||||
if file.IsNotExist(filename) {
|
||||
return nil
|
||||
}
|
||||
f, err := os.Open(filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
kc.mu.Lock()
|
||||
defer kc.mu.Unlock()
|
||||
return json.NewDecoder(f).Decode(kc)
|
||||
}
|
||||
|
||||
func (kc *keyConfig) isgroupexist(ctx *zero.Ctx) (ok bool) {
|
||||
kc.mu.Lock()
|
||||
defer kc.mu.Unlock()
|
||||
_, ok = kc.Groups[ctx.Event.GroupID]
|
||||
return
|
||||
}
|
||||
|
||||
// 获取群配置
|
||||
func (kc *keyConfig) groupof(groupID int64) *group {
|
||||
kc.mu.Lock()
|
||||
defer kc.mu.Unlock()
|
||||
g, ok := kc.Groups[groupID]
|
||||
if ok {
|
||||
return g
|
||||
}
|
||||
g = &group{
|
||||
TextAudit: true,
|
||||
ImageAudit: true,
|
||||
BANTime: 1,
|
||||
MaxBANTimeAddRange: 60,
|
||||
BANTimeAddTime: 1,
|
||||
AuditHistory: map[int64]*auditHistory{},
|
||||
}
|
||||
kc.Groups[groupID] = g
|
||||
return g
|
||||
}
|
||||
|
||||
// 保存配置文件
|
||||
func (kc *keyConfig) saveto(filename string) error {
|
||||
kc.mu.Lock()
|
||||
defer kc.mu.Unlock()
|
||||
f, err := os.Create(filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
return json.NewEncoder(f).Encode(kc)
|
||||
}
|
||||
|
||||
type group struct {
|
||||
mu sync.Mutex
|
||||
Enable mark // 是否启用内容审核
|
||||
TextAudit mark // 文本检测
|
||||
ImageAudit mark // 图像检测
|
||||
DMRemind mark // 撤回提示
|
||||
MoreRemind mark // 详细违规提示
|
||||
DMBAN mark // 撤回后禁言
|
||||
BANTimeAddEnable mark // 禁言累加
|
||||
BANTime int64 // 标准禁言时间, 禁用累加, 但开启禁言的的情况下采用该值
|
||||
MaxBANTimeAddRange int64 // 最大禁言时间累加范围, 最高禁言时间
|
||||
BANTimeAddTime int64 // 禁言累加时间, 该值是开启禁累加功能后, 再次触发时, 根据被禁次数X该值计算出的禁言时间
|
||||
WhiteListType [8]bool // 类型白名单, 处于白名单类型的违规, 不会被触发 0:含多种类型, 具体看官方链接, 1:违禁违规、2:文本色情、3:敏感信息、4:恶意推广、5:低俗辱骂 6:恶意推广-联系方式、7:恶意推广-软文推广
|
||||
AuditHistory map[int64]*auditHistory // 被封禁用户列表
|
||||
}
|
||||
|
||||
func (g *group) set(f func(g *group)) {
|
||||
g.mu.Lock()
|
||||
f(g)
|
||||
g.mu.Unlock()
|
||||
}
|
||||
|
||||
func (g *group) setWhiteListType(typ int, ok bool) {
|
||||
g.mu.Lock()
|
||||
defer g.mu.Unlock()
|
||||
g.WhiteListType[typ] = ok
|
||||
}
|
||||
|
||||
func (g *group) copyWhiteListType() [8]bool {
|
||||
g.mu.Lock()
|
||||
defer g.mu.Unlock()
|
||||
return g.WhiteListType
|
||||
}
|
||||
|
||||
// 从群历史违规记录中获取用户
|
||||
func (g *group) historyof(userID int64) *auditHistory {
|
||||
g.mu.Lock()
|
||||
defer g.mu.Unlock()
|
||||
audit, ok := g.AuditHistory[userID]
|
||||
if ok {
|
||||
return audit
|
||||
}
|
||||
// 如果没有用户, 则创建一个并返回
|
||||
if g.AuditHistory == nil {
|
||||
g.AuditHistory = make(map[int64]*auditHistory)
|
||||
}
|
||||
audit = &auditHistory{}
|
||||
g.AuditHistory[userID] = audit
|
||||
return audit
|
||||
}
|
||||
|
||||
// 生成回复文本
|
||||
func (g *group) reply(bdres *baiduRes) message.Message {
|
||||
g.mu.Lock()
|
||||
defer g.mu.Unlock()
|
||||
// 建立消息段
|
||||
msgs := make([]message.Segment, 0, 8)
|
||||
// 生成简略审核结果回复
|
||||
msgs = append(msgs, message.Text(bdres.Conclusion, "\n"))
|
||||
// 查看是否开启详细审核内容提示, 并确定审核内容值为疑似, 或者不合规
|
||||
if !g.MoreRemind {
|
||||
return msgs
|
||||
}
|
||||
// 遍历返回的不合规数据, 生成详细违规内容
|
||||
for i, datum := range bdres.Data {
|
||||
msgs = append(msgs, message.Text("[", i, "]:", datum.Msg, "\n"))
|
||||
// 检查命中词条是否大于0
|
||||
if len(datum.Hits) == 0 {
|
||||
return msgs
|
||||
}
|
||||
// 遍历打印命中的违规词条
|
||||
for _, hit := range datum.Hits {
|
||||
if len(datum.Hits) == 0 {
|
||||
return msgs
|
||||
}
|
||||
msgs = append(msgs, message.Text("("))
|
||||
for i4, i3 := range hit.Words {
|
||||
// 检查是否是最后一个要打印的词条, 如果是则不加上逗号
|
||||
if i4 != len(hit.Words)-1 {
|
||||
msgs = append(msgs, message.Text(i3, ","))
|
||||
} else {
|
||||
msgs = append(msgs, message.Text(i3))
|
||||
}
|
||||
}
|
||||
msgs = append(msgs, message.Text(")"))
|
||||
}
|
||||
}
|
||||
return msgs
|
||||
}
|
||||
|
||||
type mark bool
|
||||
|
||||
// String 打印启用状态
|
||||
func (em mark) String() string {
|
||||
if em {
|
||||
return "开启"
|
||||
}
|
||||
return "关闭"
|
||||
}
|
||||
@@ -2,25 +2,22 @@
|
||||
package b14coder
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
|
||||
control "github.com/FloatTech/zbputils/control"
|
||||
"github.com/FloatTech/floatbox/crypto"
|
||||
ctrl "github.com/FloatTech/zbpctrl"
|
||||
"github.com/FloatTech/zbputils/control"
|
||||
base14 "github.com/fumiama/go-base16384"
|
||||
tea "github.com/fumiama/gofastTEA"
|
||||
zero "github.com/wdvxdr1123/ZeroBot"
|
||||
"github.com/wdvxdr1123/ZeroBot/message"
|
||||
"github.com/wdvxdr1123/ZeroBot/utils/helper"
|
||||
|
||||
"github.com/FloatTech/zbputils/control/order"
|
||||
)
|
||||
|
||||
func init() {
|
||||
en := control.Register("base16384", order.AcquirePrio(), &control.Options{
|
||||
en := control.AutoRegister(&ctrl.Options[*zero.Ctx]{
|
||||
DisableOnDefault: false,
|
||||
Help: "base16384加解密\n" +
|
||||
"- 加密xxx\n- 解密xxx\n- 用yyy加密xxx\n- 用yyy解密xxx",
|
||||
Brief: "base16384加解密",
|
||||
Help: "- 加密xxx\n- 解密xxx\n- 用yyy加密xxx\n- 用yyy解密xxx",
|
||||
})
|
||||
en.OnRegex(`^加密\s?(.*)`).SetBlock(true).
|
||||
en.OnRegex(`^加密\s*(.+)$`).SetBlock(true).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
str := ctx.State["regex_matched"].([]string)[1]
|
||||
es := base14.EncodeString(str)
|
||||
@@ -30,7 +27,7 @@ func init() {
|
||||
ctx.SendChain(message.Text("加密失败!"))
|
||||
}
|
||||
})
|
||||
en.OnRegex(`^解密\s?([一-踀]*[㴁-㴆]?)$`).SetBlock(true).
|
||||
en.OnRegex(`^解密\s*([一-踀]+[㴁-㴆]?)$`).SetBlock(true).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
str := ctx.State["regex_matched"].([]string)[1]
|
||||
es := base14.DecodeString(str)
|
||||
@@ -40,10 +37,10 @@ func init() {
|
||||
ctx.SendChain(message.Text("解密失败!"))
|
||||
}
|
||||
})
|
||||
en.OnRegex(`^用(.*)加密\s?(.*)`).SetBlock(true).
|
||||
en.OnRegex(`^用(.+)加密\s*(.+)$`).SetBlock(true).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
key, str := ctx.State["regex_matched"].([]string)[1], ctx.State["regex_matched"].([]string)[2]
|
||||
t := getea(key)
|
||||
t := crypto.GetTEA(key)
|
||||
es, err := base14.UTF16BE2UTF8(base14.Encode(t.Encrypt(helper.StringToBytes(str))))
|
||||
if err == nil {
|
||||
ctx.SendChain(message.Text(helper.BytesToString(es)))
|
||||
@@ -51,10 +48,10 @@ func init() {
|
||||
ctx.SendChain(message.Text("加密失败!"))
|
||||
}
|
||||
})
|
||||
en.OnRegex(`^用(.*)解密\s?([一-踀]*[㴁-㴆]?)$`).SetBlock(true).
|
||||
en.OnRegex(`^用(.+)解密\s*([一-踀]+[㴁-㴆]?)$`).SetBlock(true).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
key, str := ctx.State["regex_matched"].([]string)[1], ctx.State["regex_matched"].([]string)[2]
|
||||
t := getea(key)
|
||||
t := crypto.GetTEA(key)
|
||||
es, err := base14.UTF82UTF16BE(helper.StringToBytes(str))
|
||||
if err == nil {
|
||||
ctx.SendChain(message.Text(helper.BytesToString(t.Decrypt(base14.Decode(es)))))
|
||||
@@ -63,15 +60,3 @@ func init() {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func getea(key string) tea.TEA {
|
||||
kr := []rune(key)
|
||||
if len(kr) > 4 {
|
||||
kr = kr[:4]
|
||||
} else {
|
||||
for len(kr) < 4 {
|
||||
kr = append(kr, rune(4-len(kr)))
|
||||
}
|
||||
}
|
||||
return *(*tea.TEA)(*(*unsafe.Pointer)(unsafe.Pointer(&kr)))
|
||||
}
|
||||
62
plugin/base64gua/main.go
Normal file
62
plugin/base64gua/main.go
Normal file
@@ -0,0 +1,62 @@
|
||||
// Package base64gua base64卦 与 tea 加解密
|
||||
package base64gua
|
||||
|
||||
import (
|
||||
"github.com/FloatTech/floatbox/crypto"
|
||||
ctrl "github.com/FloatTech/zbpctrl"
|
||||
"github.com/FloatTech/zbputils/control"
|
||||
"github.com/fumiama/unibase2n"
|
||||
zero "github.com/wdvxdr1123/ZeroBot"
|
||||
"github.com/wdvxdr1123/ZeroBot/message"
|
||||
"github.com/wdvxdr1123/ZeroBot/utils/helper"
|
||||
)
|
||||
|
||||
func init() {
|
||||
en := control.AutoRegister(&ctrl.Options[*zero.Ctx]{
|
||||
DisableOnDefault: false,
|
||||
Brief: "六十四卦加解密",
|
||||
Help: "- 六十四卦加密xxx\n- 六十四卦解密xxx\n- 六十四卦用yyy加密xxx\n- 六十四卦用yyy解密xxx",
|
||||
})
|
||||
en.OnRegex(`^六十四卦加密\s*(.+)$`).SetBlock(true).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
str := ctx.State["regex_matched"].([]string)[1]
|
||||
es := unibase2n.Base64Gua.EncodeString(str)
|
||||
if es != "" {
|
||||
ctx.SendChain(message.Text(es))
|
||||
} else {
|
||||
ctx.SendChain(message.Text("加密失败!"))
|
||||
}
|
||||
})
|
||||
en.OnRegex(`^六十四卦解密\s*([䷀-䷿]+[☰☱]?)$`).SetBlock(true).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
str := ctx.State["regex_matched"].([]string)[1]
|
||||
es := unibase2n.Base64Gua.DecodeString(str)
|
||||
if es != "" {
|
||||
ctx.SendChain(message.Text(es))
|
||||
} else {
|
||||
ctx.SendChain(message.Text("解密失败!"))
|
||||
}
|
||||
})
|
||||
en.OnRegex(`^六十四卦用(.+)加密\s*(.+)$`).SetBlock(true).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
key, str := ctx.State["regex_matched"].([]string)[1], ctx.State["regex_matched"].([]string)[2]
|
||||
t := crypto.GetTEA(key)
|
||||
es, err := unibase2n.UTF16BE2UTF8(unibase2n.Base64Gua.Encode(t.Encrypt(helper.StringToBytes(str))))
|
||||
if err == nil {
|
||||
ctx.SendChain(message.Text(helper.BytesToString(es)))
|
||||
} else {
|
||||
ctx.SendChain(message.Text("加密失败!"))
|
||||
}
|
||||
})
|
||||
en.OnRegex(`^六十四卦用(.+)解密\s*([䷀-䷿]+[☰☱]?)$`).SetBlock(true).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
key, str := ctx.State["regex_matched"].([]string)[1], ctx.State["regex_matched"].([]string)[2]
|
||||
t := crypto.GetTEA(key)
|
||||
es, err := unibase2n.UTF82UTF16BE(helper.StringToBytes(str))
|
||||
if err == nil {
|
||||
ctx.SendChain(message.Text(helper.BytesToString(t.Decrypt(unibase2n.Base64Gua.Decode(es)))))
|
||||
} else {
|
||||
ctx.SendChain(message.Text("解密失败!"))
|
||||
}
|
||||
})
|
||||
}
|
||||
62
plugin/baseamasiro/main.go
Normal file
62
plugin/baseamasiro/main.go
Normal file
@@ -0,0 +1,62 @@
|
||||
// Package baseamasiro base天城文 与 tea 加解密
|
||||
package baseamasiro
|
||||
|
||||
import (
|
||||
"github.com/FloatTech/floatbox/crypto"
|
||||
ctrl "github.com/FloatTech/zbpctrl"
|
||||
"github.com/FloatTech/zbputils/control"
|
||||
"github.com/fumiama/unibase2n"
|
||||
zero "github.com/wdvxdr1123/ZeroBot"
|
||||
"github.com/wdvxdr1123/ZeroBot/message"
|
||||
"github.com/wdvxdr1123/ZeroBot/utils/helper"
|
||||
)
|
||||
|
||||
func init() {
|
||||
en := control.AutoRegister(&ctrl.Options[*zero.Ctx]{
|
||||
DisableOnDefault: false,
|
||||
Brief: "天城文加解密",
|
||||
Help: "- 天城文加密xxx\n- 天城文解密xxx\n- 天城文用yyy加密xxx\n- 天城文用yyy解密xxx",
|
||||
})
|
||||
en.OnRegex(`^天城文加密\s*(.+)$`).SetBlock(true).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
str := ctx.State["regex_matched"].([]string)[1]
|
||||
es := unibase2n.BaseDevanagari.EncodeString(str)
|
||||
if es != "" {
|
||||
ctx.SendChain(message.Text(es))
|
||||
} else {
|
||||
ctx.SendChain(message.Text("加密失败!"))
|
||||
}
|
||||
})
|
||||
en.OnRegex(`^天城文解密\s*([ऀ-ॿ]+[০-৫]?)$`).SetBlock(true).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
str := ctx.State["regex_matched"].([]string)[1]
|
||||
es := unibase2n.BaseDevanagari.DecodeString(str)
|
||||
if es != "" {
|
||||
ctx.SendChain(message.Text(es))
|
||||
} else {
|
||||
ctx.SendChain(message.Text("解密失败!"))
|
||||
}
|
||||
})
|
||||
en.OnRegex(`^天城文用(.+)加密\s*(.+)$`).SetBlock(true).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
key, str := ctx.State["regex_matched"].([]string)[1], ctx.State["regex_matched"].([]string)[2]
|
||||
t := crypto.GetTEA(key)
|
||||
es, err := unibase2n.UTF16BE2UTF8(unibase2n.BaseDevanagari.Encode(t.Encrypt(helper.StringToBytes(str))))
|
||||
if err == nil {
|
||||
ctx.SendChain(message.Text(helper.BytesToString(es)))
|
||||
} else {
|
||||
ctx.SendChain(message.Text("加密失败!"))
|
||||
}
|
||||
})
|
||||
en.OnRegex(`^天城文用(.+)解密\s*([ऀ-ॿ]+[০-৫]?)$`).SetBlock(true).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
key, str := ctx.State["regex_matched"].([]string)[1], ctx.State["regex_matched"].([]string)[2]
|
||||
t := crypto.GetTEA(key)
|
||||
es, err := unibase2n.UTF82UTF16BE(helper.StringToBytes(str))
|
||||
if err == nil {
|
||||
ctx.SendChain(message.Text(helper.BytesToString(t.Decrypt(unibase2n.BaseDevanagari.Decode(es)))))
|
||||
} else {
|
||||
ctx.SendChain(message.Text("解密失败!"))
|
||||
}
|
||||
})
|
||||
}
|
||||
582
plugin/bilibili/bilibili.go
Normal file
582
plugin/bilibili/bilibili.go
Normal file
@@ -0,0 +1,582 @@
|
||||
// Package bilibili 查询b站用户信息
|
||||
package bilibili
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"image"
|
||||
"image/color"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
"sort"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
bz "github.com/FloatTech/AnimeAPI/bilibili"
|
||||
fcext "github.com/FloatTech/floatbox/ctxext"
|
||||
"github.com/FloatTech/floatbox/file"
|
||||
"github.com/FloatTech/floatbox/web"
|
||||
"github.com/FloatTech/gg"
|
||||
"github.com/FloatTech/imgfactory"
|
||||
ctrl "github.com/FloatTech/zbpctrl"
|
||||
"github.com/FloatTech/zbputils/control"
|
||||
"github.com/FloatTech/zbputils/img/text"
|
||||
log "github.com/sirupsen/logrus"
|
||||
zero "github.com/wdvxdr1123/ZeroBot"
|
||||
"github.com/wdvxdr1123/ZeroBot/message"
|
||||
)
|
||||
|
||||
var (
|
||||
danmakuTypeMap = map[int64]string{
|
||||
0: "普通消息",
|
||||
1: "礼物",
|
||||
2: "上舰",
|
||||
3: "Superchat",
|
||||
4: "进入直播间",
|
||||
5: "标题变动",
|
||||
6: "分区变动",
|
||||
7: "直播中止",
|
||||
8: "直播继续",
|
||||
}
|
||||
cfg = bz.NewCookieConfig("data/Bilibili/config.json")
|
||||
)
|
||||
|
||||
// 查成分的
|
||||
func init() {
|
||||
engine := control.AutoRegister(&ctrl.Options[*zero.Ctx]{
|
||||
DisableOnDefault: false,
|
||||
Brief: "b站查成分查弹幕",
|
||||
Help: "- >vup info [xxx]\n" +
|
||||
"- >user info [xxx]\n" +
|
||||
"- 查成分 [xxx]\n" +
|
||||
"- 查弹幕 [xxx]\n" +
|
||||
"- 设置b站cookie b_ut=7;buvid3=0;i-wanna-go-back=-1;innersign=0;\n" +
|
||||
"- 更新vup\n" +
|
||||
"Tips: (412就是拦截的意思,建议私聊把cookie设全)\n",
|
||||
PublicDataFolder: "Bilibili",
|
||||
})
|
||||
cachePath := engine.DataFolder() + "cache/"
|
||||
_ = os.RemoveAll(cachePath)
|
||||
_ = os.MkdirAll(cachePath, 0755)
|
||||
var getdb = fcext.DoOnceOnSuccess(func(ctx *zero.Ctx) bool {
|
||||
var err error
|
||||
_, _ = engine.GetLazyData("bilibili.db", false)
|
||||
vdb, err = initializeVup(engine.DataFolder() + "bilibili.db")
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return false
|
||||
}
|
||||
return true
|
||||
})
|
||||
engine.OnRegex(`^>user info\s?(.{1,25})$`, bz.RequireUser(cfg)).SetBlock(true).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
id := ctx.State["uid"].(string)
|
||||
card, err := bz.GetMemberCard(id)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return
|
||||
}
|
||||
ctx.SendChain(message.Text(
|
||||
"uid: ", card.Mid, "\n",
|
||||
"name: ", card.Name, "\n",
|
||||
"sex: ", card.Sex, "\n",
|
||||
"sign: ", card.Sign, "\n",
|
||||
"level: ", card.LevelInfo.CurrentLevel, "\n",
|
||||
"birthday: ", card.Birthday,
|
||||
))
|
||||
})
|
||||
|
||||
engine.OnRegex(`^>vup info\s?(.{1,25})$`, bz.RequireUser(cfg)).SetBlock(true).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
id := ctx.State["uid"].(string)
|
||||
// 获取详情
|
||||
fo, err := bz.GetVtbDetail(id)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return
|
||||
}
|
||||
ctx.SendChain(message.Text(
|
||||
"b站id: ", fo.Mid, "\n",
|
||||
"名字: ", fo.Uname, "\n",
|
||||
"当前粉丝数: ", fo.Follower, "\n",
|
||||
"24h涨粉数: ", fo.Rise, "\n",
|
||||
"视频投稿数: ", fo.Video, "\n",
|
||||
"直播间id: ", fo.Roomid, "\n",
|
||||
"舰队: ", fo.GuardNum, "\n",
|
||||
"直播总排名: ", fo.AreaRank, "\n",
|
||||
"数据来源: ", "https://vtbs.moe/detail/", fo.Mid, "\n",
|
||||
"数据获取时间: ", time.Now().Format("2006-01-02 15:04:05"),
|
||||
))
|
||||
})
|
||||
|
||||
engine.OnRegex(`^查成分\s?(.{1,25})$`, bz.RequireUser(cfg), getdb).SetBlock(true).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
id := ctx.State["uid"].(string)
|
||||
today := time.Now().Format("20060102")
|
||||
drawedFile := cachePath + id + today + "vupLike.png"
|
||||
if file.IsExist(drawedFile) {
|
||||
ctx.SendChain(message.Image("file:///" + file.BOTPATH + "/" + drawedFile))
|
||||
return
|
||||
}
|
||||
u, err := bz.GetMemberCard(id)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return
|
||||
}
|
||||
vups, err := vdb.filterVup(u.Attentions)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return
|
||||
}
|
||||
vupLen := len(vups)
|
||||
medals, err := cfg.GetMedalWall(id)
|
||||
sort.Sort(bz.MedalSorter(medals))
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
}
|
||||
frontVups := make([]vup, 0)
|
||||
medalMap := make(map[int64]bz.Medal)
|
||||
for _, v := range medals {
|
||||
up := vup{
|
||||
Mid: v.Mid,
|
||||
Uname: v.Uname,
|
||||
}
|
||||
frontVups = append(frontVups, up)
|
||||
medalMap[v.Mid] = v
|
||||
}
|
||||
vups = append(vups, frontVups...)
|
||||
copy(vups[len(frontVups):], vups)
|
||||
copy(vups, frontVups)
|
||||
for i := len(frontVups); i < len(vups); i++ {
|
||||
if _, ok := medalMap[vups[i].Mid]; ok {
|
||||
vups = append(vups[:i], vups[i+1:]...)
|
||||
i--
|
||||
}
|
||||
}
|
||||
facePath := cachePath + id + "vupFace" + path.Ext(u.Face)
|
||||
backX := 500
|
||||
backY := 500
|
||||
var back image.Image
|
||||
if path.Ext(u.Face) != ".webp" {
|
||||
err = initFacePic(facePath, u.Face)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return
|
||||
}
|
||||
back, err = gg.LoadImage(facePath)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return
|
||||
}
|
||||
back = imgfactory.Size(back, backX, backY).Image()
|
||||
}
|
||||
if len(vups) > 50 {
|
||||
ctx.SendChain(message.Text(u.Name + "关注的up主太多了, 只展示前50个up"))
|
||||
vups = vups[:50]
|
||||
}
|
||||
canvas := gg.NewContext(1500, int(500*(1.1+float64(len(vups))/3)))
|
||||
fontSize := 50.0
|
||||
canvas.SetColor(color.White)
|
||||
canvas.Clear()
|
||||
if back != nil {
|
||||
canvas.DrawImage(back, 0, 0)
|
||||
}
|
||||
canvas.SetColor(color.Black)
|
||||
data, err := file.GetLazyData(text.BoldFontFile, control.Md5File, true)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
}
|
||||
if err = canvas.ParseFontFace(data, fontSize); err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return
|
||||
}
|
||||
sl, _ := canvas.MeasureString("好")
|
||||
length, h := canvas.MeasureString(u.Mid)
|
||||
n, _ := canvas.MeasureString(u.Name)
|
||||
canvas.DrawString(u.Name, 550, 160-h)
|
||||
canvas.DrawRoundedRectangle(600+n-length*0.1, 160-h*2.5, length*1.2, h*2, fontSize*0.2)
|
||||
canvas.SetRGB255(221, 221, 221)
|
||||
canvas.Fill()
|
||||
canvas.SetColor(color.Black)
|
||||
canvas.DrawString(u.Mid, 600+n, 160-h)
|
||||
canvas.DrawString(fmt.Sprintf("粉丝:%d", u.Fans), 550, 240-h)
|
||||
canvas.DrawString(fmt.Sprintf("关注:%d", len(u.Attentions)), 1000, 240-h)
|
||||
canvas.DrawString(fmt.Sprintf("管人痴成分:%.2f%%(%d/%d)", float64(vupLen)/float64(len(u.Attentions))*100, vupLen, len(u.Attentions)), 550, 320-h)
|
||||
regtime := time.Unix(u.Regtime, 0).Format("2006-01-02 15:04:05")
|
||||
canvas.DrawString("注册日期:"+regtime, 550, 400-h)
|
||||
canvas.DrawString("查询日期:"+time.Now().Format("2006-01-02"), 550, 480-h)
|
||||
for i, v := range vups {
|
||||
if i%2 == 1 {
|
||||
canvas.SetRGB255(245, 245, 245)
|
||||
canvas.DrawRectangle(0, float64(backY)*1.1+float64(i)*float64(backY)/3, float64(backX*3), float64(backY)/3)
|
||||
canvas.Fill()
|
||||
}
|
||||
canvas.SetColor(color.Black)
|
||||
nl, _ := canvas.MeasureString(v.Uname)
|
||||
canvas.DrawString(v.Uname, float64(backX)*0.1, float64(backY)*1.1+float64(i+1)*float64(backY)/3-2*h)
|
||||
ml, _ := canvas.MeasureString(strconv.FormatInt(v.Mid, 10))
|
||||
canvas.DrawRoundedRectangle(nl-0.1*ml+float64(backX)*0.2, float64(backY)*1.1+float64(i+1)*float64(backY)/3-h*3.5, ml*1.2, h*2, fontSize*0.2)
|
||||
canvas.SetRGB255(221, 221, 221)
|
||||
canvas.Fill()
|
||||
canvas.SetColor(color.Black)
|
||||
canvas.DrawString(strconv.FormatInt(v.Mid, 10), nl+float64(backX)*0.2, float64(backY)*1.1+float64(i+1)*float64(backY)/3-2*h)
|
||||
if m, ok := medalMap[v.Mid]; ok {
|
||||
mnl, _ := canvas.MeasureString(m.MedalName)
|
||||
grad := gg.NewLinearGradient(nl+ml-sl/2+float64(backX)*0.4, float64(backY)*1.1+float64(i+1)*float64(backY)/3-3.5*h, nl+ml+mnl+sl/2+float64(backX)*0.4, float64(backY)*1.1+float64(i+1)*float64(backY)/3-1.5*h)
|
||||
r, g, b := int2rbg(m.MedalColorStart)
|
||||
grad.AddColorStop(0, color.RGBA{R: uint8(r), G: uint8(g), B: uint8(b), A: 255})
|
||||
r, g, b = int2rbg(m.MedalColorEnd)
|
||||
grad.AddColorStop(1, color.RGBA{R: uint8(r), G: uint8(g), B: uint8(b), A: 255})
|
||||
canvas.SetFillStyle(grad)
|
||||
canvas.SetLineWidth(4)
|
||||
canvas.MoveTo(nl+ml-sl/2+float64(backX)*0.4, float64(backY)*1.1+float64(i+1)*float64(backY)/3-3.5*h)
|
||||
canvas.LineTo(nl+ml+mnl+sl/2+float64(backX)*0.4, float64(backY)*1.1+float64(i+1)*float64(backY)/3-3.5*h)
|
||||
canvas.LineTo(nl+ml+mnl+sl/2+float64(backX)*0.4, float64(backY)*1.1+float64(i+1)*float64(backY)/3-1.5*h)
|
||||
canvas.LineTo(nl+ml-sl/2+float64(backX)*0.4, float64(backY)*1.1+float64(i+1)*float64(backY)/3-1.5*h)
|
||||
canvas.ClosePath()
|
||||
canvas.Fill()
|
||||
canvas.SetColor(color.White)
|
||||
canvas.DrawString(m.MedalName, nl+ml+float64(backX)*0.4, float64(backY)*1.1+float64(i+1)*float64(backY)/3-2*h)
|
||||
r, g, b = int2rbg(m.MedalColorBorder)
|
||||
canvas.SetRGB255(int(r), int(g), int(b))
|
||||
canvas.DrawString(strconv.FormatInt(m.Level, 10), nl+ml+mnl+sl+float64(backX)*0.4, float64(backY)*1.1+float64(i+1)*float64(backY)/3-2*h)
|
||||
mll, _ := canvas.MeasureString(strconv.FormatInt(m.Level, 10))
|
||||
canvas.SetLineWidth(4)
|
||||
canvas.MoveTo(nl+ml-sl/2+float64(backX)*0.4, float64(backY)*1.1+float64(i+1)*float64(backY)/3-3.5*h)
|
||||
canvas.LineTo(nl+ml+mnl+mll+sl/2+float64(backX)*0.5, float64(backY)*1.1+float64(i+1)*float64(backY)/3-3.5*h)
|
||||
canvas.LineTo(nl+ml+mnl+mll+sl/2+float64(backX)*0.5, float64(backY)*1.1+float64(i+1)*float64(backY)/3-1.5*h)
|
||||
canvas.LineTo(nl+ml-sl/2+float64(backX)*0.4, float64(backY)*1.1+float64(i+1)*float64(backY)/3-1.5*h)
|
||||
canvas.ClosePath()
|
||||
canvas.Stroke()
|
||||
}
|
||||
}
|
||||
f, err := os.Create(drawedFile)
|
||||
if err != nil {
|
||||
log.Errorln("[bilibili]", err)
|
||||
data, err := imgfactory.ToBytes(canvas.Image())
|
||||
if err != nil {
|
||||
log.Errorln("[bilibili]", err)
|
||||
return
|
||||
}
|
||||
ctx.SendChain(message.ImageBytes(data))
|
||||
return
|
||||
}
|
||||
_, err = imgfactory.WriteTo(canvas.Image(), f)
|
||||
_ = f.Close()
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return
|
||||
}
|
||||
ctx.SendChain(message.Image("file:///" + file.BOTPATH + "/" + drawedFile))
|
||||
})
|
||||
|
||||
engine.OnRegex(`^查弹幕\s?(\S{1,25})\s?(\d*)$`, bz.RequireUser(cfg)).SetBlock(true).Handle(func(ctx *zero.Ctx) {
|
||||
id := ctx.State["uid"].(string)
|
||||
pagenum := ctx.State["regex_matched"].([]string)[2]
|
||||
if pagenum == "" {
|
||||
pagenum = "0"
|
||||
}
|
||||
u, err := bz.GetMemberCard(id)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return
|
||||
}
|
||||
var danmaku bz.Danmakusuki
|
||||
tr := &http.Transport{
|
||||
DisableKeepAlives: true,
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
||||
}
|
||||
|
||||
client := &http.Client{Transport: tr}
|
||||
data, err := web.RequestDataWith(client, fmt.Sprintf(bz.DanmakuAPI, id, pagenum), "GET", "", web.RandUA(), nil)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return
|
||||
}
|
||||
err = json.Unmarshal(data, &danmaku)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return
|
||||
}
|
||||
today := time.Now().Format("20060102150415")
|
||||
drawedFile := cachePath + id + today + "vupLike.png"
|
||||
facePath := cachePath + id + "vupFace" + path.Ext(u.Face)
|
||||
backX := 500
|
||||
backY := 500
|
||||
var back image.Image
|
||||
if path.Ext(u.Face) != ".webp" {
|
||||
err = initFacePic(facePath, u.Face)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return
|
||||
}
|
||||
back, err = gg.LoadImage(facePath)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return
|
||||
}
|
||||
back = imgfactory.Size(back, backX, backY).Image()
|
||||
}
|
||||
canvas := gg.NewContext(100, 100)
|
||||
fontSize := 50.0
|
||||
data, err = file.GetLazyData(text.BoldFontFile, control.Md5File, true)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
}
|
||||
if err = canvas.ParseFontFace(data, fontSize); err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return
|
||||
}
|
||||
dz, h := canvas.MeasureString("好")
|
||||
danmuH := h * 2
|
||||
faceH := float64(510)
|
||||
|
||||
totalDanmuku := 0
|
||||
for i := 0; i < len(danmaku.Data.Data.Records); i++ {
|
||||
totalDanmuku += len(danmaku.Data.Data.Records[i].Danmakus) + 1
|
||||
}
|
||||
cw := 3000
|
||||
mcw := float64(2000)
|
||||
ch := 550 + len(danmaku.Data.Data.Records)*int(faceH) + totalDanmuku*int(danmuH)
|
||||
canvas = gg.NewContext(cw, ch)
|
||||
canvas.SetColor(color.White)
|
||||
canvas.Clear()
|
||||
canvas.SetColor(color.Black)
|
||||
if err = canvas.ParseFontFace(data, fontSize); err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return
|
||||
}
|
||||
facestart := 100
|
||||
fontH := h * 1.6
|
||||
startWidth := float64(700)
|
||||
startWidth2 := float64(20)
|
||||
|
||||
if back != nil {
|
||||
canvas.DrawImage(back, facestart, 0)
|
||||
}
|
||||
length, _ := canvas.MeasureString(u.Mid)
|
||||
n, _ := canvas.MeasureString(u.Name)
|
||||
canvas.DrawString(u.Name, startWidth, 122.5)
|
||||
canvas.DrawRoundedRectangle(900+n-length*0.1, 66, length*1.2, 75, fontSize*0.2)
|
||||
canvas.SetRGB255(221, 221, 221)
|
||||
canvas.Fill()
|
||||
canvas.SetColor(color.Black)
|
||||
canvas.DrawString(u.Mid, 900+n, 122.5)
|
||||
canvas.DrawString(fmt.Sprintf("粉丝:%d 关注:%d", u.Fans, u.Attention), startWidth, 222.5)
|
||||
canvas.DrawString(fmt.Sprintf("页码:[%d/%d]", danmaku.Data.PageNum, (danmaku.Data.Total-1)/5), startWidth, 322.5)
|
||||
canvas.DrawString("网页链接: "+fmt.Sprintf(bz.DanmakuURL, u.Mid), startWidth, 422.5)
|
||||
var channelStart float64
|
||||
channelStart = float64(550)
|
||||
for i := 0; i < len(danmaku.Data.Data.Records); i++ {
|
||||
item := danmaku.Data.Data.Records[i]
|
||||
facePath = cachePath + strconv.Itoa(item.Channel.UID) + "vupFace" + path.Ext(item.Channel.FaceURL)
|
||||
if path.Ext(item.Channel.FaceURL) != ".webp" {
|
||||
err = initFacePic(facePath, item.Channel.FaceURL)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return
|
||||
}
|
||||
back, err = gg.LoadImage(facePath)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return
|
||||
}
|
||||
back = imgfactory.Size(back, backX, backY).Image()
|
||||
}
|
||||
if back != nil {
|
||||
canvas.DrawImage(back, facestart, int(channelStart))
|
||||
}
|
||||
canvas.SetRGB255(24, 144, 255)
|
||||
canvas.DrawString("标题: "+item.Live.Title, startWidth, channelStart+fontH)
|
||||
canvas.DrawString("主播: "+item.Channel.UName, startWidth, channelStart+fontH*2)
|
||||
canvas.SetColor(color.Black)
|
||||
canvas.DrawString("开始时间: "+time.UnixMilli(item.Live.StartDate).Format("2006-01-02 15:04:05"), startWidth, channelStart+fontH*3)
|
||||
if item.Live.IsFinish {
|
||||
canvas.DrawString("结束时间: "+time.UnixMilli(item.Live.StopDate).Format("2006-01-02 15:04:05"), startWidth, channelStart+fontH*4)
|
||||
canvas.DrawString("直播时长: "+strconv.FormatFloat(float64(item.Live.StopDate-item.Live.StartDate)/3600000.0, 'f', 1, 64)+"小时", startWidth, channelStart+fontH*5)
|
||||
} else {
|
||||
t := "结束时间:"
|
||||
l, _ := canvas.MeasureString(t)
|
||||
canvas.DrawString(t, startWidth, channelStart+fontH*4)
|
||||
|
||||
canvas.SetRGB255(0, 128, 0)
|
||||
t = "正在直播"
|
||||
canvas.DrawString(t, startWidth+l*1.1, channelStart+fontH*4)
|
||||
canvas.SetColor(color.Black)
|
||||
|
||||
canvas.DrawString("直播时长: "+strconv.FormatFloat(float64(time.Now().UnixMilli()-item.Live.StartDate)/3600000.0, 'f', 1, 64)+"小时", startWidth, channelStart+fontH*5)
|
||||
}
|
||||
canvas.DrawString("弹幕数量: "+strconv.Itoa(item.Live.DanmakusCount), startWidth, channelStart+fontH*6)
|
||||
canvas.DrawString("观看次数: "+strconv.Itoa(item.Live.WatchCount), startWidth, channelStart+fontH*7)
|
||||
|
||||
t := "收益:"
|
||||
l, _ := canvas.MeasureString(t)
|
||||
canvas.DrawString(t, startWidth, channelStart+fontH*8)
|
||||
|
||||
t = "¥" + strconv.Itoa(int(item.Live.TotalIncome))
|
||||
canvas.SetRGB255(255, 0, 0)
|
||||
canvas.DrawString(t, startWidth+l*1.1, channelStart+fontH*8)
|
||||
canvas.SetColor(color.Black)
|
||||
|
||||
DanmakuStart := channelStart + faceH
|
||||
for i := 0; i < len(item.Danmakus); i++ {
|
||||
moveW := startWidth2
|
||||
danmuNow := DanmakuStart + danmuH*float64(i+1)
|
||||
danItem := item.Danmakus[i]
|
||||
|
||||
t := time.UnixMilli(danItem.SendDate).Format("15:04:05")
|
||||
l, _ := canvas.MeasureString(t)
|
||||
canvas.DrawString(t, moveW, danmuNow)
|
||||
moveW += l + dz
|
||||
|
||||
t = danItem.UName
|
||||
l, _ = canvas.MeasureString(t)
|
||||
canvas.SetRGB255(24, 144, 255)
|
||||
canvas.DrawString(t, moveW, danmuNow)
|
||||
canvas.SetColor(color.Black)
|
||||
moveW += l + dz
|
||||
|
||||
switch danItem.Type {
|
||||
case 0:
|
||||
t = danItem.Message
|
||||
l, _ = canvas.MeasureString(t)
|
||||
canvas.DrawString(t, moveW, danmuNow)
|
||||
moveW += l + dz
|
||||
case 1:
|
||||
t = danmakuTypeMap[danItem.Type]
|
||||
l, _ = canvas.MeasureString(t)
|
||||
canvas.SetRGB255(255, 0, 0)
|
||||
canvas.DrawString(t, moveW, danmuNow)
|
||||
moveW += l + dz
|
||||
|
||||
t = danItem.Message
|
||||
l, _ = canvas.MeasureString(t)
|
||||
canvas.DrawString(t, moveW, danmuNow)
|
||||
canvas.SetColor(color.Black)
|
||||
moveW += l + dz
|
||||
case 2, 3:
|
||||
t = danmakuTypeMap[danItem.Type]
|
||||
l, _ = canvas.MeasureString(t)
|
||||
if danItem.Type == 3 {
|
||||
canvas.SetRGB255(0, 85, 255)
|
||||
} else {
|
||||
canvas.SetRGB255(128, 0, 128)
|
||||
}
|
||||
|
||||
canvas.DrawString(t, moveW, danmuNow)
|
||||
moveW += l + dz
|
||||
|
||||
t = danItem.Message
|
||||
l, _ = canvas.MeasureString(t)
|
||||
canvas.DrawString(t, moveW, danmuNow)
|
||||
moveW += l
|
||||
|
||||
t = "["
|
||||
l, _ = canvas.MeasureString(t)
|
||||
canvas.DrawString(t, moveW, danmuNow)
|
||||
moveW += l
|
||||
|
||||
t = "¥" + strconv.FormatFloat(danItem.Price, 'f', 1, 64)
|
||||
l, _ = canvas.MeasureString(t)
|
||||
canvas.SetRGB255(255, 0, 0)
|
||||
canvas.DrawString(t, moveW, danmuNow)
|
||||
if danItem.Type == 3 {
|
||||
canvas.SetRGB255(0, 85, 255)
|
||||
} else {
|
||||
canvas.SetRGB255(128, 0, 128)
|
||||
}
|
||||
moveW += l
|
||||
|
||||
t = "]"
|
||||
l, _ = canvas.MeasureString(t)
|
||||
canvas.DrawString(t, moveW, danmuNow)
|
||||
canvas.SetColor(color.Black)
|
||||
moveW += l + dz
|
||||
case 4, 5, 6, 7, 8:
|
||||
t = danmakuTypeMap[danItem.Type]
|
||||
canvas.SetRGB255(0, 128, 0)
|
||||
l, _ = canvas.MeasureString(t)
|
||||
canvas.DrawString(t, moveW, danmuNow)
|
||||
canvas.SetColor(color.Black)
|
||||
moveW += l + dz
|
||||
default:
|
||||
canvas.SetRGB255(0, 128, 0)
|
||||
l, _ = canvas.MeasureString("未知类型" + strconv.Itoa(int(danItem.Type)))
|
||||
canvas.DrawString(t, moveW, danmuNow)
|
||||
canvas.SetColor(color.Black)
|
||||
moveW += l + dz
|
||||
}
|
||||
if moveW > mcw {
|
||||
mcw = moveW
|
||||
}
|
||||
}
|
||||
channelStart = DanmakuStart + float64(len(item.Danmakus)+1)*danmuH
|
||||
}
|
||||
im := canvas.Image().(*image.RGBA)
|
||||
nim := im.SubImage(image.Rect(0, 0, int(mcw), ch))
|
||||
f, err := os.Create(drawedFile)
|
||||
if err != nil {
|
||||
log.Errorln("[bilibili]", err)
|
||||
data, err := imgfactory.ToBytes(nim)
|
||||
if err != nil {
|
||||
log.Errorln("[bilibili]", err)
|
||||
return
|
||||
}
|
||||
ctx.SendChain(message.ImageBytes(data))
|
||||
return
|
||||
}
|
||||
_, err = imgfactory.WriteTo(nim, f)
|
||||
_ = f.Close()
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return
|
||||
}
|
||||
ctx.SendChain(message.Image("file:///" + file.BOTPATH + "/" + drawedFile))
|
||||
})
|
||||
|
||||
engine.OnRegex(`^设置b站cookie?\s+(.*)$`, zero.SuperUserPermission).SetBlock(true).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
cookie := ctx.State["regex_matched"].([]string)[1]
|
||||
err := cfg.Set(cookie)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return
|
||||
}
|
||||
ctx.SendChain(message.Text("成功设置b站cookie为" + cookie))
|
||||
})
|
||||
|
||||
engine.OnFullMatch("更新vup", zero.SuperUserPermission).SetBlock(true).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
ctx.SendChain(message.Text("少女祈祷中..."))
|
||||
err := updateVup()
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return
|
||||
}
|
||||
ctx.SendChain(message.Text("vup已更新"))
|
||||
})
|
||||
}
|
||||
|
||||
func initFacePic(filename, faceURL string) error {
|
||||
if file.IsNotExist(filename) {
|
||||
data, err := web.GetData(faceURL)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = os.WriteFile(filename, data, 0666)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func int2rbg(t int64) (int64, int64, int64) {
|
||||
var buf [8]byte
|
||||
binary.LittleEndian.PutUint64(buf[:], uint64(t))
|
||||
b, g, r := int64(buf[0]), int64(buf[1]), int64(buf[2])
|
||||
return r, g, b
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
// Package bilibili 查询b站用户信息
|
||||
package bilibili
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
control "github.com/FloatTech/zbputils/control"
|
||||
"github.com/tidwall/gjson"
|
||||
zero "github.com/wdvxdr1123/ZeroBot"
|
||||
"github.com/wdvxdr1123/ZeroBot/message"
|
||||
|
||||
"github.com/FloatTech/zbputils/control/order"
|
||||
)
|
||||
|
||||
var engine = control.Register("bilibili", order.AcquirePrio(), &control.Options{
|
||||
DisableOnDefault: false,
|
||||
Help: "bilibili\n" +
|
||||
"- >vup info [名字 | uid]\n" +
|
||||
"- >user info [名字 | uid]",
|
||||
})
|
||||
|
||||
// 查成分的
|
||||
func init() {
|
||||
engine.OnRegex(`^>(?:user|vup)\s?info\s?(.{1,25})$`).SetBlock(true).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
keyword := ctx.State["regex_matched"].([]string)[1]
|
||||
rest, err := uid(keyword)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR:", err))
|
||||
return
|
||||
}
|
||||
id := rest.Get("data.result.0.mid").String()
|
||||
url := "https://api.bilibili.com/x/relation/same/followings?vmid=" + id
|
||||
method := "GET"
|
||||
client := &http.Client{}
|
||||
req, err := http.NewRequest(method, url, nil)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR:", err))
|
||||
return
|
||||
}
|
||||
req.Header.Add("cookie", "CURRENT_FNVAL=80; _uuid=772B88E8-3ED1-D589-29BB-F6CB5214239A06137infoc; blackside_state=1; bfe_id=6f285c892d9d3c1f8f020adad8bed553; rpdid=|(umY~Jkl|kJ0J'uYkR|)lu|); fingerprint=0ec2b1140fb30b56d7b5e415bc3b5fb1; buvid_fp=C91F5265-3DF4-4D5A-9FF3-C546370B14C0143096infoc; buvid_fp_plain=C91F5265-3DF4-4D5A-9FF3-C546370B14C0143096infoc; SESSDATA=9e0266f6%2C1639637127%2Cb0172%2A61; bili_jct=96ddbd7e22d527abdc0501339a12d4d3; DedeUserID=695737880; DedeUserID__ckMd5=0117660e75db7b01; sid=5labuhaf; PVID=1; bfe_id=1e33d9ad1cb29251013800c68af42315")
|
||||
res, err := client.Do(req)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR:", err))
|
||||
return
|
||||
}
|
||||
defer res.Body.Close()
|
||||
|
||||
body, err := io.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR:", err))
|
||||
return
|
||||
}
|
||||
data := string(body)
|
||||
ctx.SendChain(message.Text(
|
||||
"uid: ", rest.Get("data.result.0.mid").Int(), "\n",
|
||||
"name: ", rest.Get("data.result.0.uname").Str, "\n",
|
||||
"sex: ", []string{"", "", "女", "男"}[rest.Get("data.result.0.gender").Int()], "\n",
|
||||
"sign: ", rest.Get("data.result.0.usign").Str, "\n",
|
||||
"level: ", rest.Get("data.result.0.level").Int(), "\n",
|
||||
"follow: ", gjson.Get(data, "data.list.#.uname"),
|
||||
))
|
||||
})
|
||||
}
|
||||
@@ -1,85 +0,0 @@
|
||||
package bilibili
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/FloatTech/zbputils/web"
|
||||
"github.com/tidwall/gjson"
|
||||
zero "github.com/wdvxdr1123/ZeroBot"
|
||||
"github.com/wdvxdr1123/ZeroBot/message"
|
||||
)
|
||||
|
||||
// 查vup粉丝数据
|
||||
func init() {
|
||||
engine.OnRegex(`^>vup info\s?(.{1,25})$`).SetBlock(true).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
keyword := ctx.State["regex_matched"].([]string)[1]
|
||||
res, err := uid(keyword)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR:", err))
|
||||
return
|
||||
}
|
||||
id := res.Get("data.result.0.mid").String()
|
||||
// 获取详情
|
||||
fo, err := fansapi(id)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR:", err))
|
||||
return
|
||||
}
|
||||
ctx.SendChain(message.Text(
|
||||
"uid: ", fo.Mid, "\n",
|
||||
"名字: ", fo.Uname, "\n",
|
||||
"当前粉丝数: ", fo.Follower, "\n",
|
||||
"24h涨粉数: ", fo.Rise, "\n",
|
||||
"视频投稿数: ", fo.Video, "\n",
|
||||
"直播间id: ", fo.Roomid, "\n",
|
||||
"舰队: ", fo.GuardNum, "\n",
|
||||
"直播总排名: ", fo.AreaRank, "\n",
|
||||
"数据来源: ", "https://vtbs.moe/detail/", fo.Mid, "\n",
|
||||
"数据获取时间: ", time.Now().Format("2006-01-02 15:04:05"),
|
||||
))
|
||||
})
|
||||
}
|
||||
|
||||
// 搜索api:通过把触发指令传入的昵称找出uid返回
|
||||
func uid(keyword string) (*gjson.Result, error) {
|
||||
api := "http://api.bilibili.com/x/web-interface/search/type?search_type=bili_user&&user_type=1&keyword=" + keyword
|
||||
data, err := web.GetData(api)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
json := gjson.ParseBytes(data)
|
||||
if json.Get("data.numResults").Int() == 0 {
|
||||
return nil, errors.New("查无此人")
|
||||
}
|
||||
return &json, nil
|
||||
}
|
||||
|
||||
type follower struct {
|
||||
Mid int `json:"mid"`
|
||||
Uname string `json:"uname"`
|
||||
Video int `json:"video"`
|
||||
Roomid int `json:"roomid"`
|
||||
Rise int `json:"rise"`
|
||||
Follower int `json:"follower"`
|
||||
GuardNum int `json:"guardNum"`
|
||||
AreaRank int `json:"areaRank"`
|
||||
}
|
||||
|
||||
// 请求api
|
||||
func fansapi(uid string) (*follower, error) {
|
||||
url := "https://api.vtbs.moe/v1/detail/" + uid
|
||||
resp, err := http.Get(url)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
result := &follower{}
|
||||
if err := json.NewDecoder(resp.Body).Decode(result); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
90
plugin/bilibili/model.go
Normal file
90
plugin/bilibili/model.go
Normal file
@@ -0,0 +1,90 @@
|
||||
package bilibili
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/FloatTech/floatbox/binary"
|
||||
"github.com/FloatTech/floatbox/web"
|
||||
"github.com/jinzhu/gorm"
|
||||
"github.com/tidwall/gjson"
|
||||
)
|
||||
|
||||
var (
|
||||
vtbURLs = [...]string{"https://api.vtbs.moe/v1/short", "https://api.tokyo.vtbs.moe/v1/short", "https://vtbs.musedash.moe/v1/short"}
|
||||
vdb *vupdb
|
||||
)
|
||||
|
||||
// vupdb 分数数据库
|
||||
type vupdb gorm.DB
|
||||
|
||||
type vup struct {
|
||||
Mid int64 `gorm:"column:mid;primary_key"`
|
||||
Uname string `gorm:"column:uname"`
|
||||
Roomid int64 `gorm:"column:roomid"`
|
||||
}
|
||||
|
||||
func (vup) TableName() string {
|
||||
return "vup"
|
||||
}
|
||||
|
||||
// initializeVup 初始化vup数据库
|
||||
func initializeVup(dbpath string) (*vupdb, error) {
|
||||
if _, err := os.Stat(dbpath); err != nil || os.IsNotExist(err) {
|
||||
// 生成文件
|
||||
f, err := os.Create(dbpath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
}
|
||||
gdb, err := gorm.Open("sqlite3", dbpath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
gdb.AutoMigrate(&vup{})
|
||||
return (*vupdb)(gdb), nil
|
||||
}
|
||||
|
||||
func (vdb *vupdb) insertVupByMid(mid int64, uname string, roomid int64) (err error) {
|
||||
db := (*gorm.DB)(vdb)
|
||||
v := vup{
|
||||
Mid: mid,
|
||||
Uname: uname,
|
||||
Roomid: roomid,
|
||||
}
|
||||
if err = db.Model(&vup{}).First(&v, "mid = ? ", mid).Error; err != nil {
|
||||
if gorm.IsRecordNotFoundError(err) {
|
||||
err = db.Model(&vup{}).Create(&v).Error
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// filterVup 筛选vup
|
||||
func (vdb *vupdb) filterVup(ids []int64) (vups []vup, err error) {
|
||||
db := (*gorm.DB)(vdb)
|
||||
if err = db.Model(&vup{}).Find(&vups, "mid in (?)", ids).Error; err != nil {
|
||||
return vups, err
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func updateVup() error {
|
||||
for _, v := range vtbURLs {
|
||||
data, err := web.GetData(v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
gjson.Get(binary.BytesToString(data), "@this").ForEach(func(_, value gjson.Result) bool {
|
||||
mid := value.Get("mid").Int()
|
||||
uname := value.Get("uname").String()
|
||||
roomid := value.Get("roomid").Int()
|
||||
err = vdb.insertVupByMid(mid, uname, roomid)
|
||||
return err == nil
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
// Package bilibiliparse b站视频链接解析
|
||||
package bilibiliparse
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/FloatTech/zbputils/control"
|
||||
"github.com/antchfx/htmlquery"
|
||||
log "github.com/sirupsen/logrus"
|
||||
zero "github.com/wdvxdr1123/ZeroBot"
|
||||
"github.com/wdvxdr1123/ZeroBot/message"
|
||||
|
||||
"github.com/FloatTech/zbputils/control/order"
|
||||
)
|
||||
|
||||
const (
|
||||
bilibiliRe = "https://www.bilibili.com/video/av[0-9]+|https://www.bilibili.com/video/BV[0-9a-zA-Z]+|https://b23.tv/[0-9a-zA-Z]+|https://www.bilibili.com/video/bv[0-9a-zA-Z]+"
|
||||
validRe = "https://www.bilibili.com/video/(BV[0-9a-zA-Z]+)"
|
||||
)
|
||||
|
||||
func init() {
|
||||
engine := control.Register("bilibiliparse", order.AcquirePrio(), &control.Options{
|
||||
DisableOnDefault: false,
|
||||
Help: "b站视频链接解析\n" +
|
||||
"- https://www.bilibili.com/video/BV1xx411c7BF | https://www.bilibili.com/video/av1605 | https://b23.tv/I8uzWCA | https://www.bilibili.com/video/bv1xx411c7BF",
|
||||
})
|
||||
|
||||
engine.OnRegex(bilibiliRe).SetBlock(true).Handle(func(ctx *zero.Ctx) {
|
||||
bilibiliURL := ctx.State["regex_matched"].([]string)[0]
|
||||
m := parseURL(bilibiliURL)
|
||||
if len(m) != 0 {
|
||||
ctx.Send(m)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func parseURL(bilibiliURL string) (m message.Message) {
|
||||
doc, err := htmlquery.LoadURL(bilibiliURL)
|
||||
if err != nil {
|
||||
log.Errorln("[bilibiliparse]:访问的链接为", bilibiliURL, ",错误为", err)
|
||||
}
|
||||
videoURL := htmlquery.FindOne(doc, "/html/head/meta[@itemprop='url']").Attr[2].Val
|
||||
re := regexp.MustCompile(validRe)
|
||||
if !re.MatchString(videoURL) {
|
||||
return
|
||||
}
|
||||
bv := re.FindStringSubmatch(videoURL)[1]
|
||||
title := htmlquery.FindOne(doc, "//*[@id='viewbox_report']/h1/span/text()").Data
|
||||
m = append(m, message.Text(title+"\n"))
|
||||
upName := strings.TrimSpace(htmlquery.FindOne(doc, "//*[@id='v_upinfo']/div[2]/div[1]/a[1]/text()").Data)
|
||||
fmt.Println(upName)
|
||||
fanNumber := htmlquery.InnerText(htmlquery.FindOne(doc, "//i[@class='van-icon-general_addto_s']").NextSibling.NextSibling)
|
||||
fmt.Println(fanNumber)
|
||||
m = append(m, message.Text("up:"+upName+",粉丝:"+fanNumber+"\n"))
|
||||
view := htmlquery.FindOne(doc, "//*[@id='viewbox_report']/div/span[@class='view']/text()").Data
|
||||
dm := htmlquery.FindOne(doc, "//*[@id='viewbox_report']/div/span[@class='dm']/text()").Data
|
||||
m = append(m, message.Text(view+dm+"\n"))
|
||||
t := htmlquery.FindOne(doc, "//*[@id='viewbox_report']/div/span[3]/text()").Data
|
||||
m = append(m, message.Text(t))
|
||||
image := htmlquery.FindOne(doc, "/html/head/meta[@itemprop='image']").Attr[2].Val
|
||||
m = append(m, message.Image(image))
|
||||
like := htmlquery.FindOne(doc, "//*[@id='arc_toolbar_report']/div[1]/span[@class='like']/text()").Data
|
||||
coin := htmlquery.FindOne(doc, "//*[@id='arc_toolbar_report']/div[1]/span[@class='coin']/text()").Data
|
||||
m = append(m, message.Text("\n点赞:", strings.TrimSpace(like)+",投币:", strings.TrimSpace(coin)+"\n"))
|
||||
collect := htmlquery.FindOne(doc, "//*[@id='arc_toolbar_report']/div[1]/span[@class='collect']/text()").Data
|
||||
share := htmlquery.FindOne(doc, "//*[@id='arc_toolbar_report']/div[1]/span[@class='share']/text()").Data
|
||||
m = append(m, message.Text("收藏:", strings.TrimSpace(collect)+",分享:", strings.TrimSpace(share)+"\n"))
|
||||
m = append(m, message.Text(bv))
|
||||
return
|
||||
}
|
||||
@@ -1,562 +0,0 @@
|
||||
// Package bilibilipush b站推送
|
||||
package bilibilipush
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/tidwall/gjson"
|
||||
zero "github.com/wdvxdr1123/ZeroBot"
|
||||
"github.com/wdvxdr1123/ZeroBot/message"
|
||||
|
||||
"github.com/FloatTech/zbputils/binary"
|
||||
"github.com/FloatTech/zbputils/control"
|
||||
"github.com/FloatTech/zbputils/control/order"
|
||||
"github.com/FloatTech/zbputils/img/text"
|
||||
"github.com/FloatTech/zbputils/web"
|
||||
)
|
||||
|
||||
const (
|
||||
ua = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.107 Safari/537.36"
|
||||
referer = "https://www.bilibili.com/"
|
||||
infoURL = "https://api.bilibili.com/x/space/acc/info?mid=%d"
|
||||
userDynamicURL = "https://api.vc.bilibili.com/dynamic_svr/v1/dynamic_svr/space_history?host_uid=%d&offset_dynamic_id=0&need_top=0"
|
||||
liveListURL = "https://api.live.bilibili.com/room/v1/Room/get_status_info_by_uids"
|
||||
tURL = "https://t.bilibili.com/"
|
||||
liveURL = "https://live.bilibili.com/"
|
||||
serviceName = "bilibilipush"
|
||||
)
|
||||
|
||||
// bdb bilibili推送数据库
|
||||
var bdb *bilibilipushdb
|
||||
|
||||
var (
|
||||
lastTime = map[int64]int64{}
|
||||
typeMsg = map[int64]string{
|
||||
1: "转发了一条动态",
|
||||
2: "有图营业",
|
||||
4: "无图营业",
|
||||
8: "发布了新投稿",
|
||||
16: "发布了短视频",
|
||||
64: "发布了新专栏",
|
||||
256: "发布了新音频",
|
||||
}
|
||||
liveStatus = map[int64]int{}
|
||||
uidErrorMsg = map[int]string{
|
||||
0: "输入的uid有效",
|
||||
-400: "uid不存在,注意uid不是房间号",
|
||||
-402: "uid不存在,注意uid不是房间号",
|
||||
-412: "操作过于频繁IP暂时被风控,请半小时后再尝试",
|
||||
}
|
||||
upMap = map[int64]string{}
|
||||
)
|
||||
|
||||
func init() {
|
||||
go bilibiliPushDaily()
|
||||
en := control.Register(serviceName, order.AcquirePrio(), &control.Options{
|
||||
DisableOnDefault: false,
|
||||
Help: "bilibilipush\n" +
|
||||
"- 添加订阅[uid]\n" +
|
||||
"- 取消订阅[uid]\n" +
|
||||
"- 取消动态订阅[uid]\n" +
|
||||
"- 取消直播订阅[uid]\n" +
|
||||
"- 推送列表",
|
||||
PrivateDataFolder: serviceName,
|
||||
})
|
||||
|
||||
// 加载数据库
|
||||
go func() {
|
||||
dbpath := en.DataFolder()
|
||||
dbfile := dbpath + "push.db"
|
||||
bdb = initialize(dbfile)
|
||||
log.Println("[bilibilipush]加载bilibilipush数据库")
|
||||
}()
|
||||
|
||||
en.OnRegex(`^添加订阅\s?(\d+)$`, zero.UserOrGrpAdmin).SetBlock(true).Handle(func(ctx *zero.Ctx) {
|
||||
buid, _ := strconv.ParseInt(ctx.State["regex_matched"].([]string)[1], 10, 64)
|
||||
var name string
|
||||
var ok bool
|
||||
if name, ok = upMap[buid]; !ok {
|
||||
var status int
|
||||
status, name = checkBuid(buid)
|
||||
if status != 0 {
|
||||
msg, ok := uidErrorMsg[status]
|
||||
if !ok {
|
||||
msg = "未知错误,请私聊反馈给" + zero.BotConfig.NickName[0]
|
||||
}
|
||||
ctx.SendChain(message.Text(msg))
|
||||
return
|
||||
}
|
||||
}
|
||||
gid := ctx.Event.GroupID
|
||||
if gid == 0 {
|
||||
gid = -ctx.Event.UserID
|
||||
}
|
||||
if err := subscribe(buid, gid); err != nil {
|
||||
log.Errorln("[bilibilipush]:", err)
|
||||
} else {
|
||||
ctx.SendChain(message.Text("已添加" + name + "的订阅"))
|
||||
}
|
||||
})
|
||||
en.OnRegex(`^取消订阅\s?(\d+)$`, zero.UserOrGrpAdmin).SetBlock(true).Handle(func(ctx *zero.Ctx) {
|
||||
buid, _ := strconv.ParseInt(ctx.State["regex_matched"].([]string)[1], 10, 64)
|
||||
var name string
|
||||
var ok bool
|
||||
if name, ok = upMap[buid]; !ok {
|
||||
var status int
|
||||
status, name = checkBuid(buid)
|
||||
if status != 0 {
|
||||
msg, ok := uidErrorMsg[status]
|
||||
if !ok {
|
||||
msg = "未知错误,请私聊反馈给" + zero.BotConfig.NickName[0]
|
||||
}
|
||||
ctx.SendChain(message.Text(msg))
|
||||
return
|
||||
}
|
||||
}
|
||||
gid := ctx.Event.GroupID
|
||||
if gid == 0 {
|
||||
gid = -ctx.Event.UserID
|
||||
}
|
||||
if err := unsubscribe(buid, gid); err != nil {
|
||||
log.Errorln("[bilibilipush]:", err)
|
||||
} else {
|
||||
ctx.SendChain(message.Text("已取消" + name + "的订阅"))
|
||||
}
|
||||
})
|
||||
en.OnRegex(`^取消动态订阅\s?(\d+)$`, zero.UserOrGrpAdmin).SetBlock(true).Handle(func(ctx *zero.Ctx) {
|
||||
buid, _ := strconv.ParseInt(ctx.State["regex_matched"].([]string)[1], 10, 64)
|
||||
var name string
|
||||
var ok bool
|
||||
if name, ok = upMap[buid]; !ok {
|
||||
var status int
|
||||
status, name = checkBuid(buid)
|
||||
if status != 0 {
|
||||
msg, ok := uidErrorMsg[status]
|
||||
if !ok {
|
||||
msg = "未知错误,请私聊反馈给" + zero.BotConfig.NickName[0]
|
||||
}
|
||||
ctx.SendChain(message.Text(msg))
|
||||
return
|
||||
}
|
||||
}
|
||||
gid := ctx.Event.GroupID
|
||||
if gid == 0 {
|
||||
gid = -ctx.Event.UserID
|
||||
}
|
||||
if err := unsubscribeDynamic(buid, gid); err != nil {
|
||||
log.Errorln("[bilibilipush]:", err)
|
||||
} else {
|
||||
ctx.SendChain(message.Text("已取消" + name + "的动态订阅"))
|
||||
}
|
||||
})
|
||||
en.OnRegex(`^取消直播订阅\s?(\d+)$`, zero.UserOrGrpAdmin).SetBlock(true).Handle(func(ctx *zero.Ctx) {
|
||||
buid, _ := strconv.ParseInt(ctx.State["regex_matched"].([]string)[1], 10, 64)
|
||||
var name string
|
||||
var ok bool
|
||||
if name, ok = upMap[buid]; !ok {
|
||||
var status int
|
||||
status, name = checkBuid(buid)
|
||||
if status != 0 {
|
||||
msg, ok := uidErrorMsg[status]
|
||||
if !ok {
|
||||
msg = "未知错误,请私聊反馈给" + zero.BotConfig.NickName[0]
|
||||
}
|
||||
ctx.SendChain(message.Text(msg))
|
||||
return
|
||||
}
|
||||
}
|
||||
gid := ctx.Event.GroupID
|
||||
if gid == 0 {
|
||||
gid = -ctx.Event.UserID
|
||||
}
|
||||
if err := unsubscribeLive(buid, gid); err != nil {
|
||||
log.Errorln("[bilibilipush]:", err)
|
||||
} else {
|
||||
ctx.SendChain(message.Text("已取消" + name + "的直播订阅"))
|
||||
}
|
||||
})
|
||||
en.OnFullMatch("推送列表", zero.UserOrGrpAdmin).SetBlock(true).Handle(func(ctx *zero.Ctx) {
|
||||
gid := ctx.Event.GroupID
|
||||
if gid == 0 {
|
||||
gid = -ctx.Event.UserID
|
||||
}
|
||||
bpl := bdb.getAllPushByGroup(gid)
|
||||
fmt.Println(bpl)
|
||||
msg := "--------推送列表--------"
|
||||
for _, v := range bpl {
|
||||
if _, ok := upMap[v.BilibiliUID]; !ok {
|
||||
bdb.updateAllUp()
|
||||
fmt.Println(upMap)
|
||||
}
|
||||
msg += fmt.Sprintf("\nuid:%-12d 动态:", v.BilibiliUID)
|
||||
if v.DynamicDisable == 0 {
|
||||
msg += "●"
|
||||
} else {
|
||||
msg += "○"
|
||||
}
|
||||
msg += " 直播:"
|
||||
if v.LiveDisable == 0 {
|
||||
msg += "●"
|
||||
} else {
|
||||
msg += "○"
|
||||
}
|
||||
msg += " up主:" + upMap[v.BilibiliUID]
|
||||
}
|
||||
data, err := text.RenderToBase64(msg, text.FontFile, 600, 20)
|
||||
if err != nil {
|
||||
log.Errorln("[bilibilipush]:", err)
|
||||
}
|
||||
if id := ctx.SendChain(message.Image("base64://" + binary.BytesToString(data))); id.ID() == 0 {
|
||||
ctx.SendChain(message.Text("ERROR:可能被风控了"))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func bilibiliPushDaily() {
|
||||
t := time.NewTicker(time.Second * 10)
|
||||
defer t.Stop()
|
||||
for range t.C {
|
||||
log.Println("-----bilibilipush拉取推送信息-----")
|
||||
sendDynamic()
|
||||
sendLive()
|
||||
}
|
||||
}
|
||||
|
||||
func checkBuid(buid int64) (status int, name string) {
|
||||
data, err := web.RequestDataWith(web.NewDefaultClient(), fmt.Sprintf(infoURL, buid), "GET", referer, ua)
|
||||
if err != nil {
|
||||
log.Errorln("[bilibilipush]:", err)
|
||||
}
|
||||
status = int(gjson.Get(binary.BytesToString(data), "code").Int())
|
||||
name = gjson.Get(binary.BytesToString(data), "data.name").String()
|
||||
if status == 0 {
|
||||
bdb.insertBilibiliUp(buid, name)
|
||||
upMap[buid] = name
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// subscribe 订阅
|
||||
func subscribe(buid, groupid int64) (err error) {
|
||||
bpMap := map[string]interface{}{
|
||||
"bilibili_uid": buid,
|
||||
"group_id": groupid,
|
||||
"live_disable": 0,
|
||||
"dynamic_disable": 0,
|
||||
}
|
||||
err = bdb.insertOrUpdateLiveAndDynamic(bpMap)
|
||||
return
|
||||
}
|
||||
|
||||
// unsubscribe 取消订阅
|
||||
func unsubscribe(buid, groupid int64) (err error) {
|
||||
bpMap := map[string]interface{}{
|
||||
"bilibili_uid": buid,
|
||||
"group_id": groupid,
|
||||
"live_disable": 1,
|
||||
"dynamic_disable": 1,
|
||||
}
|
||||
err = bdb.insertOrUpdateLiveAndDynamic(bpMap)
|
||||
return
|
||||
}
|
||||
|
||||
func unsubscribeDynamic(buid, groupid int64) (err error) {
|
||||
bpMap := map[string]interface{}{
|
||||
"bilibili_uid": buid,
|
||||
"group_id": groupid,
|
||||
"dynamic_disable": 1,
|
||||
}
|
||||
err = bdb.insertOrUpdateLiveAndDynamic(bpMap)
|
||||
return
|
||||
}
|
||||
|
||||
func unsubscribeLive(buid, groupid int64) (err error) {
|
||||
bpMap := map[string]interface{}{
|
||||
"bilibili_uid": buid,
|
||||
"group_id": groupid,
|
||||
"live_disable": 1,
|
||||
}
|
||||
err = bdb.insertOrUpdateLiveAndDynamic(bpMap)
|
||||
return
|
||||
}
|
||||
|
||||
func getUserDynamicCard(buid int64) (cardList []gjson.Result) {
|
||||
data, err := web.RequestDataWith(web.NewDefaultClient(), fmt.Sprintf(userDynamicURL, buid), "GET", referer, ua)
|
||||
if err != nil {
|
||||
log.Errorln("[bilibilipush]:", err)
|
||||
}
|
||||
cardList = gjson.Get(binary.BytesToString(data), "data.cards").Array()
|
||||
return
|
||||
}
|
||||
|
||||
func getLiveList(uids ...int64) string {
|
||||
m := make(map[string]interface{})
|
||||
m["uids"] = uids
|
||||
b, _ := json.Marshal(m)
|
||||
client := &http.Client{}
|
||||
// 提交请求
|
||||
request, err := http.NewRequest("POST", liveListURL, bytes.NewBuffer(b))
|
||||
if err != nil {
|
||||
log.Errorln("[bilibilipush]:", err)
|
||||
}
|
||||
request.Header.Add("Referer", referer)
|
||||
request.Header.Add("User-Agent", ua)
|
||||
response, err := client.Do(request)
|
||||
if err != nil {
|
||||
log.Errorln("[bilibilipush]:", err)
|
||||
}
|
||||
defer response.Body.Close()
|
||||
data, err := io.ReadAll(response.Body)
|
||||
if err != nil {
|
||||
log.Errorln("[bilibilipush]:", err)
|
||||
}
|
||||
return binary.BytesToString(data)
|
||||
}
|
||||
|
||||
func sendDynamic() {
|
||||
uids := bdb.getAllBuidByDynamic()
|
||||
for _, buid := range uids {
|
||||
cardList := getUserDynamicCard(buid)
|
||||
if len(cardList) == 0 {
|
||||
return
|
||||
}
|
||||
t, ok := lastTime[buid]
|
||||
if !ok {
|
||||
lastTime[buid] = cardList[0].Get("desc.timestamp").Int()
|
||||
return
|
||||
}
|
||||
for i := len(cardList) - 1; i >= 0; i-- {
|
||||
ct := cardList[i].Get("desc.timestamp").Int()
|
||||
if ct > t && ct > time.Now().Unix()-600 {
|
||||
lastTime[buid] = ct
|
||||
m, ok := control.Lookup(serviceName)
|
||||
if ok {
|
||||
groupList := bdb.getAllGroupByBuidAndDynamic(buid)
|
||||
var msg []message.MessageSegment
|
||||
cType := cardList[i].Get("desc.type").Int()
|
||||
cardStr := cardList[i].Get("card").String()
|
||||
switch cType {
|
||||
case 0:
|
||||
cName := cardList[i].Get("desc.user_profile.info.uname").String()
|
||||
cTime := time.Unix(cardList[i].Get("desc.timestamp").Int(), 0).Format("2006-01-02 15:04:05")
|
||||
msg = append(msg, message.Text(cName+"在"+cTime+typeMsg[cType]+"\n"))
|
||||
case 1:
|
||||
cName := gjson.Get(cardStr, "user.uname").String()
|
||||
msg = append(msg, message.Text(cName+typeMsg[cType]+"\n"))
|
||||
cContent := gjson.Get(cardStr, "item.content").String()
|
||||
msg = append(msg, message.Text(cContent+"\n"))
|
||||
msg = append(msg, message.Text("转发的内容:\n"))
|
||||
cOrigType := gjson.Get(cardStr, "item.orig_type").Int()
|
||||
cOrigin := gjson.Get(cardStr, "origin").String()
|
||||
switch cOrigType {
|
||||
case 1:
|
||||
cName := gjson.Get(cOrigin, "user.uname").String()
|
||||
msg = append(msg, message.Text(cName+typeMsg[cOrigType]+"\n"))
|
||||
case 2:
|
||||
cName := gjson.Get(cOrigin, "user.name").String()
|
||||
cUploadTime := time.Unix(gjson.Get(cOrigin, "item.upload_time").Int(), 0).Format("2006-01-02 15:04:05")
|
||||
msg = append(msg, message.Text(cName+"在"+cUploadTime+typeMsg[cOrigType]+"\n"))
|
||||
cDescription := gjson.Get(cOrigin, "item.description")
|
||||
msg = append(msg, message.Text(cDescription))
|
||||
if gjson.Get(cOrigin, "item.pictures.#").Int() != 0 {
|
||||
gjson.Get(cOrigin, "item.pictures").ForEach(func(_, v gjson.Result) bool {
|
||||
msg = append(msg, message.Image(v.Get("img_src").String()))
|
||||
return true
|
||||
})
|
||||
}
|
||||
case 4:
|
||||
cName := gjson.Get(cOrigin, "user.uname").String()
|
||||
cTimestamp := time.Unix(gjson.Get(cOrigin, "item.timestamp").Int(), 0).Format("2006-01-02 15:04:05")
|
||||
msg = append(msg, message.Text(cName+"在"+cTimestamp+typeMsg[cOrigType]+"\n"))
|
||||
cContent := gjson.Get(cOrigin, "item.content").String()
|
||||
msg = append(msg, message.Text(cContent+"\n"))
|
||||
case 8:
|
||||
cName := gjson.Get(cOrigin, "owner.name").String()
|
||||
cTime := time.Unix(gjson.Get(cOrigin, "pubdate").Int(), 0).Format("2006-01-02 15:04:05")
|
||||
msg = append(msg, message.Text(cName+"在"+cTime+typeMsg[cOrigType]+"\n"))
|
||||
cTitle := gjson.Get(cOrigin, "title").String()
|
||||
msg = append(msg, message.Text(cTitle))
|
||||
cPic := gjson.Get(cOrigin, "pic").String()
|
||||
msg = append(msg, message.Image(cPic))
|
||||
cDesc := gjson.Get(cOrigin, "desc").String()
|
||||
msg = append(msg, message.Text(cDesc+"\n"))
|
||||
cShareSubtitle := gjson.Get(cOrigin, "share_subtitle").String()
|
||||
msg = append(msg, message.Text(cShareSubtitle+"\n"))
|
||||
cShortLink := gjson.Get(cOrigin, "short_link").String()
|
||||
msg = append(msg, message.Text("视频链接:"+cShortLink+"\n"))
|
||||
case 16:
|
||||
cName := gjson.Get(cOrigin, "user.name").String()
|
||||
cUploadTime := gjson.Get(cOrigin, "item.upload_time").String()
|
||||
msg = append(msg, message.Text(cName+"在"+cUploadTime+typeMsg[cOrigType]+"\n"))
|
||||
cDescription := gjson.Get(cOrigin, "item.description")
|
||||
msg = append(msg, message.Text(cDescription))
|
||||
cCover := gjson.Get(cOrigin, "item.cover.default").String()
|
||||
msg = append(msg, message.Image(cCover))
|
||||
case 64:
|
||||
cName := gjson.Get(cOrigin, "author.name").String()
|
||||
cPublishTime := time.Unix(gjson.Get(cOrigin, "publish_time").Int(), 0).Format("2006-01-02 15:04:05")
|
||||
msg = append(msg, message.Text(cName+"在"+cPublishTime+typeMsg[cOrigType]+"\n"))
|
||||
cTitle := gjson.Get(cOrigin, "title").String()
|
||||
msg = append(msg, message.Text(cTitle+"\n"))
|
||||
cSummary := gjson.Get(cOrigin, "summary").String()
|
||||
msg = append(msg, message.Text(cSummary))
|
||||
cBannerURL := gjson.Get(cOrigin, "banner_url").String()
|
||||
msg = append(msg, message.Image(cBannerURL))
|
||||
case 256:
|
||||
cUpper := gjson.Get(cOrigin, "upper").String()
|
||||
cTime := time.UnixMilli(gjson.Get(cOrigin, "ctime").Int()).Format("2006-01-02 15:04:05")
|
||||
msg = append(msg, message.Text(cUpper+"在"+cTime+typeMsg[cOrigType]+"\n"))
|
||||
cTitle := gjson.Get(cOrigin, "title").String()
|
||||
msg = append(msg, message.Text(cTitle))
|
||||
cCover := gjson.Get(cOrigin, "cover").String()
|
||||
msg = append(msg, message.Image(cCover))
|
||||
default:
|
||||
msg = append(msg, message.Text("未知动态类型"+strconv.FormatInt(cOrigType, 10)+"\n"))
|
||||
}
|
||||
case 2:
|
||||
cName := gjson.Get(cardStr, "user.name").String()
|
||||
cUploadTime := time.Unix(gjson.Get(cardStr, "item.upload_time").Int(), 0).Format("2006-01-02 15:04:05")
|
||||
msg = append(msg, message.Text(cName+"在"+cUploadTime+typeMsg[cType]+"\n"))
|
||||
cDescription := gjson.Get(cardStr, "item.description")
|
||||
msg = append(msg, message.Text(cDescription))
|
||||
if gjson.Get(cardStr, "item.pictures.#").Int() != 0 {
|
||||
gjson.Get(cardStr, "item.pictures").ForEach(func(_, v gjson.Result) bool {
|
||||
msg = append(msg, message.Image(v.Get("img_src").String()))
|
||||
return true
|
||||
})
|
||||
}
|
||||
case 4:
|
||||
cName := gjson.Get(cardStr, "user.uname").String()
|
||||
cTimestamp := time.Unix(gjson.Get(cardStr, "item.timestamp").Int(), 0).Format("2006-01-02 15:04:05")
|
||||
msg = append(msg, message.Text(cName+"在"+cTimestamp+typeMsg[cType]+"\n"))
|
||||
cContent := gjson.Get(cardStr, "item.content").String()
|
||||
msg = append(msg, message.Text(cContent+"\n"))
|
||||
case 8:
|
||||
cName := gjson.Get(cardStr, "owner.name").String()
|
||||
cTime := time.Unix(gjson.Get(cardStr, "ctime").Int(), 0).Format("2006-01-02 15:04:05")
|
||||
msg = append(msg, message.Text(cName+"在"+cTime+typeMsg[cType]+"\n"))
|
||||
cTitle := gjson.Get(cardStr, "title").String()
|
||||
msg = append(msg, message.Text(cTitle))
|
||||
cPic := gjson.Get(cardStr, "pic").String()
|
||||
msg = append(msg, message.Image(cPic))
|
||||
cDesc := gjson.Get(cardStr, "desc").String()
|
||||
msg = append(msg, message.Text(cDesc+"\n"))
|
||||
cShareSubtitle := gjson.Get(cardStr, "share_subtitle").String()
|
||||
msg = append(msg, message.Text(cShareSubtitle+"\n"))
|
||||
cShortLink := gjson.Get(cardStr, "short_link").String()
|
||||
msg = append(msg, message.Text("视频链接:"+cShortLink+"\n"))
|
||||
case 16:
|
||||
cName := gjson.Get(cardStr, "user.name").String()
|
||||
cUploadTime := gjson.Get(cardStr, "item.upload_time").String()
|
||||
msg = append(msg, message.Text(cName+"在"+cUploadTime+typeMsg[cType]+"\n"))
|
||||
cDescription := gjson.Get(cardStr, "item.description")
|
||||
msg = append(msg, message.Text(cDescription))
|
||||
cCover := gjson.Get(cardStr, "item.cover.default").String()
|
||||
msg = append(msg, message.Image(cCover))
|
||||
case 64:
|
||||
cName := gjson.Get(cardStr, "author.name").String()
|
||||
cPublishTime := time.Unix(gjson.Get(cardStr, "publish_time").Int(), 0).Format("2006-01-02 15:04:05")
|
||||
msg = append(msg, message.Text(cName+"在"+cPublishTime+typeMsg[cType]+"\n"))
|
||||
cTitle := gjson.Get(cardStr, "title").String()
|
||||
msg = append(msg, message.Text(cTitle+"\n"))
|
||||
cSummary := gjson.Get(cardStr, "summary").String()
|
||||
msg = append(msg, message.Text(cSummary))
|
||||
cBannerURL := gjson.Get(cardStr, "banner_url").String()
|
||||
msg = append(msg, message.Image(cBannerURL))
|
||||
case 256:
|
||||
cUpper := gjson.Get(cardStr, "upper").String()
|
||||
cTime := time.UnixMilli(gjson.Get(cardStr, "ctime").Int()).Format("2006-01-02 15:04:05")
|
||||
msg = append(msg, message.Text(cUpper+"在"+cTime+typeMsg[cType]+"\n"))
|
||||
cTitle := gjson.Get(cardStr, "title").String()
|
||||
msg = append(msg, message.Text(cTitle))
|
||||
cCover := gjson.Get(cardStr, "cover").String()
|
||||
msg = append(msg, message.Image(cCover))
|
||||
default:
|
||||
msg = append(msg, message.Text("未知动态类型"+strconv.FormatInt(cType, 10)+"\n"))
|
||||
}
|
||||
cID := cardList[i].Get("desc.dynamic_id").String()
|
||||
msg = append(msg, message.Text("动态链接:", tURL+cID))
|
||||
|
||||
zero.RangeBot(func(id int64, ctx *zero.Ctx) bool {
|
||||
for _, gid := range groupList {
|
||||
if m.IsEnabledIn(gid) {
|
||||
switch {
|
||||
case gid > 0:
|
||||
ctx.SendGroupMessage(gid, msg)
|
||||
case gid < 0:
|
||||
ctx.SendPrivateMessage(-gid, msg)
|
||||
default:
|
||||
log.Errorln("[bilibilipush]:gid为0")
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func sendLive() {
|
||||
uids := bdb.getAllBuidByLive()
|
||||
gjson.Get(getLiveList(uids...), "data").ForEach(func(key, value gjson.Result) bool {
|
||||
newStatus := int(value.Get("live_status").Int())
|
||||
if newStatus == 2 {
|
||||
newStatus = 0
|
||||
}
|
||||
if _, ok := liveStatus[key.Int()]; !ok {
|
||||
liveStatus[key.Int()] = newStatus
|
||||
return true
|
||||
}
|
||||
oldStatus := liveStatus[key.Int()]
|
||||
if newStatus != oldStatus && newStatus == 1 {
|
||||
liveStatus[key.Int()] = newStatus
|
||||
m, ok := control.Lookup(serviceName)
|
||||
if ok {
|
||||
groupList := bdb.getAllGroupByBuidAndLive(key.Int())
|
||||
roomID := value.Get("short_id").Int()
|
||||
if roomID == 0 {
|
||||
roomID = value.Get("room_id").Int()
|
||||
}
|
||||
lURL := liveURL + strconv.FormatInt(roomID, 10)
|
||||
lName := value.Get("uname").String()
|
||||
lTitle := value.Get("title").String()
|
||||
lCover := value.Get("cover_from_user").String()
|
||||
if lCover == "" {
|
||||
lCover = value.Get("keyframe").String()
|
||||
}
|
||||
var msg []message.MessageSegment
|
||||
msg = append(msg, message.Text(lName+" 正在直播:\n"))
|
||||
msg = append(msg, message.Text(lTitle))
|
||||
msg = append(msg, message.Image(lCover))
|
||||
msg = append(msg, message.Text("直播链接:", lURL))
|
||||
zero.RangeBot(func(id int64, ctx *zero.Ctx) bool {
|
||||
for _, gid := range groupList {
|
||||
if m.IsEnabledIn(gid) {
|
||||
switch {
|
||||
case gid > 0:
|
||||
ctx.SendGroupMessage(gid, msg)
|
||||
case gid < 0:
|
||||
ctx.SendPrivateMessage(-gid, msg)
|
||||
default:
|
||||
log.Errorln("[bilibilipush]:gid为0")
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
})
|
||||
}
|
||||
} else if newStatus != oldStatus {
|
||||
liveStatus[key.Int()] = newStatus
|
||||
}
|
||||
return true
|
||||
})
|
||||
}
|
||||
291
plugin/bilibiliparse/parse.go
Normal file
291
plugin/bilibiliparse/parse.go
Normal file
@@ -0,0 +1,291 @@
|
||||
// Package bilibiliparse bilibili卡片解析
|
||||
package bilibiliparse
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
bz "github.com/FloatTech/AnimeAPI/bilibili"
|
||||
"github.com/FloatTech/floatbox/file"
|
||||
"github.com/FloatTech/floatbox/web"
|
||||
ctrl "github.com/FloatTech/zbpctrl"
|
||||
"github.com/FloatTech/zbputils/control"
|
||||
"github.com/FloatTech/zbputils/ctxext"
|
||||
"github.com/pkg/errors"
|
||||
zero "github.com/wdvxdr1123/ZeroBot"
|
||||
"github.com/wdvxdr1123/ZeroBot/message"
|
||||
)
|
||||
|
||||
const (
|
||||
enableVideoSummary = int64(0x10)
|
||||
disableVideoSummary = ^enableVideoSummary
|
||||
enableVideoDownload = int64(0x20)
|
||||
disableVideoDownload = ^enableVideoDownload
|
||||
bilibiliparseReferer = "https://www.bilibili.com"
|
||||
ua = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.107 Safari/537.36"
|
||||
)
|
||||
|
||||
var (
|
||||
limit = ctxext.NewLimiterManager(time.Second*10, 1)
|
||||
searchVideo = `bilibili.com\\?/video\\?/(?:av(\d+)|([bB][vV][0-9a-zA-Z]+))`
|
||||
searchDynamic = `(t.bilibili.com|m.bilibili.com\\?/dynamic)\\?/(\d+)`
|
||||
searchArticle = `bilibili.com\\?/read\\?/(?:cv|mobile\\?/)(\d+)`
|
||||
searchLiveRoom = `live.bilibili.com\\?/(\d+)`
|
||||
searchVideoRe = regexp.MustCompile(searchVideo)
|
||||
searchDynamicRe = regexp.MustCompile(searchDynamic)
|
||||
searchArticleRe = regexp.MustCompile(searchArticle)
|
||||
searchLiveRoomRe = regexp.MustCompile(searchLiveRoom)
|
||||
cachePath string
|
||||
cfg = bz.NewCookieConfig("data/Bilibili/config.json")
|
||||
)
|
||||
|
||||
// 插件主体
|
||||
func init() {
|
||||
en := control.AutoRegister(&ctrl.Options[*zero.Ctx]{
|
||||
DisableOnDefault: false,
|
||||
Brief: "b站链接解析",
|
||||
Help: "例:- t.bilibili.com/642277677329285174\n- bilibili.com/read/cv17134450\n- bilibili.com/video/BV13B4y1x7pS\n- live.bilibili.com/22603245 ",
|
||||
})
|
||||
cachePath = en.DataFolder() + "cache/"
|
||||
_ = os.RemoveAll(cachePath)
|
||||
_ = os.MkdirAll(cachePath, 0755)
|
||||
en.OnRegex(`((b23|acg).tv|bili2233.cn)\\?/[0-9a-zA-Z]+`).SetBlock(true).Limit(limit.LimitByGroup).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
u := ctx.State["regex_matched"].([]string)[0]
|
||||
u = strings.ReplaceAll(u, "\\", "")
|
||||
realurl, err := bz.GetRealURL("https://" + u)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return
|
||||
}
|
||||
switch {
|
||||
case searchVideoRe.MatchString(realurl):
|
||||
ctx.State["regex_matched"] = searchVideoRe.FindStringSubmatch(realurl)
|
||||
handleVideo(ctx)
|
||||
case searchDynamicRe.MatchString(realurl):
|
||||
ctx.State["regex_matched"] = searchDynamicRe.FindStringSubmatch(realurl)
|
||||
handleDynamic(ctx)
|
||||
case searchArticleRe.MatchString(realurl):
|
||||
ctx.State["regex_matched"] = searchArticleRe.FindStringSubmatch(realurl)
|
||||
handleArticle(ctx)
|
||||
case searchLiveRoomRe.MatchString(realurl):
|
||||
ctx.State["regex_matched"] = searchLiveRoomRe.FindStringSubmatch(realurl)
|
||||
handleLive(ctx)
|
||||
}
|
||||
})
|
||||
en.OnRegex(`^(开启|打开|启用|关闭|关掉|禁用)视频总结$`, zero.AdminPermission).SetBlock(true).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
gid := ctx.Event.GroupID
|
||||
if gid <= 0 {
|
||||
// 个人用户设为负数
|
||||
gid = -ctx.Event.UserID
|
||||
}
|
||||
option := ctx.State["regex_matched"].([]string)[1]
|
||||
c, ok := ctx.State["manager"].(*ctrl.Control[*zero.Ctx])
|
||||
if !ok {
|
||||
ctx.SendChain(message.Text("找不到服务!"))
|
||||
return
|
||||
}
|
||||
data := c.GetData(ctx.Event.GroupID)
|
||||
switch option {
|
||||
case "开启", "打开", "启用":
|
||||
data |= enableVideoSummary
|
||||
case "关闭", "关掉", "禁用":
|
||||
data &= disableVideoSummary
|
||||
default:
|
||||
return
|
||||
}
|
||||
err := c.SetData(gid, data)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("出错啦: ", err))
|
||||
return
|
||||
}
|
||||
ctx.SendChain(message.Text("已", option, "视频总结"))
|
||||
})
|
||||
en.OnRegex(`^(开启|打开|启用|关闭|关掉|禁用)视频上传$`, zero.AdminPermission).SetBlock(true).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
gid := ctx.Event.GroupID
|
||||
if gid <= 0 {
|
||||
// 个人用户设为负数
|
||||
gid = -ctx.Event.UserID
|
||||
}
|
||||
option := ctx.State["regex_matched"].([]string)[1]
|
||||
c, ok := ctx.State["manager"].(*ctrl.Control[*zero.Ctx])
|
||||
if !ok {
|
||||
ctx.SendChain(message.Text("找不到服务!"))
|
||||
return
|
||||
}
|
||||
data := c.GetData(ctx.Event.GroupID)
|
||||
switch option {
|
||||
case "开启", "打开", "启用":
|
||||
data |= enableVideoDownload
|
||||
case "关闭", "关掉", "禁用":
|
||||
data &= disableVideoDownload
|
||||
default:
|
||||
return
|
||||
}
|
||||
err := c.SetData(gid, data)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("出错啦: ", err))
|
||||
return
|
||||
}
|
||||
ctx.SendChain(message.Text("已", option, "视频上传"))
|
||||
})
|
||||
en.OnRegex(searchVideo).SetBlock(true).Limit(limit.LimitByGroup).Handle(handleVideo)
|
||||
en.OnRegex(searchDynamic).SetBlock(true).Limit(limit.LimitByGroup).Handle(handleDynamic)
|
||||
en.OnRegex(searchArticle).SetBlock(true).Limit(limit.LimitByGroup).Handle(handleArticle)
|
||||
en.OnRegex(searchLiveRoom).SetBlock(true).Limit(limit.LimitByGroup).Handle(handleLive)
|
||||
}
|
||||
|
||||
func handleVideo(ctx *zero.Ctx) {
|
||||
id := ctx.State["regex_matched"].([]string)[1]
|
||||
if id == "" {
|
||||
id = ctx.State["regex_matched"].([]string)[2]
|
||||
}
|
||||
card, err := bz.GetVideoInfo(id)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return
|
||||
}
|
||||
msg, err := card.ToVideoMessage()
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return
|
||||
}
|
||||
c, ok := ctx.State["manager"].(*ctrl.Control[*zero.Ctx])
|
||||
if ok && c.GetData(ctx.Event.GroupID)&enableVideoSummary == enableVideoSummary {
|
||||
summaryMsg, err := getVideoSummary(cfg, card)
|
||||
if err != nil {
|
||||
msg = append(msg, message.Text("ERROR: ", err))
|
||||
} else {
|
||||
msg = append(msg, summaryMsg...)
|
||||
}
|
||||
}
|
||||
ctx.SendChain(msg...)
|
||||
if ok && c.GetData(ctx.Event.GroupID)&enableVideoDownload == enableVideoDownload {
|
||||
downLoadMsg, err := getVideoDownload(cfg, card, cachePath)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return
|
||||
}
|
||||
ctx.SendChain(downLoadMsg...)
|
||||
}
|
||||
}
|
||||
|
||||
func handleDynamic(ctx *zero.Ctx) {
|
||||
msg, err := cfg.GetDetailMessage(ctx.State["regex_matched"].([]string)[2])
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return
|
||||
}
|
||||
ctx.SendChain(msg...)
|
||||
}
|
||||
|
||||
func handleArticle(ctx *zero.Ctx) {
|
||||
card, err := bz.GetArticleInfo(ctx.State["regex_matched"].([]string)[1])
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return
|
||||
}
|
||||
ctx.SendChain(card.ToArticleMessage(ctx.State["regex_matched"].([]string)[1])...)
|
||||
}
|
||||
|
||||
func handleLive(ctx *zero.Ctx) {
|
||||
cookie, err := cfg.Load()
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return
|
||||
}
|
||||
card, err := bz.GetLiveRoomInfo(ctx.State["regex_matched"].([]string)[1], cookie)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return
|
||||
}
|
||||
ctx.SendChain(card.ToMessage()...)
|
||||
}
|
||||
|
||||
// getVideoSummary AI视频总结
|
||||
func getVideoSummary(cookiecfg *bz.CookieConfig, card bz.Card) (msg []message.Segment, err error) {
|
||||
var (
|
||||
data []byte
|
||||
videoSummary bz.VideoSummary
|
||||
)
|
||||
data, err = web.RequestDataWithHeaders(web.NewDefaultClient(), bz.SignURL(fmt.Sprintf(bz.VideoSummaryURL, card.BvID, card.CID, card.Owner.Mid)), "GET", func(req *http.Request) error {
|
||||
if cookiecfg != nil {
|
||||
cookie := ""
|
||||
cookie, err = cookiecfg.Load()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req.Header.Add("cookie", cookie)
|
||||
}
|
||||
req.Header.Set("User-Agent", ua)
|
||||
return nil
|
||||
}, nil)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = json.Unmarshal(data, &videoSummary)
|
||||
msg = make([]message.Segment, 0, 16)
|
||||
msg = append(msg, message.Text("已为你生成视频总结\n\n"))
|
||||
msg = append(msg, message.Text(videoSummary.Data.ModelResult.Summary, "\n\n"))
|
||||
for _, v := range videoSummary.Data.ModelResult.Outline {
|
||||
msg = append(msg, message.Text("● ", v.Title, "\n"))
|
||||
for _, p := range v.PartOutline {
|
||||
msg = append(msg, message.Text(fmt.Sprintf("%d:%d %s\n", p.Timestamp/60, p.Timestamp%60, p.Content)))
|
||||
}
|
||||
msg = append(msg, message.Text("\n"))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func getVideoDownload(cookiecfg *bz.CookieConfig, card bz.Card, cachePath string) (msg []message.Segment, err error) {
|
||||
var (
|
||||
data []byte
|
||||
videoDownload bz.VideoDownload
|
||||
stderr bytes.Buffer
|
||||
)
|
||||
today := time.Now().Format("20060102")
|
||||
videoFile := fmt.Sprintf("%s%s%s.mp4", cachePath, card.BvID, today)
|
||||
if file.IsExist(videoFile) {
|
||||
msg = append(msg, message.Video("file:///"+file.BOTPATH+"/"+videoFile))
|
||||
return
|
||||
}
|
||||
data, err = web.RequestDataWithHeaders(web.NewDefaultClient(), bz.SignURL(fmt.Sprintf(bz.VideoDownloadURL, card.BvID, card.CID)), "GET", func(req *http.Request) error {
|
||||
if cookiecfg != nil {
|
||||
cookie := ""
|
||||
cookie, err = cookiecfg.Load()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req.Header.Add("cookie", cookie)
|
||||
}
|
||||
req.Header.Set("User-Agent", ua)
|
||||
return nil
|
||||
}, nil)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = json.Unmarshal(data, &videoDownload)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
headers := fmt.Sprintf("User-Agent: %s\nReferer: %s", ua, bilibiliparseReferer)
|
||||
// 限制最多下载8分钟视频
|
||||
cmd := exec.Command("ffmpeg", "-ss", "0", "-t", "480", "-headers", headers, "-i", videoDownload.Data.Durl[0].URL, "-c", "copy", videoFile)
|
||||
cmd.Stderr = &stderr
|
||||
err = cmd.Run()
|
||||
if err != nil {
|
||||
err = errors.Errorf("未配置ffmpeg,%v", stderr)
|
||||
return
|
||||
}
|
||||
msg = append(msg, message.Video("file:///"+file.BOTPATH+"/"+videoFile))
|
||||
return
|
||||
}
|
||||
418
plugin/bilibilipush/bilibilipush.go
Normal file
418
plugin/bilibilipush/bilibilipush.go
Normal file
@@ -0,0 +1,418 @@
|
||||
// Package bilibilipush b站推送
|
||||
package bilibilipush
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
bz "github.com/FloatTech/AnimeAPI/bilibili"
|
||||
"github.com/FloatTech/floatbox/binary"
|
||||
"github.com/FloatTech/floatbox/web"
|
||||
ctrl "github.com/FloatTech/zbpctrl"
|
||||
"github.com/FloatTech/zbputils/control"
|
||||
"github.com/FloatTech/zbputils/img/text"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/tidwall/gjson"
|
||||
zero "github.com/wdvxdr1123/ZeroBot"
|
||||
"github.com/wdvxdr1123/ZeroBot/message"
|
||||
)
|
||||
|
||||
const (
|
||||
ua = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.107 Safari/537.36"
|
||||
referer = "https://space.bilibili.com/%v"
|
||||
infoURL = "https://api.bilibili.com/x/space/wbi/acc/info?mid=%v"
|
||||
)
|
||||
|
||||
// bdb bilibili推送数据库
|
||||
var bdb *bilibilipushdb
|
||||
|
||||
var (
|
||||
lastTime = map[int64]int64{}
|
||||
liveStatus = map[int64]int{}
|
||||
upMap = map[int64]string{}
|
||||
cfg = bz.NewCookieConfig("data/Bilibili/config.json")
|
||||
)
|
||||
|
||||
func init() {
|
||||
en := control.AutoRegister(&ctrl.Options[*zero.Ctx]{
|
||||
DisableOnDefault: false,
|
||||
Brief: "b站推送",
|
||||
Help: "- 添加b站订阅[uid|name]\n" +
|
||||
"- 取消b站订阅[uid|name]\n" +
|
||||
"- 取消b站动态订阅[uid|name]\n" +
|
||||
"- 取消b站直播订阅[uid|name]\n" +
|
||||
"- b站推送列表\n" +
|
||||
"- [开启|关闭]艾特全体\n" +
|
||||
"Tips: 需要先在 bilibili 插件中设置cookie\n" +
|
||||
"需要配合 job 插件一起使用, 全局只需要设置一个, 无视响应状态推送, 下为例子\n" +
|
||||
"记录在\"@every 5m\"触发的指令\n" +
|
||||
"拉取b站推送",
|
||||
PrivateDataFolder: "bilibilipush",
|
||||
})
|
||||
|
||||
// 加载bilibili推送数据库
|
||||
dbpath := en.DataFolder()
|
||||
dbfile := dbpath + "push.db"
|
||||
bdb = initializePush(dbfile)
|
||||
en.OnFullMatch(`开启艾特全体`, zero.UserOrGrpAdmin, zero.OnlyGroup).SetBlock(true).Handle(func(ctx *zero.Ctx) {
|
||||
gid := ctx.Event.GroupID
|
||||
if err := changeAtAll(gid, 1); err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return
|
||||
}
|
||||
ctx.SendChain(message.Text("已开启艾特全体Oo"))
|
||||
})
|
||||
|
||||
en.OnFullMatch(`关闭艾特全体`, zero.UserOrGrpAdmin, zero.OnlyGroup).SetBlock(true).Handle(func(ctx *zero.Ctx) {
|
||||
gid := ctx.Event.GroupID
|
||||
if err := changeAtAll(gid, 0); err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return
|
||||
}
|
||||
ctx.SendChain(message.Text("已关闭艾特全体Oo"))
|
||||
})
|
||||
|
||||
en.OnRegex(`^添加[B|b]站订阅\s?(.{1,25})$`, zero.UserOrGrpAdmin, bz.RequireUser(cfg)).SetBlock(true).Handle(func(ctx *zero.Ctx) {
|
||||
buid, _ := strconv.ParseInt(ctx.State["uid"].(string), 10, 64)
|
||||
name, err := getName(buid, cfg)
|
||||
if err != nil || name == "" {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return
|
||||
}
|
||||
gid := ctx.Event.GroupID
|
||||
if gid == 0 {
|
||||
gid = -ctx.Event.UserID
|
||||
}
|
||||
if err := subscribe(buid, gid); err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return
|
||||
}
|
||||
ctx.SendChain(message.Text("已添加" + name + "的订阅"))
|
||||
})
|
||||
|
||||
en.OnRegex(`^取消[B|b]站订阅\s?(.{1,25})$`, zero.UserOrGrpAdmin, bz.RequireUser(cfg)).SetBlock(true).Handle(func(ctx *zero.Ctx) {
|
||||
buid, _ := strconv.ParseInt(ctx.State["uid"].(string), 10, 64)
|
||||
name, err := getName(buid, cfg)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return
|
||||
}
|
||||
gid := ctx.Event.GroupID
|
||||
if gid == 0 {
|
||||
gid = -ctx.Event.UserID
|
||||
}
|
||||
if err := unsubscribe(buid, gid); err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return
|
||||
}
|
||||
ctx.SendChain(message.Text("已取消" + name + "的订阅"))
|
||||
})
|
||||
en.OnRegex(`^取消[B|b]站动态订阅\s?(.{1,25})$`, zero.UserOrGrpAdmin, bz.RequireUser(cfg)).SetBlock(true).Handle(func(ctx *zero.Ctx) {
|
||||
buid, _ := strconv.ParseInt(ctx.State["uid"].(string), 10, 64)
|
||||
name, err := getName(buid, cfg)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return
|
||||
}
|
||||
gid := ctx.Event.GroupID
|
||||
if gid == 0 {
|
||||
gid = -ctx.Event.UserID
|
||||
}
|
||||
if err := unsubscribeDynamic(buid, gid); err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return
|
||||
}
|
||||
ctx.SendChain(message.Text("已取消" + name + "的动态订阅"))
|
||||
})
|
||||
en.OnRegex(`^取消[B|b]站直播订阅\s?(.{1,25})$`, zero.UserOrGrpAdmin, bz.RequireUser(cfg)).SetBlock(true).Handle(func(ctx *zero.Ctx) {
|
||||
buid, _ := strconv.ParseInt(ctx.State["uid"].(string), 10, 64)
|
||||
gid := ctx.Event.GroupID
|
||||
if gid == 0 {
|
||||
gid = -ctx.Event.UserID
|
||||
}
|
||||
name, err := getName(buid, cfg)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return
|
||||
}
|
||||
if err := unsubscribeLive(buid, gid); err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return
|
||||
}
|
||||
ctx.SendChain(message.Text("已取消" + name + "的直播订阅"))
|
||||
})
|
||||
|
||||
en.OnRegex(`^[B|b]站推送列表$`, zero.UserOrGrpAdmin).SetBlock(true).Handle(func(ctx *zero.Ctx) {
|
||||
gid := ctx.Event.GroupID
|
||||
if gid == 0 {
|
||||
gid = -ctx.Event.UserID
|
||||
}
|
||||
bpl := bdb.getAllPushByGroup(gid)
|
||||
msg := "--------B站推送列表--------"
|
||||
for _, v := range bpl {
|
||||
if _, ok := upMap[v.BilibiliUID]; !ok {
|
||||
bdb.updateAllUp()
|
||||
}
|
||||
msg += fmt.Sprintf("\nuid:%-12d 动态:", v.BilibiliUID)
|
||||
if v.DynamicDisable == 0 {
|
||||
msg += "●"
|
||||
} else {
|
||||
msg += "○"
|
||||
}
|
||||
msg += " 直播:"
|
||||
if v.LiveDisable == 0 {
|
||||
msg += "●"
|
||||
} else {
|
||||
msg += "○"
|
||||
}
|
||||
msg += " up主:" + upMap[v.BilibiliUID]
|
||||
}
|
||||
data, err := text.RenderToBase64(msg, text.FontFile, 600, 20)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return
|
||||
}
|
||||
if id := ctx.SendChain(message.Image("base64://" + binary.BytesToString(data))); id.ID() == 0 {
|
||||
ctx.SendChain(message.Text("ERROR: 可能被风控了"))
|
||||
}
|
||||
})
|
||||
en.OnRegex(`拉取[B|b]站推送$`).SetBlock(true).Handle(func(ctx *zero.Ctx) {
|
||||
err := sendDynamic(ctx)
|
||||
if err != nil {
|
||||
ctx.SendPrivateMessage(ctx.Event.UserID, message.Text("Error: bilibilipush,", err))
|
||||
}
|
||||
err = sendLive(ctx)
|
||||
if err != nil {
|
||||
ctx.SendPrivateMessage(ctx.Event.UserID, message.Text("Error: bilibilipush,", err))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func changeAtAll(gid int64, b int) (err error) {
|
||||
bpMap := map[string]any{
|
||||
"group_id": gid,
|
||||
"at_all": b,
|
||||
}
|
||||
return bdb.updateAtAll(bpMap)
|
||||
}
|
||||
|
||||
// 取得uid的名字
|
||||
func getName(buid int64, cookiecfg *bz.CookieConfig) (name string, err error) {
|
||||
var ok bool
|
||||
if name, ok = upMap[buid]; !ok {
|
||||
data, err := web.RequestDataWithHeaders(web.NewDefaultClient(), bz.SignURL(fmt.Sprintf(infoURL, buid)), "GET", func(r *http.Request) error {
|
||||
if cookiecfg != nil {
|
||||
cookie := ""
|
||||
cookie, err = cookiecfg.Load()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
r.Header.Add("Cookie", cookie)
|
||||
}
|
||||
r.Header.Set("User-Agent", ua)
|
||||
return nil
|
||||
}, nil)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
status := int(gjson.Get(binary.BytesToString(data), "code").Int())
|
||||
if status != 0 {
|
||||
err = errors.New(gjson.Get(binary.BytesToString(data), "message").String())
|
||||
return "", err
|
||||
}
|
||||
name = gjson.Get(binary.BytesToString(data), "data.name").String()
|
||||
bdb.insertBilibiliUp(buid, name)
|
||||
upMap[buid] = name
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// subscribe 订阅
|
||||
func subscribe(buid, groupid int64) (err error) {
|
||||
bpMap := map[string]any{
|
||||
"bilibili_uid": buid,
|
||||
"group_id": groupid,
|
||||
"live_disable": 0,
|
||||
"dynamic_disable": 0,
|
||||
}
|
||||
return bdb.insertOrUpdateLiveAndDynamic(bpMap)
|
||||
}
|
||||
|
||||
// unsubscribe 取消订阅
|
||||
func unsubscribe(buid, groupid int64) (err error) {
|
||||
bpMap := map[string]any{
|
||||
"bilibili_uid": buid,
|
||||
"group_id": groupid,
|
||||
"live_disable": 1,
|
||||
"dynamic_disable": 1,
|
||||
}
|
||||
return bdb.insertOrUpdateLiveAndDynamic(bpMap)
|
||||
}
|
||||
|
||||
func unsubscribeDynamic(buid, groupid int64) (err error) {
|
||||
bpMap := map[string]any{
|
||||
"bilibili_uid": buid,
|
||||
"group_id": groupid,
|
||||
"dynamic_disable": 1,
|
||||
}
|
||||
return bdb.insertOrUpdateLiveAndDynamic(bpMap)
|
||||
}
|
||||
|
||||
func unsubscribeLive(buid, groupid int64) (err error) {
|
||||
bpMap := map[string]any{
|
||||
"bilibili_uid": buid,
|
||||
"group_id": groupid,
|
||||
"live_disable": 1,
|
||||
}
|
||||
return bdb.insertOrUpdateLiveAndDynamic(bpMap)
|
||||
}
|
||||
|
||||
func getUserDynamicCard(buid int64, cookiecfg *bz.CookieConfig) (cardList []gjson.Result, err error) {
|
||||
data, err := web.RequestDataWithHeaders(web.NewDefaultClient(), fmt.Sprintf(bz.SpaceHistoryURL, buid, 0), "GET", func(req *http.Request) error {
|
||||
if cookiecfg != nil {
|
||||
cookie := ""
|
||||
cookie, err = cookiecfg.Load()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req.Header.Add("Cookie", cookie)
|
||||
}
|
||||
req.Header.Add("User-Agent", ua)
|
||||
return nil
|
||||
}, nil)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
cardList = gjson.Get(binary.BytesToString(data), "data.cards").Array()
|
||||
return
|
||||
}
|
||||
|
||||
func getLiveList(uids ...int64) (string, error) {
|
||||
m := make(map[string]any)
|
||||
m["uids"] = uids
|
||||
b, err := json.Marshal(m)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
data, err := web.PostData(bz.LiveListURL, "application/json", bytes.NewReader(b))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return binary.BytesToString(data), nil
|
||||
}
|
||||
|
||||
func sendDynamic(ctx *zero.Ctx) error {
|
||||
uids := bdb.getAllBuidByDynamic()
|
||||
for _, buid := range uids {
|
||||
time.Sleep(2 * time.Second)
|
||||
cardList, err := getUserDynamicCard(buid, cfg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(cardList) == 0 {
|
||||
return nil
|
||||
}
|
||||
t, ok := lastTime[buid]
|
||||
// 第一次先记录时间,啥也不做
|
||||
if !ok {
|
||||
lastTime[buid] = cardList[0].Get("desc.timestamp").Int()
|
||||
return nil
|
||||
}
|
||||
for i := len(cardList) - 1; i >= 0; i-- {
|
||||
ct := cardList[i].Get("desc.timestamp").Int()
|
||||
if ct > t && ct > time.Now().Unix()-600 {
|
||||
lastTime[buid] = ct
|
||||
m, ok := control.Lookup("bilibilipush")
|
||||
if ok {
|
||||
groupList := bdb.getAllGroupByBuidAndDynamic(buid)
|
||||
dc, err := bz.LoadDynamicDetail(cardList[i].Raw)
|
||||
if err != nil {
|
||||
err = errors.Errorf("动态%v的解析有问题,%v", cardList[i].Get("desc.dynamic_id_str"), err)
|
||||
return err
|
||||
}
|
||||
msg, err := dc.ToMessage()
|
||||
if err != nil {
|
||||
err = errors.Errorf("动态%v的解析有问题,%v", cardList[i].Get("desc.dynamic_id_str"), err)
|
||||
return err
|
||||
}
|
||||
for _, gid := range groupList {
|
||||
if m.IsEnabledIn(gid) {
|
||||
time.Sleep(time.Millisecond * 100)
|
||||
switch {
|
||||
case gid > 0:
|
||||
ctx.SendGroupMessage(gid, msg)
|
||||
case gid < 0:
|
||||
ctx.SendPrivateMessage(-gid, msg)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func sendLive(ctx *zero.Ctx) error {
|
||||
uids := bdb.getAllBuidByLive()
|
||||
ll, err := getLiveList(uids...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
gjson.Get(ll, "data").ForEach(func(key, value gjson.Result) bool {
|
||||
newStatus := int(value.Get("live_status").Int())
|
||||
if newStatus == 2 {
|
||||
newStatus = 0
|
||||
}
|
||||
if _, ok := liveStatus[key.Int()]; !ok {
|
||||
liveStatus[key.Int()] = newStatus
|
||||
return true
|
||||
}
|
||||
oldStatus := liveStatus[key.Int()]
|
||||
if newStatus != oldStatus && newStatus == 1 {
|
||||
liveStatus[key.Int()] = newStatus
|
||||
m, ok := control.Lookup("bilibilipush")
|
||||
if ok {
|
||||
groupList := bdb.getAllGroupByBuidAndLive(key.Int())
|
||||
roomID := value.Get("short_id").Int()
|
||||
if roomID == 0 {
|
||||
roomID = value.Get("room_id").Int()
|
||||
}
|
||||
lURL := bz.LiveURL + strconv.FormatInt(roomID, 10)
|
||||
lName := value.Get("uname").String()
|
||||
lTitle := value.Get("title").String()
|
||||
lCover := value.Get("cover_from_user").String()
|
||||
if lCover == "" {
|
||||
lCover = value.Get("keyframe").String()
|
||||
}
|
||||
var msg []message.Segment
|
||||
msg = append(msg, message.Text(lName+" 正在直播:\n"))
|
||||
msg = append(msg, message.Text(lTitle))
|
||||
msg = append(msg, message.Image(lCover))
|
||||
msg = append(msg, message.Text("直播链接:", lURL))
|
||||
for _, gid := range groupList {
|
||||
if m.IsEnabledIn(gid) {
|
||||
time.Sleep(time.Millisecond * 100)
|
||||
switch {
|
||||
case gid > 0:
|
||||
if res := bdb.getAtAll(gid); res == 1 {
|
||||
msg = append([]message.Segment{message.AtAll()}, msg...)
|
||||
}
|
||||
ctx.SendGroupMessage(gid, msg)
|
||||
case gid < 0:
|
||||
ctx.SendPrivateMessage(-gid, msg)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if newStatus != oldStatus {
|
||||
liveStatus[key.Int()] = newStatus
|
||||
}
|
||||
return true
|
||||
})
|
||||
return nil
|
||||
}
|
||||
@@ -4,11 +4,10 @@ import (
|
||||
"encoding/json"
|
||||
"os"
|
||||
|
||||
_ "github.com/fumiama/sqlite3" // import sql
|
||||
"github.com/jinzhu/gorm"
|
||||
)
|
||||
|
||||
// bilibilipushdb bili推送数据库
|
||||
// bilibilipushdb bilibili推送数据库
|
||||
type bilibilipushdb gorm.DB
|
||||
|
||||
type bilibilipush struct {
|
||||
@@ -34,8 +33,17 @@ func (bilibiliup) TableName() string {
|
||||
return "bilibili_up"
|
||||
}
|
||||
|
||||
// initialize 初始化ScoreDB数据库
|
||||
func initialize(dbpath string) *bilibilipushdb {
|
||||
type bilibiliAt struct {
|
||||
GroupID int64 `gorm:"column:group_id;primary_key" json:"group_id"`
|
||||
AtAll int64 `gorm:"column:at_all;default:0" json:"at_all"`
|
||||
}
|
||||
|
||||
func (bilibiliAt) TableName() string {
|
||||
return "bilibili_at"
|
||||
}
|
||||
|
||||
// initializePush 初始化bilibilipushdb数据库
|
||||
func initializePush(dbpath string) *bilibilipushdb {
|
||||
var err error
|
||||
if _, err = os.Stat(dbpath); err != nil || os.IsNotExist(err) {
|
||||
// 生成文件
|
||||
@@ -49,22 +57,28 @@ func initialize(dbpath string) *bilibilipushdb {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
gdb.AutoMigrate(&bilibilipush{}).AutoMigrate(&bilibiliup{})
|
||||
gdb.AutoMigrate(&bilibilipush{}).AutoMigrate(&bilibiliup{}).AutoMigrate(&bilibiliAt{})
|
||||
return (*bilibilipushdb)(gdb)
|
||||
}
|
||||
|
||||
// insertOrUpdateLiveAndDynamic 插入或更新数据库
|
||||
func (bdb *bilibilipushdb) insertOrUpdateLiveAndDynamic(bpMap map[string]interface{}) (err error) {
|
||||
func (bdb *bilibilipushdb) insertOrUpdateLiveAndDynamic(bpMap map[string]any) (err error) {
|
||||
db := (*gorm.DB)(bdb)
|
||||
bp := bilibilipush{}
|
||||
data, _ := json.Marshal(&bpMap)
|
||||
_ = json.Unmarshal(data, &bp)
|
||||
if err = db.Debug().Model(&bilibilipush{}).First(&bp, "bilibili_uid = ? and group_id = ?", bp.BilibiliUID, bp.GroupID).Error; err != nil {
|
||||
data, err := json.Marshal(&bpMap)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = json.Unmarshal(data, &bp)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if err = db.Model(&bilibilipush{}).First(&bp, "bilibili_uid = ? and group_id = ?", bp.BilibiliUID, bp.GroupID).Error; err != nil {
|
||||
if gorm.IsRecordNotFoundError(err) {
|
||||
err = db.Debug().Model(&bilibilipush{}).Create(&bp).Error
|
||||
err = db.Model(&bilibilipush{}).Create(&bp).Error
|
||||
}
|
||||
} else {
|
||||
err = db.Debug().Model(&bilibilipush{}).Where("bilibili_uid = ? and group_id = ?", bp.BilibiliUID, bp.GroupID).Update(bpMap).Error
|
||||
err = db.Model(&bilibilipush{}).Where("bilibili_uid = ? and group_id = ?", bp.BilibiliUID, bp.GroupID).Update(bpMap).Error
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -72,7 +86,7 @@ func (bdb *bilibilipushdb) insertOrUpdateLiveAndDynamic(bpMap map[string]interfa
|
||||
func (bdb *bilibilipushdb) getAllBuidByLive() (buidList []int64) {
|
||||
db := (*gorm.DB)(bdb)
|
||||
var bpl []bilibilipush
|
||||
db.Debug().Model(&bilibilipush{}).Find(&bpl, "live_disable = 0")
|
||||
db.Model(&bilibilipush{}).Find(&bpl, "live_disable = 0")
|
||||
temp := make(map[int64]bool)
|
||||
for _, v := range bpl {
|
||||
_, ok := temp[v.BilibiliUID]
|
||||
@@ -87,7 +101,7 @@ func (bdb *bilibilipushdb) getAllBuidByLive() (buidList []int64) {
|
||||
func (bdb *bilibilipushdb) getAllBuidByDynamic() (buidList []int64) {
|
||||
db := (*gorm.DB)(bdb)
|
||||
var bpl []bilibilipush
|
||||
db.Debug().Model(&bilibilipush{}).Find(&bpl, "dynamic_disable = 0")
|
||||
db.Model(&bilibilipush{}).Find(&bpl, "dynamic_disable = 0")
|
||||
temp := make(map[int64]bool)
|
||||
for _, v := range bpl {
|
||||
_, ok := temp[v.BilibiliUID]
|
||||
@@ -102,7 +116,7 @@ func (bdb *bilibilipushdb) getAllBuidByDynamic() (buidList []int64) {
|
||||
func (bdb *bilibilipushdb) getAllGroupByBuidAndLive(buid int64) (groupList []int64) {
|
||||
db := (*gorm.DB)(bdb)
|
||||
var bpl []bilibilipush
|
||||
db.Debug().Model(&bilibilipush{}).Find(&bpl, "bilibili_uid = ? and live_disable = 0", buid)
|
||||
db.Model(&bilibilipush{}).Find(&bpl, "bilibili_uid = ? and live_disable = 0", buid)
|
||||
for _, v := range bpl {
|
||||
groupList = append(groupList, v.GroupID)
|
||||
}
|
||||
@@ -112,7 +126,7 @@ func (bdb *bilibilipushdb) getAllGroupByBuidAndLive(buid int64) (groupList []int
|
||||
func (bdb *bilibilipushdb) getAllGroupByBuidAndDynamic(buid int64) (groupList []int64) {
|
||||
db := (*gorm.DB)(bdb)
|
||||
var bpl []bilibilipush
|
||||
db.Debug().Model(&bilibilipush{}).Find(&bpl, "bilibili_uid = ? and dynamic_disable = 0", buid)
|
||||
db.Model(&bilibilipush{}).Find(&bpl, "bilibili_uid = ? and dynamic_disable = 0", buid)
|
||||
for _, v := range bpl {
|
||||
groupList = append(groupList, v.GroupID)
|
||||
}
|
||||
@@ -121,7 +135,36 @@ func (bdb *bilibilipushdb) getAllGroupByBuidAndDynamic(buid int64) (groupList []
|
||||
|
||||
func (bdb *bilibilipushdb) getAllPushByGroup(groupID int64) (bpl []bilibilipush) {
|
||||
db := (*gorm.DB)(bdb)
|
||||
db.Debug().Model(&bilibilipush{}).Find(&bpl, "group_id = ? and (live_disable = 0 or dynamic_disable = 0)", groupID)
|
||||
db.Model(&bilibilipush{}).Find(&bpl, "group_id = ? and (live_disable = 0 or dynamic_disable = 0)", groupID)
|
||||
return
|
||||
}
|
||||
|
||||
func (bdb *bilibilipushdb) getAtAll(groupID int64) (res int64) {
|
||||
db := (*gorm.DB)(bdb)
|
||||
var bpl bilibiliAt
|
||||
db.Model(&bilibilipush{}).Find(&bpl, "group_id = ?", groupID)
|
||||
res = bpl.AtAll
|
||||
return
|
||||
}
|
||||
|
||||
func (bdb *bilibilipushdb) updateAtAll(bpMap map[string]any) (err error) {
|
||||
db := (*gorm.DB)(bdb)
|
||||
bp := bilibiliAt{}
|
||||
data, err := json.Marshal(&bpMap)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = json.Unmarshal(data, &bp)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if err = db.Model(&bilibiliAt{}).First(&bp, "group_id = ?", bp.GroupID).Error; err != nil {
|
||||
if gorm.IsRecordNotFoundError(err) {
|
||||
err = db.Model(&bilibiliAt{}).Create(&bp).Error
|
||||
}
|
||||
} else {
|
||||
err = db.Model(&bilibiliAt{}).Where("group_id = ?", bp.GroupID).Update(bpMap).Error
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -131,13 +174,13 @@ func (bdb *bilibilipushdb) insertBilibiliUp(buid int64, name string) {
|
||||
BilibiliUID: buid,
|
||||
Name: name,
|
||||
}
|
||||
db.Debug().Model(&bilibiliup{}).Create(bu)
|
||||
db.Model(&bilibiliup{}).Create(bu)
|
||||
}
|
||||
|
||||
func (bdb *bilibilipushdb) updateAllUp() {
|
||||
db := (*gorm.DB)(bdb)
|
||||
var bul []bilibiliup
|
||||
db.Debug().Model(&bilibiliup{}).Find(&bul)
|
||||
db.Model(&bilibiliup{}).Find(&bul)
|
||||
for _, v := range bul {
|
||||
upMap[v.BilibiliUID] = v.Name
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
// Package bookreview 书评
|
||||
package bookreview
|
||||
|
||||
import (
|
||||
log "github.com/sirupsen/logrus"
|
||||
zero "github.com/wdvxdr1123/ZeroBot"
|
||||
"github.com/wdvxdr1123/ZeroBot/message"
|
||||
|
||||
"github.com/FloatTech/zbputils/binary"
|
||||
"github.com/FloatTech/zbputils/control"
|
||||
"github.com/FloatTech/zbputils/control/order"
|
||||
"github.com/FloatTech/zbputils/file"
|
||||
"github.com/FloatTech/zbputils/img/text"
|
||||
)
|
||||
|
||||
func init() {
|
||||
engine := control.Register("bookreview", order.AcquirePrio(), &control.Options{
|
||||
DisableOnDefault: false,
|
||||
Help: "哀伤雪刃推书记录\n- 书评[xxx]\n- 随机书评",
|
||||
PublicDataFolder: "BookReview",
|
||||
})
|
||||
|
||||
go func() {
|
||||
dbpath := engine.DataFolder()
|
||||
db.DBPath = dbpath + "bookreview.db"
|
||||
// os.RemoveAll(dbpath)
|
||||
_, _ = file.GetLazyData(db.DBPath, false, true)
|
||||
err := db.Create("bookreview", &book{})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
n, err := db.Count("bookreview")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
log.Printf("[bookreview]读取%d条书评", n)
|
||||
}()
|
||||
|
||||
// 中文、英文、数字但不包括下划线等符号
|
||||
engine.OnRegex("^书评([\u4E00-\u9FA5A-Za-z0-9]{1,25})$").SetBlock(true).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
b := getBookReviewByKeyword(ctx.State["regex_matched"].([]string)[1])
|
||||
data, err := text.RenderToBase64(b.BookReview, text.FontFile, 400, 20)
|
||||
if err != nil {
|
||||
log.Println("err:", err)
|
||||
}
|
||||
if id := ctx.SendChain(message.Image("base64://" + binary.BytesToString(data))); id.ID() == 0 {
|
||||
ctx.SendChain(message.Text("ERROR:可能被风控了"))
|
||||
}
|
||||
})
|
||||
|
||||
engine.OnFullMatch("随机书评").SetBlock(true).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
br := getRandomBookReview()
|
||||
data, err := text.RenderToBase64(br.BookReview, text.FontFile, 400, 20)
|
||||
if err != nil {
|
||||
log.Println("err:", err)
|
||||
}
|
||||
if id := ctx.SendChain(message.Image("base64://" + binary.BytesToString(data))); id.ID() == 0 {
|
||||
ctx.SendChain(message.Text("ERROR:可能被风控了"))
|
||||
}
|
||||
})
|
||||
}
|
||||
76
plugin/bookreview/book_review.go
Normal file
76
plugin/bookreview/book_review.go
Normal file
@@ -0,0 +1,76 @@
|
||||
// Package bookreview 书评
|
||||
package bookreview
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
zero "github.com/wdvxdr1123/ZeroBot"
|
||||
"github.com/wdvxdr1123/ZeroBot/message"
|
||||
|
||||
"github.com/FloatTech/floatbox/binary"
|
||||
fcext "github.com/FloatTech/floatbox/ctxext"
|
||||
sql "github.com/FloatTech/sqlite"
|
||||
ctrl "github.com/FloatTech/zbpctrl"
|
||||
"github.com/FloatTech/zbputils/control"
|
||||
"github.com/FloatTech/zbputils/img/text"
|
||||
)
|
||||
|
||||
func init() {
|
||||
engine := control.AutoRegister(&ctrl.Options[*zero.Ctx]{
|
||||
DisableOnDefault: false,
|
||||
Brief: "哀伤雪刃推书书评",
|
||||
Help: "- 书评[xxx]\n- 随机书评",
|
||||
PublicDataFolder: "BookReview",
|
||||
})
|
||||
|
||||
getdb := fcext.DoOnceOnSuccess(func(ctx *zero.Ctx) bool {
|
||||
db = sql.New(engine.DataFolder() + "bookreview.db")
|
||||
// os.RemoveAll(dbpath)
|
||||
_, _ = engine.GetLazyData("bookreview.db", true)
|
||||
err := db.Open(time.Hour)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return false
|
||||
}
|
||||
err = db.Create("bookreview", &book{})
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return false
|
||||
}
|
||||
n, err := db.Count("bookreview")
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return false
|
||||
}
|
||||
log.Infof("[bookreview]读取%d条书评", n)
|
||||
return true
|
||||
})
|
||||
|
||||
// 中文、英文、数字但不包括下划线等符号
|
||||
engine.OnRegex("^书评([\u4E00-\u9FA5A-Za-z0-9]{1,25})$", getdb).SetBlock(true).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
b := getBookReviewByKeyword(ctx.State["regex_matched"].([]string)[1])
|
||||
data, err := text.RenderToBase64(b.BookReview, text.FontFile, 400, 20)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return
|
||||
}
|
||||
if id := ctx.SendChain(message.Image("base64://" + binary.BytesToString(data))); id.ID() == 0 {
|
||||
ctx.SendChain(message.Text("ERROR: 可能被风控了"))
|
||||
}
|
||||
})
|
||||
|
||||
engine.OnFullMatch("随机书评", getdb).SetBlock(true).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
br := getRandomBookReview()
|
||||
data, err := text.RenderToBase64(br.BookReview, text.FontFile, 400, 20)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return
|
||||
}
|
||||
if id := ctx.SendChain(message.Image("base64://" + binary.BytesToString(data))); id.ID() == 0 {
|
||||
ctx.SendChain(message.Text("ERROR: 可能被风控了"))
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -7,11 +7,11 @@ type book struct {
|
||||
BookReview string `db:"bookreview"`
|
||||
}
|
||||
|
||||
var db = &sql.Sqlite{}
|
||||
var db sql.Sqlite
|
||||
|
||||
// 暂时随机选择一个书评
|
||||
func getBookReviewByKeyword(keyword string) (b book) {
|
||||
_ = db.Find("bookreview", &b, "where bookreview LIKE '%"+keyword+"%'")
|
||||
_ = db.Find("bookreview", &b, "WHERE bookreview LIKE ?", "%"+keyword+"%")
|
||||
return
|
||||
}
|
||||
|
||||
50
plugin/breakrepeat/breakrepeat.go
Normal file
50
plugin/breakrepeat/breakrepeat.go
Normal file
@@ -0,0 +1,50 @@
|
||||
// Package breakrepeat 打断复读
|
||||
package breakrepeat
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"strconv"
|
||||
|
||||
ctrl "github.com/FloatTech/zbpctrl"
|
||||
"github.com/FloatTech/zbputils/control"
|
||||
"github.com/RomiChan/syncx"
|
||||
zero "github.com/wdvxdr1123/ZeroBot"
|
||||
)
|
||||
|
||||
const throttle = 3 // 不可超过 9
|
||||
|
||||
var sm syncx.Map[int64, string]
|
||||
|
||||
func init() {
|
||||
engine := control.AutoRegister(&ctrl.Options[*zero.Ctx]{
|
||||
DisableOnDefault: false,
|
||||
Brief: "打断复读",
|
||||
Help: "- 打断" + strconv.Itoa(throttle) + "次以上复读\n",
|
||||
})
|
||||
engine.On("message/group", zero.OnlyGroup, func(ctx *zero.Ctx) bool {
|
||||
return !zero.HasPicture(ctx)
|
||||
}).SetBlock(false).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
gid := ctx.Event.GroupID
|
||||
raw := ctx.Event.RawMessage
|
||||
r, ok := sm.Load(gid)
|
||||
if !ok || len(r) <= 3 || r[3:] != raw {
|
||||
sm.Store(gid, "0: "+raw)
|
||||
return
|
||||
}
|
||||
c := int(r[0] - '0')
|
||||
if c < throttle {
|
||||
sm.Store(gid, strconv.Itoa(c+1)+": "+raw)
|
||||
return
|
||||
}
|
||||
sm.Delete(gid)
|
||||
if len(r) > 5 {
|
||||
ru := []rune(r[3:])
|
||||
rand.Shuffle(len(ru), func(i, j int) {
|
||||
ru[i], ru[j] = ru[j], ru[i]
|
||||
})
|
||||
r = string(ru)
|
||||
}
|
||||
ctx.Send(r)
|
||||
})
|
||||
}
|
||||
@@ -1,123 +0,0 @@
|
||||
// Package cangtoushi 藏头诗
|
||||
package cangtoushi
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/cookiejar"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
control "github.com/FloatTech/zbputils/control"
|
||||
"github.com/antchfx/htmlquery"
|
||||
log "github.com/sirupsen/logrus"
|
||||
zero "github.com/wdvxdr1123/ZeroBot"
|
||||
"github.com/wdvxdr1123/ZeroBot/message"
|
||||
"github.com/wdvxdr1123/ZeroBot/utils/helper"
|
||||
|
||||
"github.com/FloatTech/zbputils/control/order"
|
||||
)
|
||||
|
||||
const (
|
||||
loginURL = "https://www.shicimingju.com/cangtoushi/"
|
||||
searchURL = "https://www.shicimingju.com/cangtoushi/index.html"
|
||||
ua = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36"
|
||||
referer = "https://www.shicimingju.com/cangtoushi/index.html"
|
||||
)
|
||||
|
||||
var (
|
||||
gCurCookieJar *cookiejar.Jar
|
||||
csrf string
|
||||
)
|
||||
|
||||
func init() {
|
||||
engine := control.Register("cangtoushi", order.AcquirePrio(), &control.Options{
|
||||
DisableOnDefault: false,
|
||||
Help: "藏头诗\n" +
|
||||
"- 藏头诗[xxx]\n- 藏尾诗[xxx]",
|
||||
})
|
||||
engine.OnRegex(`藏头诗\s?([一-龥]{3,10})$`).SetBlock(true).Handle(func(ctx *zero.Ctx) {
|
||||
kw := ctx.State["regex_matched"].([]string)[1]
|
||||
login()
|
||||
data, err := search(kw, "7", "0")
|
||||
if err != nil {
|
||||
log.Errorln("[cangtoushi]:", err)
|
||||
}
|
||||
text := dealHTML(helper.BytesToString(data))
|
||||
ctx.SendChain(message.Text(text))
|
||||
})
|
||||
|
||||
engine.OnRegex(`藏尾诗\s?([一-龥]{3,10})$`).SetBlock(true).Handle(func(ctx *zero.Ctx) {
|
||||
kw := ctx.State["regex_matched"].([]string)[1]
|
||||
login()
|
||||
data, err := search(kw, "7", "2")
|
||||
if err != nil {
|
||||
log.Errorln("[cangtoushi]:", err)
|
||||
}
|
||||
text := dealHTML(helper.BytesToString(data))
|
||||
ctx.SendChain(message.Text(text))
|
||||
})
|
||||
}
|
||||
|
||||
func login() {
|
||||
gCurCookieJar, _ = cookiejar.New(nil)
|
||||
client := &http.Client{
|
||||
Jar: gCurCookieJar,
|
||||
}
|
||||
request, err := http.NewRequest("GET", loginURL, nil)
|
||||
if err != nil {
|
||||
log.Errorln("[cangtoushi]:", err)
|
||||
}
|
||||
request.Header.Add("User-Agent", ua)
|
||||
response, err := client.Do(request)
|
||||
if err != nil {
|
||||
log.Errorln("[cangtoushi]:", err)
|
||||
}
|
||||
data, err := io.ReadAll(response.Body)
|
||||
if err != nil {
|
||||
log.Errorln("[cangtoushi]:", err)
|
||||
}
|
||||
response.Body.Close()
|
||||
doc, err := htmlquery.Parse(strings.NewReader(helper.BytesToString(data)))
|
||||
if err != nil {
|
||||
log.Errorln("[cangtoushi]:", err)
|
||||
}
|
||||
csrf = htmlquery.SelectAttr(htmlquery.FindOne(doc, "//input[@name='_csrf']"), "value")
|
||||
}
|
||||
|
||||
func search(kw, zishu, position string) (data []byte, err error) {
|
||||
postStr := fmt.Sprintf("_csrf=%s&kw=%s&zishu=%s&position=%s", url.QueryEscape(csrf), url.QueryEscape(kw), zishu, position)
|
||||
log.Println("postStr:", postStr)
|
||||
client := &http.Client{
|
||||
Jar: gCurCookieJar,
|
||||
}
|
||||
request, err := http.NewRequest("POST", searchURL, strings.NewReader(postStr))
|
||||
if err != nil {
|
||||
log.Errorln("[cangtoushi]:", err)
|
||||
}
|
||||
request.Header.Add("Referer", referer)
|
||||
request.Header.Add("User-Agent", ua)
|
||||
request.Header.Add("Content-Type", "application/x-www-form-urlencoded")
|
||||
response, err := client.Do(request)
|
||||
if err != nil {
|
||||
log.Errorln("[cangtoushi]:", err)
|
||||
}
|
||||
data, err = io.ReadAll(response.Body)
|
||||
if err != nil {
|
||||
log.Errorln("[cangtoushi]:", err)
|
||||
}
|
||||
response.Body.Close()
|
||||
return
|
||||
}
|
||||
|
||||
func dealHTML(data string) (text string) {
|
||||
doc, err := htmlquery.Parse(strings.NewReader(data))
|
||||
if err != nil {
|
||||
log.Errorln("[cangtoushi]:", err)
|
||||
}
|
||||
text = htmlquery.InnerText(htmlquery.FindOne(doc, "//div[@class='card']/div[@class='card']"))
|
||||
text = strings.ReplaceAll(text, " ", "")
|
||||
text = strings.Replace(text, "\n", "", 1)
|
||||
return text
|
||||
}
|
||||
@@ -6,18 +6,18 @@ import (
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
control "github.com/FloatTech/zbputils/control"
|
||||
ctrl "github.com/FloatTech/zbpctrl"
|
||||
"github.com/FloatTech/zbputils/control"
|
||||
zero "github.com/wdvxdr1123/ZeroBot"
|
||||
"github.com/wdvxdr1123/ZeroBot/extension/rate"
|
||||
"github.com/wdvxdr1123/ZeroBot/message"
|
||||
|
||||
"github.com/FloatTech/zbputils/control/order"
|
||||
)
|
||||
|
||||
var (
|
||||
poke = rate.NewManager(time.Minute*5, 8) // 戳一戳
|
||||
engine = control.Register("chat", order.AcquirePrio(), &control.Options{
|
||||
poke = rate.NewManager[int64](time.Minute*5, 8) // 戳一戳
|
||||
engine = control.AutoRegister(&ctrl.Options[*zero.Ctx]{
|
||||
DisableOnDefault: false,
|
||||
Brief: "基础反应, 群空调",
|
||||
Help: "chat\n- [BOT名字]\n- [戳一戳BOT]\n- 空调开\n- 空调关\n- 群温度\n- 设置温度[正整数]",
|
||||
})
|
||||
)
|
||||
|
||||
108
plugin/chatcount/chatcount.go
Normal file
108
plugin/chatcount/chatcount.go
Normal file
@@ -0,0 +1,108 @@
|
||||
// Package chatcount 聊天时长统计
|
||||
package chatcount
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"image"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"sync"
|
||||
|
||||
zero "github.com/wdvxdr1123/ZeroBot"
|
||||
"github.com/wdvxdr1123/ZeroBot/message"
|
||||
|
||||
"github.com/FloatTech/floatbox/file"
|
||||
"github.com/FloatTech/imgfactory"
|
||||
"github.com/FloatTech/rendercard"
|
||||
ctrl "github.com/FloatTech/zbpctrl"
|
||||
"github.com/FloatTech/zbputils/control"
|
||||
"github.com/FloatTech/zbputils/ctxext"
|
||||
"github.com/FloatTech/zbputils/img/text"
|
||||
)
|
||||
|
||||
const (
|
||||
rankSize = 10
|
||||
)
|
||||
|
||||
func init() {
|
||||
engine := control.AutoRegister(&ctrl.Options[*zero.Ctx]{
|
||||
DisableOnDefault: false,
|
||||
Brief: "聊天时长统计",
|
||||
Help: "- 查询水群@xxx\n- 查看水群排名",
|
||||
PrivateDataFolder: "chatcount",
|
||||
})
|
||||
go func() {
|
||||
ctdb = initialize(engine.DataFolder() + "chatcount.db")
|
||||
}()
|
||||
engine.OnMessage(zero.OnlyGroup).SetBlock(false).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
remindTime, remindFlag := ctdb.updateChatTime(ctx.Event.GroupID, ctx.Event.UserID)
|
||||
if remindFlag {
|
||||
ctx.SendChain(message.At(ctx.Event.UserID), message.Text(fmt.Sprintf("BOT提醒:你今天已经水群%d分钟了!", remindTime)))
|
||||
}
|
||||
})
|
||||
|
||||
engine.OnPrefix(`查询水群`, zero.OnlyGroup).SetBlock(true).Handle(func(ctx *zero.Ctx) {
|
||||
param := ctx.State["args"].(string)
|
||||
var uid int64
|
||||
if len(ctx.Event.Message) > 1 && ctx.Event.Message[1].Type == "at" {
|
||||
uid, _ = strconv.ParseInt(ctx.Event.Message[1].Data["qq"], 10, 64)
|
||||
} else if param == "" {
|
||||
uid = ctx.Event.UserID
|
||||
}
|
||||
name := ctx.NickName()
|
||||
todayTime, todayMessage, totalTime, totalMessage := ctdb.getChatTime(ctx.Event.GroupID, uid)
|
||||
ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text(fmt.Sprintf("%s今天水了%d分%d秒,发了%d条消息;总计水了%d分%d秒,发了%d条消息。", name, todayTime/60, todayTime%60, todayMessage, totalTime/60, totalTime%60, totalMessage)))
|
||||
})
|
||||
engine.OnFullMatch("查看水群排名", zero.OnlyGroup).Limit(ctxext.LimitByGroup).SetBlock(true).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
chatTimeList := ctdb.getChatRank(ctx.Event.GroupID)
|
||||
if len(chatTimeList) == 0 {
|
||||
ctx.SendChain(message.Text("ERROR: 没有水群数据"))
|
||||
return
|
||||
}
|
||||
rankinfo := make([]*rendercard.RankInfo, len(chatTimeList))
|
||||
|
||||
wg := &sync.WaitGroup{}
|
||||
wg.Add(len(chatTimeList))
|
||||
for i := 0; i < len(chatTimeList) && i < rankSize; i++ {
|
||||
go func(i int) {
|
||||
defer wg.Done()
|
||||
resp, err := http.Get("https://q4.qlogo.cn/g?b=qq&nk=" + strconv.FormatInt(chatTimeList[i].UserID, 10) + "&s=100")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
img, _, err := image.Decode(resp.Body)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
rankinfo[i] = &rendercard.RankInfo{
|
||||
TopLeftText: ctx.CardOrNickName(chatTimeList[i].UserID),
|
||||
BottomLeftText: "消息数: " + strconv.FormatInt(chatTimeList[i].TodayMessage, 10) + " 条",
|
||||
RightText: strconv.FormatInt(chatTimeList[i].TodayTime/60, 10) + "分" + strconv.FormatInt(chatTimeList[i].TodayTime%60, 10) + "秒",
|
||||
Avatar: img,
|
||||
}
|
||||
}(i)
|
||||
}
|
||||
wg.Wait()
|
||||
fontbyte, err := file.GetLazyData(text.GlowSansFontFile, control.Md5File, true)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return
|
||||
}
|
||||
img, err := rendercard.DrawRankingCard(fontbyte, "今日水群排行榜", rankinfo)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return
|
||||
}
|
||||
sendimg, err := imgfactory.ToBytes(img)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return
|
||||
}
|
||||
if id := ctx.SendChain(message.ImageBytes(sendimg)); id.ID() == 0 {
|
||||
ctx.SendChain(message.Text("ERROR: 可能被风控了"))
|
||||
}
|
||||
})
|
||||
}
|
||||
225
plugin/chatcount/model.go
Normal file
225
plugin/chatcount/model.go
Normal file
@@ -0,0 +1,225 @@
|
||||
package chatcount
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/RomiChan/syncx"
|
||||
|
||||
"github.com/jinzhu/gorm"
|
||||
)
|
||||
|
||||
const (
|
||||
chatInterval = 300
|
||||
)
|
||||
|
||||
var (
|
||||
// ctdb 聊天时长数据库全局变量
|
||||
ctdb *chattimedb
|
||||
// l 水群提醒时间提醒段,单位分钟
|
||||
l = newLeveler(60, 120, 180, 240, 300)
|
||||
)
|
||||
|
||||
// chattimedb 聊天时长数据库结构体
|
||||
type chattimedb struct {
|
||||
// ctdb.userTimestampMap 每个人发言的时间戳 key=groupID_userID
|
||||
userTimestampMap syncx.Map[string, int64]
|
||||
// ctdb.userTodayTimeMap 每个人今日水群时间 key=groupID_userID
|
||||
userTodayTimeMap syncx.Map[string, int64]
|
||||
// ctdb.userTodayMessageMap 每个人今日水群次数 key=groupID_userID
|
||||
userTodayMessageMap syncx.Map[string, int64]
|
||||
// db 数据库
|
||||
db *gorm.DB
|
||||
// chatmu 读写添加锁
|
||||
chatmu sync.Mutex
|
||||
}
|
||||
|
||||
// initialize 初始化
|
||||
func initialize(dbpath string) *chattimedb {
|
||||
var err error
|
||||
if _, err = os.Stat(dbpath); err != nil || os.IsNotExist(err) {
|
||||
// 生成文件
|
||||
f, err := os.Create(dbpath)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
defer f.Close()
|
||||
}
|
||||
gdb, err := gorm.Open("sqlite3", dbpath)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
gdb.AutoMigrate(&chatTime{})
|
||||
return &chattimedb{
|
||||
db: gdb,
|
||||
}
|
||||
}
|
||||
|
||||
// Close 关闭
|
||||
func (ctdb *chattimedb) Close() error {
|
||||
db := ctdb.db
|
||||
return db.Close()
|
||||
}
|
||||
|
||||
// chatTime 聊天时长,时间的单位都是秒
|
||||
type chatTime struct {
|
||||
ID uint `gorm:"primary_key"`
|
||||
GroupID int64 `gorm:"column:group_id"`
|
||||
UserID int64 `gorm:"column:user_id"`
|
||||
TodayTime int64 `gorm:"-"`
|
||||
TodayMessage int64 `gorm:"-"`
|
||||
TotalTime int64 `gorm:"column:total_time;default:0"`
|
||||
TotalMessage int64 `gorm:"column:total_message;default:0"`
|
||||
}
|
||||
|
||||
// TableName 表名
|
||||
func (chatTime) TableName() string {
|
||||
return "chat_time"
|
||||
}
|
||||
|
||||
// updateChatTime 更新发言时间,todayTime的单位是分钟
|
||||
func (ctdb *chattimedb) updateChatTime(gid, uid int64) (remindTime int64, remindFlag bool) {
|
||||
ctdb.chatmu.Lock()
|
||||
defer ctdb.chatmu.Unlock()
|
||||
db := ctdb.db
|
||||
now := time.Now()
|
||||
keyword := fmt.Sprintf("%v_%v", gid, uid)
|
||||
ts, ok := ctdb.userTimestampMap.Load(keyword)
|
||||
if !ok {
|
||||
ctdb.userTimestampMap.Store(keyword, now.Unix())
|
||||
ctdb.userTodayMessageMap.Store(keyword, 1)
|
||||
return
|
||||
}
|
||||
lastTime := time.Unix(ts, 0)
|
||||
todayTime, _ := ctdb.userTodayTimeMap.Load(keyword)
|
||||
totayMessage, _ := ctdb.userTodayMessageMap.Load(keyword)
|
||||
// 这个消息数是必须统计的
|
||||
ctdb.userTodayMessageMap.Store(keyword, totayMessage+1)
|
||||
st := chatTime{
|
||||
GroupID: gid,
|
||||
UserID: uid,
|
||||
TotalTime: todayTime,
|
||||
TotalMessage: totayMessage,
|
||||
}
|
||||
|
||||
// 如果不是同一天,把TotalTime,TotalMessage重置
|
||||
if lastTime.YearDay() != now.YearDay() {
|
||||
if err := db.Model(&st).Where("group_id = ? and user_id = ?", gid, uid).First(&st).Error; err != nil {
|
||||
if gorm.IsRecordNotFoundError(err) {
|
||||
db.Model(&st).Create(&st)
|
||||
}
|
||||
} else {
|
||||
db.Model(&st).Where("group_id = ? and user_id = ?", gid, uid).Update(
|
||||
map[string]any{
|
||||
"total_time": st.TotalTime + todayTime,
|
||||
"total_message": st.TotalMessage + totayMessage,
|
||||
})
|
||||
}
|
||||
ctdb.userTimestampMap.Store(keyword, now.Unix())
|
||||
ctdb.userTodayTimeMap.Delete(keyword)
|
||||
ctdb.userTodayMessageMap.Delete(keyword)
|
||||
return
|
||||
}
|
||||
|
||||
userChatTime := int64(now.Sub(lastTime).Seconds())
|
||||
// 当聊天时间在一定范围内的话,则计入时长
|
||||
if userChatTime < chatInterval {
|
||||
ctdb.userTodayTimeMap.Store(keyword, todayTime+userChatTime)
|
||||
remindTime = (todayTime + userChatTime) / 60
|
||||
remindFlag = l.level(int((todayTime+userChatTime)/60)) > l.level(int(todayTime/60))
|
||||
}
|
||||
ctdb.userTimestampMap.Store(keyword, now.Unix())
|
||||
return
|
||||
}
|
||||
|
||||
// getChatTime 获得用户聊天时长和消息次数,todayTime,totalTime的单位是秒,todayMessage,totalMessage单位是条数
|
||||
func (ctdb *chattimedb) getChatTime(gid, uid int64) (todayTime, todayMessage, totalTime, totalMessage int64) {
|
||||
ctdb.chatmu.Lock()
|
||||
defer ctdb.chatmu.Unlock()
|
||||
db := ctdb.db
|
||||
st := chatTime{}
|
||||
db.Model(&st).Where("group_id = ? and user_id = ?", gid, uid).First(&st)
|
||||
keyword := fmt.Sprintf("%v_%v", gid, uid)
|
||||
todayTime, _ = ctdb.userTodayTimeMap.Load(keyword)
|
||||
todayMessage, _ = ctdb.userTodayMessageMap.Load(keyword)
|
||||
totalTime = st.TotalTime
|
||||
totalMessage = st.TotalMessage
|
||||
return
|
||||
}
|
||||
|
||||
// getChatRank 获得水群排名,时间单位为秒
|
||||
func (ctdb *chattimedb) getChatRank(gid int64) (chatTimeList []chatTime) {
|
||||
ctdb.chatmu.Lock()
|
||||
defer ctdb.chatmu.Unlock()
|
||||
chatTimeList = make([]chatTime, 0, 100)
|
||||
keyList := make([]string, 0, 100)
|
||||
ctdb.userTimestampMap.Range(func(key string, value int64) bool {
|
||||
t := time.Unix(value, 0)
|
||||
if strings.Contains(key, strconv.FormatInt(gid, 10)) && t.YearDay() == time.Now().YearDay() {
|
||||
keyList = append(keyList, key)
|
||||
}
|
||||
return true
|
||||
})
|
||||
for _, v := range keyList {
|
||||
_, a, _ := strings.Cut(v, "_")
|
||||
uid, _ := strconv.ParseInt(a, 10, 64)
|
||||
todayTime, _ := ctdb.userTodayTimeMap.Load(v)
|
||||
todayMessage, _ := ctdb.userTodayMessageMap.Load(v)
|
||||
chatTimeList = append(chatTimeList, chatTime{
|
||||
GroupID: gid,
|
||||
UserID: uid,
|
||||
TodayTime: todayTime,
|
||||
TodayMessage: todayMessage,
|
||||
})
|
||||
}
|
||||
sort.Sort(sortChatTime(chatTimeList))
|
||||
return
|
||||
}
|
||||
|
||||
// leveler 结构体,包含一个 levelArray 字段
|
||||
type leveler struct {
|
||||
levelArray []int
|
||||
}
|
||||
|
||||
// newLeveler 构造函数,用于创建 Leveler 实例
|
||||
func newLeveler(levels ...int) *leveler {
|
||||
return &leveler{
|
||||
levelArray: levels,
|
||||
}
|
||||
}
|
||||
|
||||
// level 方法,封装了 getLevel 函数的逻辑
|
||||
func (l *leveler) level(t int) int {
|
||||
for i := len(l.levelArray) - 1; i >= 0; i-- {
|
||||
if t >= l.levelArray[i] {
|
||||
return i + 1
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// sortChatTime chatTime排序数组
|
||||
type sortChatTime []chatTime
|
||||
|
||||
// Len 实现 sort.Interface
|
||||
func (a sortChatTime) Len() int {
|
||||
return len(a)
|
||||
}
|
||||
|
||||
// Less 实现 sort.Interface,按 TodayTime 降序,TodayMessage 降序
|
||||
func (a sortChatTime) Less(i, j int) bool {
|
||||
if a[i].TodayTime == a[j].TodayTime {
|
||||
return a[i].TodayMessage > a[j].TodayMessage
|
||||
}
|
||||
return a[i].TodayTime > a[j].TodayTime
|
||||
}
|
||||
|
||||
// Swap 实现 sort.Interface
|
||||
func (a sortChatTime) Swap(i, j int) {
|
||||
a[i], a[j] = a[j], a[i]
|
||||
}
|
||||
173
plugin/chess/chess.go
Normal file
173
plugin/chess/chess.go
Normal file
@@ -0,0 +1,173 @@
|
||||
// Package chess 国际象棋
|
||||
package chess
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
ctrl "github.com/FloatTech/zbpctrl"
|
||||
"github.com/FloatTech/zbputils/control"
|
||||
"github.com/FloatTech/zbputils/ctxext"
|
||||
zero "github.com/wdvxdr1123/ZeroBot"
|
||||
"github.com/wdvxdr1123/ZeroBot/message"
|
||||
)
|
||||
|
||||
const helpString = `- 参与/创建一盘游戏:「下棋」(chess)
|
||||
- 参与/创建一盘盲棋:「盲棋」(blind)
|
||||
- 投降认输:「认输」 (resign)
|
||||
- 请求、接受和棋:「和棋」 (draw)
|
||||
- 走棋:!Nxf3 中英文感叹号均可,格式请参考“代数记谱法”(Algebraic notation)
|
||||
- 中断对局:「中断」 (abort)(仅群主/管理员有效)
|
||||
- 查看等级分排行榜:「排行榜」(ranking)
|
||||
- 查看自己的等级分:「等级分」(rate)
|
||||
- 清空等级分:「清空等级分 QQ号」(.clean.rate) (仅超管有效)`
|
||||
|
||||
var (
|
||||
limit = ctxext.NewLimiterManager(time.Microsecond*2500, 1)
|
||||
tempFileDir string
|
||||
engine = control.AutoRegister(&ctrl.Options[*zero.Ctx]{
|
||||
DisableOnDefault: false,
|
||||
Brief: "国际象棋",
|
||||
Help: helpString,
|
||||
PrivateDataFolder: "chess",
|
||||
}).ApplySingle(ctxext.GroupSingle)
|
||||
)
|
||||
|
||||
func init() {
|
||||
// 初始化临时文件夹
|
||||
tempFileDir = path.Join(engine.DataFolder(), "temp")
|
||||
err := os.MkdirAll(tempFileDir, 0750)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
// 初始化数据库
|
||||
dbFilePath := engine.DataFolder() + "chess.db"
|
||||
initDatabase(dbFilePath)
|
||||
// 注册指令
|
||||
engine.OnFullMatchGroup([]string{"下棋", "chess"}, zero.OnlyGroup).SetBlock(true).Limit(limit.LimitByGroup).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
if ctx.Event.Sender == nil {
|
||||
return
|
||||
}
|
||||
userUin := ctx.Event.UserID
|
||||
userName := ctx.Event.Sender.NickName
|
||||
groupCode := ctx.Event.GroupID
|
||||
replyMessage, err := game(groupCode, userUin, userName)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return
|
||||
}
|
||||
ctx.Send(replyMessage)
|
||||
})
|
||||
|
||||
engine.OnFullMatchGroup([]string{"认输", "resign"}, zero.OnlyGroup).SetBlock(true).Limit(limit.LimitByGroup).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
userUin := ctx.Event.UserID
|
||||
groupCode := ctx.Event.GroupID
|
||||
replyMessage, err := resign(groupCode, userUin)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return
|
||||
}
|
||||
ctx.Send(replyMessage)
|
||||
})
|
||||
|
||||
engine.OnFullMatchGroup([]string{"和棋", "draw"}, zero.OnlyGroup).SetBlock(true).Limit(limit.LimitByGroup).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
userUin := ctx.Event.UserID
|
||||
groupCode := ctx.Event.GroupID
|
||||
replyMessage, err := draw(groupCode, userUin)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return
|
||||
}
|
||||
ctx.Send(replyMessage)
|
||||
})
|
||||
|
||||
engine.OnFullMatchGroup([]string{"中断", "abort"}, zero.OnlyGroup, zero.AdminPermission).SetBlock(true).Limit(limit.LimitByGroup).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
groupCode := ctx.Event.GroupID
|
||||
replyMessage, err := abort(groupCode)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return
|
||||
}
|
||||
ctx.Send(replyMessage)
|
||||
})
|
||||
|
||||
engine.OnFullMatchGroup([]string{"盲棋", "blind"}, zero.OnlyGroup).SetBlock(true).Limit(limit.LimitByGroup).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
if ctx.Event.Sender == nil {
|
||||
return
|
||||
}
|
||||
userUin := ctx.Event.UserID
|
||||
userName := ctx.Event.Sender.NickName
|
||||
groupCode := ctx.Event.GroupID
|
||||
replyMessage, err := blindfold(groupCode, userUin, userName)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return
|
||||
}
|
||||
ctx.Send(replyMessage)
|
||||
})
|
||||
|
||||
engine.OnRegex("^[!|!]([0-8]|[R|N|B|Q|K|O|a-h|x]|[-|=|+])+$", zero.OnlyGroup).SetBlock(true).Limit(limit.LimitByGroup).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
userUin := ctx.Event.UserID
|
||||
groupCode := ctx.Event.GroupID
|
||||
userMsgStr := ctx.State["regex_matched"].([]string)[0]
|
||||
moveStr := strings.TrimPrefix(strings.TrimPrefix(userMsgStr, "!"), "!")
|
||||
replyMessage, err := play(groupCode, userUin, moveStr)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return
|
||||
}
|
||||
ctx.Send(replyMessage)
|
||||
})
|
||||
|
||||
engine.OnFullMatchGroup([]string{"排行榜", "ranking"}).SetBlock(true).Limit(limit.LimitByUser).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
replyMessage, err := getRanking()
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return
|
||||
}
|
||||
ctx.Send(replyMessage)
|
||||
})
|
||||
|
||||
engine.OnFullMatchGroup([]string{"等级分", "rate"}).SetBlock(true).Limit(limit.LimitByUser).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
if ctx.Event.Sender == nil {
|
||||
return
|
||||
}
|
||||
userUin := ctx.Event.UserID
|
||||
userName := ctx.Event.Sender.NickName
|
||||
replyMessage, err := rate(userUin, userName)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return
|
||||
}
|
||||
ctx.Send(replyMessage)
|
||||
})
|
||||
|
||||
engine.OnPrefixGroup([]string{"清空等级分", ".clean.rate"}, zero.SuperUserPermission).SetBlock(true).
|
||||
Limit(limit.LimitByUser).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
args := ctx.State["args"].(string)
|
||||
playerUin, err := strconv.ParseInt(strings.TrimSpace(args), 10, 64)
|
||||
if err != nil || playerUin <= 0 {
|
||||
ctx.Send(fmt.Sprintf("解析失败「%s」不是正确的 QQ 号。", args))
|
||||
return
|
||||
}
|
||||
replyMessage, err := cleanUserRate(playerUin)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return
|
||||
}
|
||||
ctx.Send(replyMessage)
|
||||
})
|
||||
}
|
||||
637
plugin/chess/core.go
Normal file
637
plugin/chess/core.go
Normal file
@@ -0,0 +1,637 @@
|
||||
package chess
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"image/color"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/FloatTech/floatbox/binary"
|
||||
"github.com/FloatTech/floatbox/file"
|
||||
"github.com/FloatTech/zbputils/control"
|
||||
"github.com/FloatTech/zbputils/img/text"
|
||||
|
||||
"github.com/RomiChan/syncx"
|
||||
"github.com/jinzhu/gorm"
|
||||
resvg "github.com/kanrichan/resvg-go"
|
||||
"github.com/notnil/chess"
|
||||
cimage "github.com/notnil/chess/image"
|
||||
"github.com/wdvxdr1123/ZeroBot/message"
|
||||
)
|
||||
|
||||
const eloDefault = 500
|
||||
|
||||
var (
|
||||
chessRoomMap syncx.Map[int64, *chessRoom]
|
||||
errNotExist = errors.New("对局不存在, 发送「下棋」或「chess」可创建对局。")
|
||||
)
|
||||
|
||||
type chessRoom struct {
|
||||
chessGame *chess.Game
|
||||
whitePlayer int64
|
||||
whiteName string
|
||||
blackPlayer int64
|
||||
blackName string
|
||||
drawPlayer int64
|
||||
lastMoveTime int64
|
||||
isBlindfold bool
|
||||
whiteErr bool // 违例记录(盲棋用)
|
||||
blackErr bool
|
||||
}
|
||||
|
||||
// game 下棋
|
||||
func game(groupCode, senderUin int64, senderName string) (message.Message, error) {
|
||||
return createGame(false, groupCode, senderUin, senderName)
|
||||
}
|
||||
|
||||
// blindfold 盲棋
|
||||
func blindfold(groupCode, senderUin int64, senderName string) (message.Message, error) {
|
||||
return createGame(true, groupCode, senderUin, senderName)
|
||||
}
|
||||
|
||||
// abort 中断对局
|
||||
func abort(groupCode int64) (message.Message, error) {
|
||||
if room, ok := chessRoomMap.Load(groupCode); ok {
|
||||
return abortGame(*room, groupCode, "对局已被管理员中断, 游戏结束。")
|
||||
}
|
||||
return nil, errNotExist
|
||||
}
|
||||
|
||||
// draw 和棋
|
||||
func draw(groupCode, senderUin int64) (msg message.Message, err error) {
|
||||
msg = message.Message{message.At(senderUin)}
|
||||
// 检查对局是否存在
|
||||
room, ok := chessRoomMap.Load(groupCode)
|
||||
if !ok {
|
||||
return nil, errNotExist
|
||||
}
|
||||
// 检查消息发送者是否为对局中的玩家
|
||||
if senderUin != room.whitePlayer && senderUin != room.blackPlayer {
|
||||
return
|
||||
}
|
||||
// 处理和棋逻辑
|
||||
room.lastMoveTime = time.Now().Unix()
|
||||
if room.drawPlayer == 0 {
|
||||
room.drawPlayer = senderUin
|
||||
chessRoomMap.Store(groupCode, room)
|
||||
msg = append(msg, message.Text("请求和棋, 发送「和棋」或「draw」接受和棋。走棋视为拒绝和棋。"))
|
||||
return
|
||||
}
|
||||
if room.drawPlayer == senderUin {
|
||||
return
|
||||
}
|
||||
err = room.chessGame.Draw(chess.DrawOffer)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
chessString := getChessString(*room)
|
||||
eloString := ""
|
||||
if len(room.chessGame.Moves()) > 4 {
|
||||
// 若走子次数超过 4 认为是有效对局, 存入数据库
|
||||
dbService := newDBService()
|
||||
if err = dbService.createPGN(chessString, room.whitePlayer, room.blackPlayer, room.whiteName, room.blackName); err != nil {
|
||||
return
|
||||
}
|
||||
whiteScore, blackScore := 0.5, 0.5
|
||||
eloString, err = getELOString(*room, whiteScore, blackScore)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
msg = append(msg, message.Text("接受和棋, 游戏结束。\n", eloString, chessString))
|
||||
chessRoomMap.Delete(groupCode)
|
||||
return
|
||||
}
|
||||
|
||||
// resign 认输
|
||||
func resign(groupCode, senderUin int64) (msg message.Message, err error) {
|
||||
msg = message.Message{message.At(senderUin)}
|
||||
// 检查对局是否存在
|
||||
room, ok := chessRoomMap.Load(groupCode)
|
||||
if !ok {
|
||||
return nil, errNotExist
|
||||
}
|
||||
// 检查是否是当前游戏玩家
|
||||
if senderUin != room.whitePlayer && senderUin != room.blackPlayer {
|
||||
return
|
||||
}
|
||||
// 如果对局未建立, 中断对局
|
||||
if room.whitePlayer == 0 || room.blackPlayer == 0 {
|
||||
chessRoomMap.Delete(groupCode)
|
||||
msg = append(msg, message.Text("对局结束"))
|
||||
return
|
||||
}
|
||||
// 计算认输方
|
||||
var resignColor chess.Color
|
||||
if senderUin == room.whitePlayer {
|
||||
resignColor = chess.White
|
||||
} else {
|
||||
resignColor = chess.Black
|
||||
}
|
||||
if isAprilFoolsDay() {
|
||||
if resignColor == chess.White {
|
||||
resignColor = chess.Black
|
||||
} else {
|
||||
resignColor = chess.White
|
||||
}
|
||||
}
|
||||
room.chessGame.Resign(resignColor)
|
||||
chessString := getChessString(*room)
|
||||
eloString := ""
|
||||
if len(room.chessGame.Moves()) > 4 {
|
||||
// 若走子次数超过 4 认为是有效对局, 存入数据库
|
||||
dbService := newDBService()
|
||||
if err = dbService.createPGN(chessString, room.whitePlayer, room.blackPlayer, room.whiteName, room.blackName); err != nil {
|
||||
return
|
||||
}
|
||||
whiteScore, blackScore := 1.0, 1.0
|
||||
if resignColor == chess.White {
|
||||
whiteScore = 0.0
|
||||
} else {
|
||||
blackScore = 0.0
|
||||
}
|
||||
eloString, err = getELOString(*room, whiteScore, blackScore)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
msg = append(msg, message.Text("认输, 游戏结束。\n", eloString, chessString))
|
||||
if isAprilFoolsDay() {
|
||||
msg = append(msg, message.Text("对手认输, 游戏结束, 你胜利了。\n", eloString, chessString))
|
||||
}
|
||||
chessRoomMap.Delete(groupCode)
|
||||
return
|
||||
}
|
||||
|
||||
// play 走棋
|
||||
func play(groupCode, senderUin int64, moveStr string) (msg message.Message, err error) {
|
||||
msg = message.Message{message.At(senderUin)}
|
||||
// 检查对局是否存在
|
||||
room, ok := chessRoomMap.Load(groupCode)
|
||||
if !ok {
|
||||
return nil, errNotExist
|
||||
}
|
||||
// 不是对局中的玩家, 忽略消息
|
||||
if (senderUin != room.whitePlayer) && (senderUin != room.blackPlayer) && !isAprilFoolsDay() {
|
||||
return
|
||||
}
|
||||
// 对局未建立
|
||||
if (room.whitePlayer == 0) || (room.blackPlayer == 0) {
|
||||
msg = append(msg, message.Text("请等候其他玩家加入游戏。"))
|
||||
return
|
||||
}
|
||||
// 需要对手走棋
|
||||
if ((senderUin == room.whitePlayer) && (room.chessGame.Position().Turn() != chess.White)) || ((senderUin == room.blackPlayer) && (room.chessGame.Position().Turn() != chess.Black)) {
|
||||
msg = append(msg, message.Text("请等待对手走棋。"))
|
||||
return
|
||||
}
|
||||
room.lastMoveTime = time.Now().Unix()
|
||||
// 走棋
|
||||
if err = room.chessGame.MoveStr(moveStr); err != nil {
|
||||
// 指令错误时检查
|
||||
if !room.isBlindfold {
|
||||
// 未开启盲棋, 提示指令错误
|
||||
msg = append(msg, message.Text("移动「", moveStr, "」违规, 请检查, 格式请参考「代数记谱法」(Algebraic notation)。"))
|
||||
return
|
||||
}
|
||||
// 开启盲棋, 判断违例情况
|
||||
var currentPlayerColor chess.Color
|
||||
if senderUin == room.whitePlayer {
|
||||
currentPlayerColor = chess.White
|
||||
} else {
|
||||
currentPlayerColor = chess.Black
|
||||
}
|
||||
// 第一次违例, 提示
|
||||
_flag := false
|
||||
if (currentPlayerColor == chess.White) && !room.whiteErr {
|
||||
room.whiteErr = true
|
||||
chessRoomMap.Store(groupCode, room)
|
||||
_flag = true
|
||||
}
|
||||
if (currentPlayerColor == chess.Black) && !room.blackErr {
|
||||
room.blackErr = true
|
||||
chessRoomMap.Store(groupCode, room)
|
||||
_flag = true
|
||||
}
|
||||
if _flag {
|
||||
msg = append(msg, message.Text("移动「", moveStr, "」违规, 再次违规会立即判负。"))
|
||||
return
|
||||
}
|
||||
// 出现多次违例, 判负
|
||||
room.chessGame.Resign(currentPlayerColor)
|
||||
chessString := getChessString(*room)
|
||||
msg = append(msg, message.Text("违规两次,游戏结束。\n", chessString))
|
||||
|
||||
chessRoomMap.Delete(groupCode)
|
||||
return
|
||||
}
|
||||
// 走子之后, 视为拒绝和棋
|
||||
if room.drawPlayer != 0 {
|
||||
room.drawPlayer = 0
|
||||
chessRoomMap.Store(groupCode, room)
|
||||
}
|
||||
// 生成棋盘图片
|
||||
var boardImgEle message.Segment
|
||||
if !room.isBlindfold {
|
||||
boardImgEle, err = getBoardElement(groupCode)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
// 检查游戏是否结束
|
||||
if room.chessGame.Method() != chess.NoMethod {
|
||||
whiteScore, blackScore := 0.5, 0.5
|
||||
var msgBuilder strings.Builder
|
||||
msgBuilder.WriteString("游戏结束, ")
|
||||
switch room.chessGame.Method() {
|
||||
case chess.FivefoldRepetition:
|
||||
msgBuilder.WriteString("和棋, 因为五次重复走子。\n")
|
||||
case chess.SeventyFiveMoveRule:
|
||||
msgBuilder.WriteString("和棋, 因为七十五步规则。\n")
|
||||
case chess.InsufficientMaterial:
|
||||
msgBuilder.WriteString("和棋, 因为不可能将死。\n")
|
||||
case chess.Stalemate:
|
||||
msgBuilder.WriteString("和棋, 因为逼和(无子可动和棋)。\n")
|
||||
case chess.Checkmate:
|
||||
var winner string
|
||||
if room.chessGame.Position().Turn() == chess.White {
|
||||
whiteScore = 0.0
|
||||
blackScore = 1.0
|
||||
winner = "黑方"
|
||||
} else {
|
||||
whiteScore = 1.0
|
||||
blackScore = 0.0
|
||||
winner = "白方"
|
||||
}
|
||||
msgBuilder.WriteString(winner)
|
||||
msgBuilder.WriteString("胜利, 因为将杀。\n")
|
||||
case chess.NoMethod:
|
||||
case chess.Resignation:
|
||||
case chess.DrawOffer:
|
||||
case chess.ThreefoldRepetition:
|
||||
case chess.FiftyMoveRule:
|
||||
default:
|
||||
}
|
||||
chessString := getChessString(*room)
|
||||
eloString := ""
|
||||
if len(room.chessGame.Moves()) > 4 {
|
||||
// 若走子次数超过 4 认为是有效对局, 存入数据库
|
||||
dbService := newDBService()
|
||||
if err = dbService.createPGN(chessString, room.whitePlayer, room.blackPlayer, room.whiteName, room.blackName); err != nil {
|
||||
return
|
||||
}
|
||||
// 仅有效对局才会计算等级分
|
||||
eloString, err = getELOString(*room, whiteScore, blackScore)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
msgBuilder.WriteString(eloString)
|
||||
msgBuilder.WriteString(chessString)
|
||||
msg = append(msg, message.Text(msgBuilder.String()))
|
||||
if !room.isBlindfold {
|
||||
msg = append(msg, boardImgEle)
|
||||
}
|
||||
|
||||
chessRoomMap.Delete(groupCode)
|
||||
return
|
||||
}
|
||||
// 提示玩家继续游戏
|
||||
var currentPlayer int64
|
||||
if room.chessGame.Position().Turn() == chess.White {
|
||||
currentPlayer = room.whitePlayer
|
||||
} else {
|
||||
currentPlayer = room.blackPlayer
|
||||
}
|
||||
msg = message.Message{message.At(currentPlayer), message.Text("对手已走子, 游戏继续。"), boardImgEle}
|
||||
return
|
||||
}
|
||||
|
||||
// rate 获取等级分
|
||||
func rate(senderUin int64, senderName string) (msg message.Message, err error) {
|
||||
rate := 0
|
||||
dbService := newDBService()
|
||||
rate, err = dbService.getELORateByUin(senderUin)
|
||||
if err != nil {
|
||||
if err != gorm.ErrRecordNotFound {
|
||||
err = errors.New("无法获取等级分信息。")
|
||||
return
|
||||
}
|
||||
err = errors.New("没有查找到等级分信息, 请至少进行一局对局。")
|
||||
}
|
||||
msg = append(msg, message.Text("玩家「", senderName, "」目前的等级分: ", rate))
|
||||
return
|
||||
}
|
||||
|
||||
// cleanUserRate 清空用户等级分
|
||||
func cleanUserRate(senderUin int64) (msg message.Message, err error) {
|
||||
dbService := newDBService()
|
||||
err = dbService.cleanELOByUin(senderUin)
|
||||
if err != nil {
|
||||
if err != gorm.ErrRecordNotFound {
|
||||
err = errors.New("无法清空等级分。")
|
||||
return
|
||||
}
|
||||
err = errors.New("没有查找到等级分信息, 请检查用户 uid 是否正确。")
|
||||
}
|
||||
msg = append(msg, message.Text("已清空用户「", senderUin, "」的等级分。"))
|
||||
return
|
||||
}
|
||||
|
||||
// createGame 创建游戏
|
||||
func createGame(isBlindfold bool, groupCode, senderUin int64, senderName string) (msg message.Message, err error) {
|
||||
room, ok := chessRoomMap.Load(groupCode)
|
||||
if !ok {
|
||||
chessRoomMap.Store(groupCode, &chessRoom{
|
||||
chessGame: chess.NewGame(),
|
||||
whitePlayer: senderUin,
|
||||
whiteName: senderName,
|
||||
blackPlayer: 0,
|
||||
blackName: "",
|
||||
drawPlayer: 0,
|
||||
lastMoveTime: time.Now().Unix(),
|
||||
isBlindfold: isBlindfold,
|
||||
whiteErr: false,
|
||||
blackErr: false,
|
||||
})
|
||||
text := "已创建新的对局, 发送「下棋」或「chess」可加入对局。"
|
||||
if isBlindfold {
|
||||
text = "已创建新的盲棋对局, 发送「盲棋」或「blind」可加入对局。"
|
||||
}
|
||||
msg = append(msg, message.Text(text))
|
||||
return
|
||||
}
|
||||
msg = message.Message{message.At(senderUin)}
|
||||
if room.blackPlayer != 0 {
|
||||
// 检测对局是否已存在超过 6 小时
|
||||
if (time.Now().Unix() - room.lastMoveTime) > 21600 {
|
||||
msg, err = abortGame(*room, groupCode, "对局已存在超过 6 小时, 游戏结束。")
|
||||
msg = append(msg, message.Text("\n\n已有对局已被中断, 如需创建新对局请重新发送指令。"))
|
||||
msg = append(msg, message.At(senderUin))
|
||||
return
|
||||
}
|
||||
// 对局在进行
|
||||
msg = append(msg, message.Text("对局已在进行中, 无法创建或加入对局, 当前对局玩家为: "))
|
||||
if room.whitePlayer != 0 {
|
||||
msg = append(msg, message.At(room.whitePlayer))
|
||||
}
|
||||
if room.blackPlayer != 0 {
|
||||
msg = append(msg, message.At(room.blackPlayer))
|
||||
}
|
||||
msg = append(msg, message.Text(", 群主或管理员发送「中断」或「abort」可中断对局(自动判和)。"))
|
||||
return
|
||||
}
|
||||
if senderUin == room.whitePlayer {
|
||||
msg = append(msg, message.Text("请等候其他玩家加入游戏。"))
|
||||
return
|
||||
}
|
||||
if room.isBlindfold && !isBlindfold {
|
||||
msg = append(msg, message.Text("已创建盲棋对局, 请加入或等待盲棋对局结束之后创建普通对局。"))
|
||||
return
|
||||
}
|
||||
if !room.isBlindfold && isBlindfold {
|
||||
msg = append(msg, message.Text("已创建普通对局, 请加入或等待普通对局结束之后创建盲棋对局。"))
|
||||
return
|
||||
}
|
||||
room.blackPlayer = senderUin
|
||||
room.blackName = senderName
|
||||
chessRoomMap.Store(groupCode, room)
|
||||
var boardImgEle message.Segment
|
||||
if !room.isBlindfold {
|
||||
boardImgEle, err = getBoardElement(groupCode)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
msg = append(msg, message.Text("黑棋已加入对局, 请白方下棋。"), message.At(room.whitePlayer))
|
||||
if !isBlindfold {
|
||||
msg = append(msg, boardImgEle)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// abortGame 中断游戏
|
||||
func abortGame(room chessRoom, groupCode int64, hint string) (message.Message, error) {
|
||||
var msg message.Message
|
||||
err := room.chessGame.Draw(chess.DrawOffer)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
chessString := getChessString(room)
|
||||
if len(room.chessGame.Moves()) > 4 {
|
||||
dbService := newDBService()
|
||||
if err := dbService.createPGN(chessString, room.whitePlayer, room.blackPlayer, room.whiteName, room.blackName); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
chessRoomMap.Delete(groupCode)
|
||||
msg = append(msg, message.Text(hint))
|
||||
if room.whitePlayer != 0 {
|
||||
msg = append(msg, message.At(room.whitePlayer))
|
||||
}
|
||||
if room.blackPlayer != 0 {
|
||||
msg = append(msg, message.At(room.blackPlayer))
|
||||
}
|
||||
msg = append(msg, message.Text("\n\n"+chessString))
|
||||
return msg, nil
|
||||
}
|
||||
|
||||
// getBoardElement 获取棋盘图片的消息内容
|
||||
func getBoardElement(groupCode int64) (imgMsg message.Segment, err error) {
|
||||
fontdata, err := file.GetLazyData(text.GNUUnifontFontFile, control.Md5File, true)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
room, ok := chessRoomMap.Load(groupCode)
|
||||
if !ok {
|
||||
return imgMsg, errNotExist
|
||||
}
|
||||
// 获取高亮方块
|
||||
highlightSquare := make([]chess.Square, 0, 2)
|
||||
moves := room.chessGame.Moves()
|
||||
if len(moves) != 0 {
|
||||
lastMove := moves[len(moves)-1]
|
||||
highlightSquare = append(highlightSquare, lastMove.S1())
|
||||
highlightSquare = append(highlightSquare, lastMove.S2())
|
||||
}
|
||||
// 生成棋盘 svg 文件
|
||||
buf := bytes.NewBuffer([]byte{})
|
||||
fenStr := room.chessGame.FEN()
|
||||
gameTurn := room.chessGame.Position().Turn()
|
||||
pos := &chess.Position{}
|
||||
if err = pos.UnmarshalText(binary.StringToBytes(fenStr)); err != nil {
|
||||
return
|
||||
}
|
||||
yellow := color.RGBA{255, 255, 0, 1}
|
||||
mark := cimage.MarkSquares(yellow, highlightSquare...)
|
||||
board := pos.Board()
|
||||
fromBlack := cimage.Perspective(gameTurn)
|
||||
err = cimage.SVG(buf, board, fromBlack, mark)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
worker, err := resvg.NewDefaultWorker(context.Background())
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer worker.Close()
|
||||
|
||||
tree, err := worker.NewTreeFromData(buf.Bytes(), &resvg.Options{
|
||||
Dpi: 96,
|
||||
FontFamily: "Unifont",
|
||||
FontSize: 24.0,
|
||||
})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer tree.Close()
|
||||
|
||||
fontdb, err := worker.NewFontDBDefault()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer fontdb.Close()
|
||||
|
||||
err = fontdb.LoadFontData(fontdata)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = tree.ConvertText(fontdb)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
pixmap, err := worker.NewPixmap(720, 720)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer pixmap.Close()
|
||||
|
||||
err = tree.Render(resvg.TransformFromScale(2, 2), pixmap)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
out, err := pixmap.EncodePNG()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
imgMsg = message.ImageBytes(out)
|
||||
return imgMsg, nil
|
||||
}
|
||||
|
||||
// getELOString 获得玩家等级分的文本内容
|
||||
func getELOString(room chessRoom, whiteScore, blackScore float64) (string, error) {
|
||||
if room.whitePlayer == 0 || room.blackPlayer == 0 {
|
||||
return "", nil
|
||||
}
|
||||
var msgBuilder strings.Builder
|
||||
msgBuilder.WriteString("玩家等级分: \n")
|
||||
dbService := newDBService()
|
||||
if err := updateELORate(room.whitePlayer, room.blackPlayer, room.whiteName, room.blackName, whiteScore, blackScore, dbService); err != nil {
|
||||
return "", err
|
||||
}
|
||||
whiteRate, blackRate, err := getELORate(room.whitePlayer, room.blackPlayer, dbService)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
msgBuilder.WriteString(room.whiteName)
|
||||
msgBuilder.WriteString(": ")
|
||||
msgBuilder.WriteString(strconv.Itoa(whiteRate))
|
||||
msgBuilder.WriteString("\n")
|
||||
msgBuilder.WriteString(room.blackName)
|
||||
msgBuilder.WriteString(": ")
|
||||
msgBuilder.WriteString(strconv.Itoa(blackRate))
|
||||
msgBuilder.WriteString("\n\n")
|
||||
return msgBuilder.String(), nil
|
||||
}
|
||||
|
||||
// getRankingString 获取等级分排行榜的文本内容
|
||||
func getRanking() (message.Message, error) {
|
||||
dbService := newDBService()
|
||||
eloList, err := dbService.getHighestRateList()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var msgBuilder strings.Builder
|
||||
msgBuilder.WriteString("当前等级分排行榜: \n\n")
|
||||
for _, elo := range eloList {
|
||||
msgBuilder.WriteString(elo.Name)
|
||||
msgBuilder.WriteString(": ")
|
||||
msgBuilder.WriteString(strconv.Itoa(elo.Rate))
|
||||
msgBuilder.WriteString("\n")
|
||||
}
|
||||
return message.Message{message.Text(msgBuilder.String())}, nil
|
||||
}
|
||||
|
||||
// updateELORate 更新 elo 等级分
|
||||
// 当数据库中没有玩家的等级分信息时, 自动新建一条记录
|
||||
func updateELORate(whiteUin, blackUin int64, whiteName, blackName string, whiteScore, blackScore float64, dbService *chessDBService) error {
|
||||
whiteRate, err := dbService.getELORateByUin(whiteUin)
|
||||
if err != nil {
|
||||
if err != gorm.ErrRecordNotFound {
|
||||
return err
|
||||
}
|
||||
// create white elo
|
||||
if err := dbService.createELO(whiteUin, whiteName, eloDefault); err != nil {
|
||||
return err
|
||||
}
|
||||
whiteRate = eloDefault
|
||||
}
|
||||
blackRate, err := dbService.getELORateByUin(blackUin)
|
||||
if err != nil {
|
||||
if err != gorm.ErrRecordNotFound {
|
||||
return err
|
||||
}
|
||||
// create black elo
|
||||
if err := dbService.createELO(blackUin, blackName, eloDefault); err != nil {
|
||||
return err
|
||||
}
|
||||
blackRate = eloDefault
|
||||
}
|
||||
whiteRate, blackRate = calculateNewRate(whiteRate, blackRate, whiteScore, blackScore)
|
||||
// 更新白棋玩家的 ELO 等级分
|
||||
if err := dbService.updateELOByUin(whiteUin, whiteName, whiteRate); err != nil {
|
||||
return err
|
||||
}
|
||||
// 更新黑棋玩家的 ELO 等级分
|
||||
return dbService.updateELOByUin(blackUin, blackName, blackRate)
|
||||
}
|
||||
|
||||
// getChessString 获取 PGN 字符串
|
||||
func getChessString(room chessRoom) string {
|
||||
game := room.chessGame
|
||||
dataString := fmt.Sprintf("[Date \"%s\"]\n", time.Now().Format("2006-01-02"))
|
||||
whiteString := fmt.Sprintf("[White \"%s\"]\n", room.whiteName)
|
||||
blackString := fmt.Sprintf("[Black \"%s\"]\n", room.blackName)
|
||||
chessString := game.String()
|
||||
|
||||
return dataString + whiteString + blackString + chessString
|
||||
}
|
||||
|
||||
// getELORate 获取玩家的 ELO 等级分
|
||||
func getELORate(whiteUin, blackUin int64, dbService *chessDBService) (whiteRate int, blackRate int, err error) {
|
||||
whiteRate, err = dbService.getELORateByUin(whiteUin)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
blackRate, err = dbService.getELORateByUin(blackUin)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// isAprilFoolsDay 判断当前时间是否为愚人节期间
|
||||
func isAprilFoolsDay() bool {
|
||||
now := time.Now()
|
||||
return now.Month() == 4 && now.Day() == 1
|
||||
}
|
||||
100
plugin/chess/db.go
Normal file
100
plugin/chess/db.go
Normal file
@@ -0,0 +1,100 @@
|
||||
package chess
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/jinzhu/gorm"
|
||||
)
|
||||
|
||||
var chessDB *gorm.DB
|
||||
|
||||
// elo user elo info
|
||||
type elo struct {
|
||||
gorm.Model
|
||||
Uin int64 `gorm:"unique_index"`
|
||||
Name string
|
||||
Rate int
|
||||
}
|
||||
|
||||
// pgn chess pgn info
|
||||
type pgn struct {
|
||||
gorm.Model
|
||||
Data string
|
||||
WhiteUin int64
|
||||
BlackUin int64
|
||||
WhiteName string
|
||||
BlackName string
|
||||
}
|
||||
|
||||
// chessDBService 数据库服务
|
||||
type chessDBService struct {
|
||||
db *gorm.DB
|
||||
}
|
||||
|
||||
// newDBService 创建数据库服务
|
||||
func newDBService() *chessDBService {
|
||||
return &chessDBService{
|
||||
db: chessDB,
|
||||
}
|
||||
}
|
||||
|
||||
// initDatabase init database
|
||||
func initDatabase(dbPath string) {
|
||||
var err error
|
||||
if _, err = os.Stat(dbPath); err != nil || os.IsNotExist(err) {
|
||||
f, err := os.Create(dbPath)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer f.Close()
|
||||
}
|
||||
chessDB, err = gorm.Open("sqlite3", dbPath)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
chessDB.AutoMigrate(&elo{}, &pgn{})
|
||||
}
|
||||
|
||||
// createELO 创建 ELO
|
||||
func (s *chessDBService) createELO(uin int64, name string, rate int) error {
|
||||
return s.db.Create(&elo{
|
||||
Uin: uin,
|
||||
Name: name,
|
||||
Rate: rate,
|
||||
}).Error
|
||||
}
|
||||
|
||||
// getELORateByUin 获取 ELO 等级分
|
||||
func (s *chessDBService) getELORateByUin(uin int64) (int, error) {
|
||||
var elo elo
|
||||
err := s.db.Select("rate").Where("uin = ?", uin).First(&elo).Error
|
||||
return elo.Rate, err
|
||||
}
|
||||
|
||||
// getHighestRateList 获取最高的等级分列表
|
||||
func (s *chessDBService) getHighestRateList() ([]elo, error) {
|
||||
var eloList []elo
|
||||
err := s.db.Order("rate desc").Limit(10).Find(&eloList).Error
|
||||
return eloList, err
|
||||
}
|
||||
|
||||
// updateELOByUin 更新 ELO 等级分
|
||||
func (s *chessDBService) updateELOByUin(uin int64, name string, rate int) error {
|
||||
return s.db.Model(&elo{}).Where("uin = ?", uin).Update("name", name).Update("rate", rate).Error
|
||||
}
|
||||
|
||||
// cleanELOByUin 清空用户 ELO 等级分
|
||||
func (s *chessDBService) cleanELOByUin(uin int64) error {
|
||||
return s.db.Model(&elo{}).Where("uin = ?", uin).Update("rate", 100).Error
|
||||
}
|
||||
|
||||
// createPGN 创建 PGN
|
||||
func (s *chessDBService) createPGN(data string, whiteUin int64, blackUin int64, whiteName string, blackName string) error {
|
||||
return s.db.Create(&pgn{
|
||||
Data: data,
|
||||
WhiteUin: whiteUin,
|
||||
BlackUin: blackUin,
|
||||
WhiteName: whiteName,
|
||||
BlackName: blackName,
|
||||
}).Error
|
||||
}
|
||||
37
plugin/chess/elo.go
Normal file
37
plugin/chess/elo.go
Normal file
@@ -0,0 +1,37 @@
|
||||
package chess
|
||||
|
||||
import (
|
||||
"math"
|
||||
)
|
||||
|
||||
// calculateNewRate calculate new rate of the player
|
||||
func calculateNewRate(whiteRate, blackRate int, whiteScore, blackScore float64) (int, int) {
|
||||
k := getKFactor(whiteRate, blackRate)
|
||||
exceptionWhite := calculateException(whiteRate, blackRate)
|
||||
exceptionBlack := calculateException(blackRate, whiteRate)
|
||||
whiteRate = calculateRate(whiteRate, whiteScore, exceptionWhite, k)
|
||||
blackRate = calculateRate(blackRate, blackScore, exceptionBlack, k)
|
||||
return whiteRate, blackRate
|
||||
}
|
||||
|
||||
func calculateException(rate int, opponentRate int) float64 {
|
||||
return 1.0 / (1.0 + math.Pow(10.0, float64(opponentRate-rate)/400.0))
|
||||
}
|
||||
|
||||
func calculateRate(rate int, score float64, exception float64, k int) int {
|
||||
newRate := int(math.Round(float64(rate) + float64(k)*(score-exception)))
|
||||
if newRate < 1 {
|
||||
newRate = 1
|
||||
}
|
||||
return newRate
|
||||
}
|
||||
|
||||
func getKFactor(rateA, rateB int) int {
|
||||
if rateA > 2400 && rateB > 2400 {
|
||||
return 16
|
||||
}
|
||||
if rateA > 2100 && rateB > 2100 {
|
||||
return 24
|
||||
}
|
||||
return 32
|
||||
}
|
||||
80
plugin/chess/elo_test.go
Normal file
80
plugin/chess/elo_test.go
Normal file
@@ -0,0 +1,80 @@
|
||||
package chess
|
||||
|
||||
import (
|
||||
"math"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestCalculateNewRate(t *testing.T) {
|
||||
type args struct {
|
||||
whiteRate int
|
||||
blackRate int
|
||||
whiteScore float64
|
||||
blackScore float64
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want int
|
||||
want1 int
|
||||
}{
|
||||
{
|
||||
name: "test1",
|
||||
args: args{
|
||||
whiteRate: 1613,
|
||||
blackRate: 1573,
|
||||
whiteScore: 0.5,
|
||||
blackScore: 0.5,
|
||||
},
|
||||
want: 1611,
|
||||
want1: 1575,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, got1 := calculateNewRate(tt.args.whiteRate, tt.args.blackRate, tt.args.whiteScore, tt.args.blackScore)
|
||||
if got != tt.want {
|
||||
t.Errorf("CalculateNewRate() got = %v, want %v", got, tt.want)
|
||||
}
|
||||
if got1 != tt.want1 {
|
||||
t.Errorf("CalculateNewRate() got1 = %v, want %v", got1, tt.want1)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_calculateException(t *testing.T) {
|
||||
type args struct {
|
||||
rate int
|
||||
opponentRate int
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want float64
|
||||
}{
|
||||
{
|
||||
name: "test1",
|
||||
args: args{
|
||||
rate: 1613,
|
||||
opponentRate: 1573,
|
||||
},
|
||||
want: 0.5573116,
|
||||
},
|
||||
{
|
||||
name: "test2",
|
||||
args: args{
|
||||
rate: 1613,
|
||||
opponentRate: 1613,
|
||||
},
|
||||
want: 0.5,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := calculateException(tt.args.rate, tt.args.opponentRate); math.Abs(got-tt.want) > 0.0001 {
|
||||
t.Errorf("calculateException() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -6,19 +6,18 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
control "github.com/FloatTech/zbputils/control"
|
||||
ctrl "github.com/FloatTech/zbpctrl"
|
||||
"github.com/FloatTech/zbputils/control"
|
||||
zero "github.com/wdvxdr1123/ZeroBot"
|
||||
"github.com/wdvxdr1123/ZeroBot/message"
|
||||
|
||||
"github.com/FloatTech/zbputils/control/order"
|
||||
)
|
||||
|
||||
func init() {
|
||||
engine := control.Register("choose", order.AcquirePrio(), &control.Options{
|
||||
engine := control.AutoRegister(&ctrl.Options[*zero.Ctx]{
|
||||
DisableOnDefault: false,
|
||||
Help: "choose\n" +
|
||||
"- 选择可口可乐还是百事可乐\n" +
|
||||
"- 选择肯德基还是麦当劳还是必胜客",
|
||||
Brief: "选择困难症帮手",
|
||||
Help: "例: 选择可口可乐还是百事可乐\n" +
|
||||
"选择肯德基还是麦当劳还是必胜客",
|
||||
})
|
||||
engine.OnPrefix("选择").SetBlock(true).Handle(handle)
|
||||
}
|
||||
|
||||
@@ -2,39 +2,50 @@
|
||||
package chouxianghua
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
zero "github.com/wdvxdr1123/ZeroBot"
|
||||
"github.com/wdvxdr1123/ZeroBot/message"
|
||||
|
||||
fcext "github.com/FloatTech/floatbox/ctxext"
|
||||
sql "github.com/FloatTech/sqlite"
|
||||
ctrl "github.com/FloatTech/zbpctrl"
|
||||
"github.com/FloatTech/zbputils/control"
|
||||
"github.com/FloatTech/zbputils/control/order"
|
||||
"github.com/FloatTech/zbputils/file"
|
||||
)
|
||||
|
||||
func init() {
|
||||
en := control.Register("chouxianghua", order.AcquirePrio(), &control.Options{
|
||||
en := control.AutoRegister(&ctrl.Options[*zero.Ctx]{
|
||||
DisableOnDefault: false,
|
||||
Help: "抽象话\n- 抽象翻译xxx",
|
||||
Brief: "翻译为抽象话",
|
||||
Help: "- 抽象翻译xxx",
|
||||
PublicDataFolder: "ChouXiangHua",
|
||||
})
|
||||
|
||||
go func() {
|
||||
dbpath := en.DataFolder()
|
||||
db.DBPath = dbpath + "cxh.db"
|
||||
// os.RemoveAll(dbpath)
|
||||
_, _ = file.GetLazyData(db.DBPath, false, true)
|
||||
err := db.Create("pinyin", &pinyin{})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
n, err := db.Count("pinyin")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
logrus.Printf("[chouxianghua]读取%d条拼音", n)
|
||||
}()
|
||||
|
||||
en.OnRegex("^抽象翻译((\\s|[\\r\\n]|[\\p{Han}\\p{P}A-Za-z0-9])+)$").SetBlock(true).
|
||||
en.OnRegex("^抽象翻译((\\s|[\\r\\n]|[\\p{Han}\\p{P}A-Za-z0-9])+)$",
|
||||
fcext.DoOnceOnSuccess(func(ctx *zero.Ctx) bool {
|
||||
db = sql.New(en.DataFolder() + "cxh.db")
|
||||
// os.RemoveAll(dbpath)
|
||||
_, _ = en.GetLazyData("cxh.db", true)
|
||||
err := db.Open(time.Hour)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return false
|
||||
}
|
||||
err = db.Create("pinyin", &pinyin{})
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return false
|
||||
}
|
||||
n, err := db.Count("pinyin")
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return false
|
||||
}
|
||||
logrus.Printf("[chouxianghua]读取%d条拼音", n)
|
||||
return true
|
||||
}),
|
||||
).SetBlock(true).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
r := cx(ctx.State["regex_matched"].([]string)[1])
|
||||
ctx.SendChain(message.Text(r))
|
||||
|
||||
@@ -11,11 +11,11 @@ type emoji struct {
|
||||
Emoji string `db:"emoji"`
|
||||
}
|
||||
|
||||
var db = &sql.Sqlite{}
|
||||
var db sql.Sqlite
|
||||
|
||||
func getPinyinByWord(word string) string {
|
||||
var p pinyin
|
||||
_ = db.Find("pinyin", &p, "where word = '"+word+"'")
|
||||
_ = db.Find("pinyin", &p, "WHERE word = ?", word)
|
||||
return p.Pronun
|
||||
}
|
||||
|
||||
@@ -25,6 +25,6 @@ func getPronunByDWord(w0, w1 rune) string {
|
||||
|
||||
func getEmojiByPronun(pronun string) string {
|
||||
var e emoji
|
||||
_ = db.Find("emoji", &e, "where pronunciation = '"+pronun+"'")
|
||||
_ = db.Find("emoji", &e, "WHERE pronunciation = ?", pronun)
|
||||
return e.Emoji
|
||||
}
|
||||
|
||||
33
plugin/chrev/init.go
Normal file
33
plugin/chrev/init.go
Normal file
@@ -0,0 +1,33 @@
|
||||
// Package chrev 英文字符反转
|
||||
package chrev
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
ctrl "github.com/FloatTech/zbpctrl"
|
||||
"github.com/FloatTech/zbputils/control"
|
||||
zero "github.com/wdvxdr1123/ZeroBot"
|
||||
"github.com/wdvxdr1123/ZeroBot/message"
|
||||
)
|
||||
|
||||
func init() {
|
||||
// 初始化engine
|
||||
engine := control.AutoRegister(&ctrl.Options[*zero.Ctx]{
|
||||
DisableOnDefault: false,
|
||||
Brief: "英文字符翻转",
|
||||
Help: "例: 翻转 I love you",
|
||||
})
|
||||
// 处理字符翻转指令
|
||||
engine.OnRegex(`^翻转\s*([A-Za-z\s]*)$`).SetBlock(true).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
// 获取需要翻转的字符串
|
||||
str := ctx.State["regex_matched"].([]string)[1]
|
||||
// 将字符顺序翻转
|
||||
tmp := strings.Builder{}
|
||||
for i := len(str) - 1; i >= 0; i-- {
|
||||
tmp.WriteRune(charMap[str[i]])
|
||||
}
|
||||
// 发送翻转后的字符串
|
||||
ctx.SendChain(message.Text(tmp.String()))
|
||||
})
|
||||
}
|
||||
59
plugin/chrev/map.go
Normal file
59
plugin/chrev/map.go
Normal file
@@ -0,0 +1,59 @@
|
||||
package chrev
|
||||
|
||||
var charMap [256]rune
|
||||
|
||||
func init() {
|
||||
charMap[' '] = ' '
|
||||
charMap['a'] = 'ɐ'
|
||||
charMap['b'] = 'q'
|
||||
charMap['c'] = 'ɔ'
|
||||
charMap['d'] = 'p'
|
||||
charMap['e'] = 'ǝ'
|
||||
charMap['f'] = 'ɟ'
|
||||
charMap['g'] = 'ƃ'
|
||||
charMap['h'] = 'ɥ'
|
||||
charMap['i'] = 'ᴉ'
|
||||
charMap['j'] = 'ɾ'
|
||||
charMap['k'] = 'ʞ'
|
||||
charMap['l'] = 'l'
|
||||
charMap['m'] = 'ɯ'
|
||||
charMap['n'] = 'u'
|
||||
charMap['o'] = 'o'
|
||||
charMap['p'] = 'd'
|
||||
charMap['q'] = 'b'
|
||||
charMap['r'] = 'ɹ'
|
||||
charMap['s'] = 's'
|
||||
charMap['t'] = 'ʇ'
|
||||
charMap['u'] = 'n'
|
||||
charMap['v'] = 'ʌ'
|
||||
charMap['w'] = 'ʍ'
|
||||
charMap['x'] = 'x'
|
||||
charMap['y'] = 'ʎ'
|
||||
charMap['z'] = 'z'
|
||||
charMap['A'] = '∀'
|
||||
charMap['B'] = 'ᗺ'
|
||||
charMap['C'] = 'Ɔ'
|
||||
charMap['D'] = 'ᗡ'
|
||||
charMap['E'] = 'Ǝ'
|
||||
charMap['F'] = 'Ⅎ'
|
||||
charMap['G'] = '⅁'
|
||||
charMap['H'] = 'H'
|
||||
charMap['I'] = 'I'
|
||||
charMap['J'] = 'ſ'
|
||||
charMap['K'] = 'ʞ'
|
||||
charMap['L'] = '˥'
|
||||
charMap['M'] = 'W'
|
||||
charMap['N'] = 'N'
|
||||
charMap['O'] = 'O'
|
||||
charMap['P'] = 'Ԁ'
|
||||
charMap['Q'] = 'Ò'
|
||||
charMap['R'] = 'ᴚ'
|
||||
charMap['S'] = 'S'
|
||||
charMap['T'] = '⏊'
|
||||
charMap['U'] = '∩'
|
||||
charMap['V'] = 'Λ'
|
||||
charMap['W'] = 'M'
|
||||
charMap['X'] = 'X'
|
||||
charMap['Y'] = '⅄'
|
||||
charMap['Z'] = 'Z'
|
||||
}
|
||||
@@ -2,57 +2,70 @@
|
||||
package coser
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"errors"
|
||||
"math/rand"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/tidwall/gjson"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
zero "github.com/wdvxdr1123/ZeroBot"
|
||||
"github.com/wdvxdr1123/ZeroBot/message"
|
||||
"github.com/wdvxdr1123/ZeroBot/utils/helper"
|
||||
|
||||
control "github.com/FloatTech/zbputils/control"
|
||||
"github.com/FloatTech/AnimeAPI/setu"
|
||||
"github.com/FloatTech/floatbox/file"
|
||||
"github.com/FloatTech/floatbox/web"
|
||||
ctrl "github.com/FloatTech/zbpctrl"
|
||||
"github.com/FloatTech/zbputils/control"
|
||||
"github.com/FloatTech/zbputils/ctxext"
|
||||
"github.com/FloatTech/zbputils/web"
|
||||
|
||||
"github.com/FloatTech/zbputils/control/order"
|
||||
)
|
||||
|
||||
var (
|
||||
ua = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.93 Safari/537.36"
|
||||
coserURL = "http://ovooa.com/API/cosplay/api.php"
|
||||
datestr = regexp.MustCompile(`/\d{4}-\d{2}-\d{2}/`)
|
||||
coserURL = "https://picture.yinux.workers.dev"
|
||||
)
|
||||
|
||||
func init() {
|
||||
control.Register("coser", order.AcquirePrio(), &control.Options{
|
||||
DisableOnDefault: false,
|
||||
Help: "三次元小姐姐\n- coser",
|
||||
}).ApplySingle(ctxext.DefaultSingle).OnFullMatch("coser", zero.OnlyGroup).SetBlock(true).Limit(ctxext.LimitByGroup).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
ctx.SendChain(message.Text("少女祈祷中......"))
|
||||
data, err := web.RequestDataWith(web.NewDefaultClient(), coserURL, "GET", "", ua)
|
||||
if err != nil {
|
||||
log.Println("err为:", err)
|
||||
p, err := setu.NewPool(setu.DefaultPoolDir,
|
||||
func(s string) (string, error) {
|
||||
if s != "coser" {
|
||||
return "", errors.New("invalid call")
|
||||
}
|
||||
|
||||
text := gjson.Get(helper.BytesToString(data), "data.Title").String()
|
||||
m := message.Message{ctxext.FakeSenderForwardNode(ctx, message.Text(text))}
|
||||
ds := ""
|
||||
gjson.Get(helper.BytesToString(data), "data.data").ForEach(func(_, value gjson.Result) bool {
|
||||
if ds == "" {
|
||||
ds = datestr.FindString(value.String())
|
||||
} else if ds != datestr.FindString(value.String()) {
|
||||
return false
|
||||
typ := setu.DefaultPoolDir + "/" + "coser"
|
||||
if file.IsNotExist(typ) {
|
||||
err := os.MkdirAll(typ, 0755)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
m = append(m, ctxext.FakeSenderForwardNode(ctx, message.Image(value.String())))
|
||||
return true
|
||||
})
|
||||
|
||||
if id := ctx.SendGroupForwardMessage(
|
||||
ctx.Event.GroupID,
|
||||
m).Get("message_id").Int(); id == 0 {
|
||||
ctx.SendChain(message.Text("ERROR:可能被风控或下载图片用时过长,请耐心等待"))
|
||||
}
|
||||
data, err := web.RequestDataWith(web.NewDefaultClient(), coserURL, "GET", "", ua, nil)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
arr := gjson.Get(helper.BytesToString(data), "data.data").Array()
|
||||
if len(arr) == 0 {
|
||||
return "", errors.New("data is empty")
|
||||
}
|
||||
pic := arr[rand.Intn(len(arr))]
|
||||
return pic.String(), nil
|
||||
}, web.GetData, time.Minute)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
control.AutoRegister(&ctrl.Options[*zero.Ctx]{
|
||||
DisableOnDefault: false,
|
||||
Brief: "三次元coser",
|
||||
Help: "- coser",
|
||||
}).ApplySingle(ctxext.DefaultSingle).OnFullMatch("coser").SetBlock(true).Limit(ctxext.LimitByGroup).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
pic, err := p.Roll("coser")
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return
|
||||
}
|
||||
if id := ctx.Send(message.Message{ctxext.FakeSenderForwardNode(ctx, message.Image("file:///"+file.BOTPATH+"/"+pic))}).ID(); id == 0 {
|
||||
ctx.SendChain(message.Text("ERROR: 可能被风控或下载图片用时过长,请耐心等待"))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -3,52 +3,61 @@ package cpstory
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
zero "github.com/wdvxdr1123/ZeroBot"
|
||||
"github.com/wdvxdr1123/ZeroBot/message"
|
||||
|
||||
control "github.com/FloatTech/zbputils/control"
|
||||
"github.com/FloatTech/zbputils/file"
|
||||
"github.com/FloatTech/zbputils/math"
|
||||
|
||||
"github.com/FloatTech/zbputils/control/order"
|
||||
fcext "github.com/FloatTech/floatbox/ctxext"
|
||||
"github.com/FloatTech/floatbox/math"
|
||||
sql "github.com/FloatTech/sqlite"
|
||||
ctrl "github.com/FloatTech/zbpctrl"
|
||||
"github.com/FloatTech/zbputils/control"
|
||||
)
|
||||
|
||||
func init() {
|
||||
engine := control.Register("cpstory", order.AcquirePrio(), &control.Options{
|
||||
engine := control.AutoRegister(&ctrl.Options[*zero.Ctx]{
|
||||
DisableOnDefault: false,
|
||||
Help: "cp短打\n- 组cp[@xxx][@xxx]\n- 磕cp大老师 雪乃",
|
||||
Brief: "cp短打", // 这里也许有更好的名字
|
||||
Help: "- 组cp[@xxx][@xxx]\n- 磕cp大老师 雪乃",
|
||||
PublicDataFolder: "CpStory",
|
||||
})
|
||||
|
||||
go func() {
|
||||
dbpath := engine.DataFolder()
|
||||
db.DBPath = dbpath + "cp.db"
|
||||
getdb := fcext.DoOnceOnSuccess(func(ctx *zero.Ctx) bool {
|
||||
db = sql.New(engine.DataFolder() + "cp.db")
|
||||
// os.RemoveAll(dbpath)
|
||||
_, _ = file.GetLazyData(db.DBPath, false, true)
|
||||
err := db.Create("cp_story", &cpstory{})
|
||||
_, _ = engine.GetLazyData("cp.db", true)
|
||||
err := db.Open(time.Hour)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return false
|
||||
}
|
||||
err = db.Create("cp_story", &cpstory{})
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return false
|
||||
}
|
||||
n, err := db.Count("cp_story")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return false
|
||||
}
|
||||
logrus.Printf("[cpstory]读取%d条故事", n)
|
||||
}()
|
||||
return true
|
||||
})
|
||||
|
||||
engine.OnRegex("^组cp.*?(\\d+).*?(\\d+)", zero.OnlyGroup).SetBlock(true).Handle(func(ctx *zero.Ctx) {
|
||||
engine.OnRegex("^组cp.*?(\\d+).*?(\\d+)", zero.OnlyGroup, getdb).SetBlock(true).Handle(func(ctx *zero.Ctx) {
|
||||
cs := getRandomCpStory()
|
||||
gong := ctx.CardOrNickName(math.Str2Int64(ctx.State["regex_matched"].([]string)[1]))
|
||||
shou := ctx.CardOrNickName(math.Str2Int64(ctx.State["regex_matched"].([]string)[2]))
|
||||
text := strings.ReplaceAll(cs.Story, "<攻>", gong)
|
||||
text = strings.ReplaceAll(text, "<受>", shou)
|
||||
text = strings.ReplaceAll(text, cs.Gong, gong)
|
||||
text = strings.ReplaceAll(text, cs.Shou, gong)
|
||||
text = strings.ReplaceAll(text, cs.Shou, shou)
|
||||
ctx.SendChain(message.Text(text))
|
||||
})
|
||||
engine.OnPrefix("磕cp").SetBlock(true).Handle(func(ctx *zero.Ctx) {
|
||||
engine.OnPrefix("磕cp", getdb).SetBlock(true).Handle(func(ctx *zero.Ctx) {
|
||||
cs := getRandomCpStory()
|
||||
params := strings.Split(ctx.State["args"].(string), " ")
|
||||
if len(params) < 2 {
|
||||
|
||||
@@ -9,7 +9,7 @@ type cpstory struct {
|
||||
Story string `db:"story"`
|
||||
}
|
||||
|
||||
var db = &sql.Sqlite{}
|
||||
var db sql.Sqlite
|
||||
|
||||
func getRandomCpStory() (cs cpstory) {
|
||||
_ = db.Pick("cp_story", &cs)
|
||||
|
||||
95
plugin/crypter/fumo.go
Normal file
95
plugin/crypter/fumo.go
Normal file
@@ -0,0 +1,95 @@
|
||||
// Package crypter Fumo语
|
||||
package crypter
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Base64字符表
|
||||
const base64Chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
|
||||
|
||||
// Fumo语字符表 - 使用各种fumo变体来表示base64字符
|
||||
var fumoChars = []string{
|
||||
"fumo-", "Fumo-", "fUmo-", "fuMo-", "fumO-", "FUmo-", "FuMo-", "FumO-",
|
||||
"fUMo-", "fUmO-", "fuMO-", "FUMo-", "FUmO-", "fUMO-", "FUMO-", "fumo.",
|
||||
"Fumo.", "fUmo.", "fuMo.", "fumO.", "FUmo.", "FuMo.", "FumO.", "fUMo.",
|
||||
"fUmO.", "fuMO.", "FUMo.", "FUmO.", "fUMO.", "FUMO.", "fumo,", "Fumo,",
|
||||
"fUmo,", "fuMo,", "fumO,", "FUmo,", "FuMo,", "FumO,", "fUMo,", "fUmO,",
|
||||
"fuMO,", "FUMo,", "FuMO,", "fUMO,", "FUMO,", "fumo+", "Fumo+", "fUmo+",
|
||||
"fuMo+", "fumO+", "FUmo+", "FuMo+", "FumO+", "fUMo+", "fUmO+", "fuMO+",
|
||||
"FUMo+", "FUmO+", "fUMO+", "FUMO+", "fumo|", "Fumo|", "fUmo|", "fuMo|",
|
||||
"fumO|", "FUmo|", "FuMo|", "FumO|", "fUMo|", "fUmO|", "fuMO|", "fumo/",
|
||||
"Fumo/", "fUmo/",
|
||||
}
|
||||
|
||||
// Base64 2 Fumo
|
||||
// 创建编码映射表
|
||||
var encodeMap = make(map[byte]string)
|
||||
|
||||
// 创建解码映射表
|
||||
var decodeMap = make(map[string]byte)
|
||||
|
||||
func init() {
|
||||
for i := 0; i < 64 && i < len(fumoChars); i++ {
|
||||
base64Char := base64Chars[i]
|
||||
fumoChar := fumoChars[i]
|
||||
|
||||
encodeMap[base64Char] = fumoChar
|
||||
decodeMap[fumoChar] = base64Char
|
||||
}
|
||||
}
|
||||
|
||||
// 加密
|
||||
func encryptFumo(text string) string {
|
||||
if text == "" {
|
||||
return "请输入要加密的文本"
|
||||
}
|
||||
textBytes := []byte(text)
|
||||
base64String := base64.StdEncoding.EncodeToString(textBytes)
|
||||
base64Body := strings.TrimRight(base64String, "=")
|
||||
paddingCount := len(base64String) - len(base64Body)
|
||||
var fumoBody strings.Builder
|
||||
for _, char := range base64Body {
|
||||
if fumoChar, exists := encodeMap[byte(char)]; exists {
|
||||
fumoBody.WriteString(fumoChar)
|
||||
} else {
|
||||
return fmt.Sprintf("Fumo加密失败: 未知字符 %c", char)
|
||||
}
|
||||
}
|
||||
result := fumoBody.String() + strings.Repeat("=", paddingCount)
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// 解密
|
||||
func decryptFumo(fumoText string) string {
|
||||
if fumoText == "" {
|
||||
return "请输入要解密的Fumo语密文"
|
||||
}
|
||||
fumoBody := strings.TrimRight(fumoText, "=")
|
||||
paddingCount := len(fumoText) - len(fumoBody)
|
||||
fumoPattern := regexp.MustCompile(`(\w+[-.,+|/])`)
|
||||
fumoWords := fumoPattern.FindAllString(fumoBody, -1)
|
||||
reconstructed := strings.Join(fumoWords, "")
|
||||
if reconstructed != fumoBody {
|
||||
return "Fumo解密失败: 包含无效的Fumo字符或格式错误"
|
||||
}
|
||||
var base64Body strings.Builder
|
||||
for _, fumoWord := range fumoWords {
|
||||
if base64Char, exists := decodeMap[fumoWord]; exists {
|
||||
base64Body.WriteByte(base64Char)
|
||||
} else {
|
||||
return fmt.Sprintf("Fumo解密失败: 包含无效的Fumo字符 %s", fumoWord)
|
||||
}
|
||||
}
|
||||
base64String := base64Body.String() + strings.Repeat("=", paddingCount)
|
||||
decodedBytes, err := base64.StdEncoding.DecodeString(base64String)
|
||||
if err != nil {
|
||||
return fmt.Sprintf("Fumo解密失败: Base64解码错误 %v", err)
|
||||
}
|
||||
originalText := string(decodedBytes)
|
||||
return originalText
|
||||
}
|
||||
42
plugin/crypter/handlers.go
Normal file
42
plugin/crypter/handlers.go
Normal file
@@ -0,0 +1,42 @@
|
||||
// Package crypter 处理函数
|
||||
package crypter
|
||||
|
||||
import (
|
||||
"github.com/FloatTech/AnimeAPI/airecord"
|
||||
"github.com/sirupsen/logrus"
|
||||
zero "github.com/wdvxdr1123/ZeroBot"
|
||||
"github.com/wdvxdr1123/ZeroBot/message"
|
||||
)
|
||||
|
||||
// hou
|
||||
func houEncryptHandler(ctx *zero.Ctx) {
|
||||
text := ctx.State["regex_matched"].([]string)[1]
|
||||
result := encodeHou(text)
|
||||
logrus.Infoln("[crypter] 回复内容:", result)
|
||||
recCfg := airecord.GetConfig()
|
||||
record := ctx.GetAIRecord(recCfg.ModelID, recCfg.Customgid, result)
|
||||
if record != "" {
|
||||
ctx.SendChain(message.Record(record))
|
||||
} else {
|
||||
ctx.SendChain(message.Text(result))
|
||||
}
|
||||
}
|
||||
|
||||
func houDecryptHandler(ctx *zero.Ctx) {
|
||||
text := ctx.State["regex_matched"].([]string)[1]
|
||||
result := decodeHou(text)
|
||||
ctx.SendChain(message.Text(result))
|
||||
}
|
||||
|
||||
// fumo
|
||||
func fumoEncryptHandler(ctx *zero.Ctx) {
|
||||
text := ctx.State["regex_matched"].([]string)[1]
|
||||
result := encryptFumo(text)
|
||||
ctx.SendChain(message.Text(result))
|
||||
}
|
||||
|
||||
func fumoDecryptHandler(ctx *zero.Ctx) {
|
||||
text := ctx.State["regex_matched"].([]string)[1]
|
||||
result := decryptFumo(text)
|
||||
ctx.SendChain(message.Text(result))
|
||||
}
|
||||
88
plugin/crypter/hou.go
Normal file
88
plugin/crypter/hou.go
Normal file
@@ -0,0 +1,88 @@
|
||||
// Package crypter 齁语加解密
|
||||
package crypter
|
||||
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// 齁语密码表
|
||||
var houCodebook = []string{
|
||||
"齁", "哦", "噢", "喔", "咕", "咿", "嗯", "啊",
|
||||
"~", "哈", "!", "唔", "哼", "❤", "呃", "呼",
|
||||
}
|
||||
|
||||
// 索引: 0 1 2 3 4 5 6 7
|
||||
// 8 9 10 11 12 13 14 15
|
||||
|
||||
// 创建映射表
|
||||
var houCodebookMap = make(map[string]int)
|
||||
|
||||
// 初始化映射表
|
||||
func init() {
|
||||
for idx, ch := range houCodebook {
|
||||
houCodebookMap[ch] = idx
|
||||
}
|
||||
}
|
||||
|
||||
func encodeHou(text string) string {
|
||||
if text == "" {
|
||||
return "请输入要加密的文本"
|
||||
}
|
||||
var encoded strings.Builder
|
||||
textBytes := []byte(text)
|
||||
for _, b := range textBytes {
|
||||
high := (b >> 4) & 0x0F
|
||||
low := b & 0x0F
|
||||
encoded.WriteString(houCodebook[high])
|
||||
encoded.WriteString(houCodebook[low])
|
||||
}
|
||||
|
||||
return encoded.String()
|
||||
}
|
||||
|
||||
func decodeHou(code string) string {
|
||||
if code == "" {
|
||||
return "请输入要解密的齁语密文"
|
||||
}
|
||||
|
||||
// 过滤出有效的齁语字符
|
||||
var validChars []string
|
||||
for _, r := range code {
|
||||
charStr := string(r)
|
||||
if _, exists := houCodebookMap[charStr]; exists {
|
||||
validChars = append(validChars, charStr)
|
||||
}
|
||||
}
|
||||
|
||||
if len(validChars)%2 != 0 {
|
||||
return "齁语密文长度错误,无法解密"
|
||||
}
|
||||
|
||||
// 解密过程
|
||||
var byteList []byte
|
||||
for i := 0; i < len(validChars); i += 2 {
|
||||
highIdx, highExists := houCodebookMap[validChars[i]]
|
||||
lowIdx, lowExists := houCodebookMap[validChars[i+1]]
|
||||
|
||||
if !highExists || !lowExists {
|
||||
return "齁语密文包含无效字符"
|
||||
}
|
||||
|
||||
originalByte := byte((highIdx << 4) | lowIdx)
|
||||
byteList = append(byteList, originalByte)
|
||||
}
|
||||
|
||||
result := string(byteList)
|
||||
|
||||
if !isValidUTF8(result) {
|
||||
return "齁语解密失败,结果不是有效的文本"
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// 检查字符串是否为有效的UTF-8编码
|
||||
func isValidUTF8(s string) bool {
|
||||
// Go的string类型默认就是UTF-8,如果转换没有出错说明是有效的
|
||||
return len(s) > 0 || s == ""
|
||||
}
|
||||
31
plugin/crypter/main.go
Normal file
31
plugin/crypter/main.go
Normal file
@@ -0,0 +1,31 @@
|
||||
// Package crypter 奇怪语言加解密
|
||||
package crypter
|
||||
|
||||
import (
|
||||
ctrl "github.com/FloatTech/zbpctrl"
|
||||
"github.com/FloatTech/zbputils/control"
|
||||
zero "github.com/wdvxdr1123/ZeroBot"
|
||||
)
|
||||
|
||||
func init() {
|
||||
engine := control.AutoRegister(&ctrl.Options[*zero.Ctx]{
|
||||
DisableOnDefault: false,
|
||||
Brief: "奇怪语言加解密",
|
||||
Help: "多种语言加解密插件\n" +
|
||||
"- 齁语加解密:\n" +
|
||||
"- 齁语加密 [文本] 或 h加密 [文本]\n" +
|
||||
"- 齁语解密 [密文] 或 h解密 [密文]\n\n" +
|
||||
"- Fumo语加解密:\n" +
|
||||
"- fumo加密 [文本]\n" +
|
||||
"- fumo解密 [密文]\n\n",
|
||||
PublicDataFolder: "Crypter",
|
||||
})
|
||||
|
||||
// hou
|
||||
engine.OnRegex(`^(?:齁语加密|h加密)\s*(.+)$`).SetBlock(true).Handle(houEncryptHandler)
|
||||
engine.OnRegex(`^(?:齁语解密|h解密)\s*(.+)$`).SetBlock(true).Handle(houDecryptHandler)
|
||||
|
||||
// Fumo
|
||||
engine.OnRegex(`^fumo加密\s*(.+)$`).SetBlock(true).Handle(fumoEncryptHandler)
|
||||
engine.OnRegex(`^fumo解密\s*(.+)$`).SetBlock(true).Handle(fumoDecryptHandler)
|
||||
}
|
||||
@@ -2,16 +2,18 @@
|
||||
package curse
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
zero "github.com/wdvxdr1123/ZeroBot"
|
||||
"github.com/wdvxdr1123/ZeroBot/message"
|
||||
|
||||
control "github.com/FloatTech/zbputils/control"
|
||||
fcext "github.com/FloatTech/floatbox/ctxext"
|
||||
"github.com/FloatTech/floatbox/process"
|
||||
sql "github.com/FloatTech/sqlite"
|
||||
ctrl "github.com/FloatTech/zbpctrl"
|
||||
"github.com/FloatTech/zbputils/control"
|
||||
"github.com/FloatTech/zbputils/ctxext"
|
||||
"github.com/FloatTech/zbputils/file"
|
||||
"github.com/FloatTech/zbputils/process"
|
||||
|
||||
"github.com/FloatTech/zbputils/control/order"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -20,40 +22,52 @@ const (
|
||||
)
|
||||
|
||||
func init() {
|
||||
engine := control.Register("curse", order.AcquirePrio(), &control.Options{
|
||||
engine := control.AutoRegister(&ctrl.Options[*zero.Ctx]{
|
||||
DisableOnDefault: true,
|
||||
Help: "骂人(求骂,自卫)\n- 骂我\n- 大力骂我",
|
||||
Brief: "骂人反击",
|
||||
Help: "- 骂我\n- 大力骂我",
|
||||
PublicDataFolder: "Curse",
|
||||
})
|
||||
|
||||
go func() {
|
||||
dbpath := engine.DataFolder()
|
||||
db.DBPath = dbpath + "curse.db"
|
||||
_, err := file.GetLazyData(db.DBPath, false, true)
|
||||
getdb := fcext.DoOnceOnSuccess(func(ctx *zero.Ctx) bool {
|
||||
db = sql.New(engine.DataFolder() + "curse.db")
|
||||
_, err := engine.GetLazyData("curse.db", true)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return false
|
||||
}
|
||||
err = db.Open(time.Hour)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return false
|
||||
}
|
||||
err = db.Create("curse", &curse{})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return false
|
||||
}
|
||||
c, err := db.Count("curse")
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return false
|
||||
}
|
||||
c, _ := db.Count("curse")
|
||||
logrus.Infoln("[curse]加载", c, "条骂人语录")
|
||||
}()
|
||||
return true
|
||||
})
|
||||
|
||||
engine.OnFullMatch("骂我").SetBlock(true).Limit(ctxext.LimitByUser).Handle(func(ctx *zero.Ctx) {
|
||||
engine.OnFullMatch("骂我", getdb).SetBlock(true).Limit(ctxext.LimitByUser).Handle(func(ctx *zero.Ctx) {
|
||||
process.SleepAbout1sTo2s()
|
||||
text := getRandomCurseByLevel(minLevel).Text
|
||||
ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text(text))
|
||||
})
|
||||
|
||||
engine.OnFullMatch("大力骂我").SetBlock(true).Limit(ctxext.LimitByUser).Handle(func(ctx *zero.Ctx) {
|
||||
engine.OnFullMatch("大力骂我", getdb).SetBlock(true).Limit(ctxext.LimitByUser).Handle(func(ctx *zero.Ctx) {
|
||||
process.SleepAbout1sTo2s()
|
||||
text := getRandomCurseByLevel(maxLevel).Text
|
||||
ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text(text))
|
||||
})
|
||||
|
||||
engine.OnKeywordGroup([]string{"他妈", "公交车", "你妈", "操", "屎", "去死", "快死", "我日", "逼", "尼玛", "艾滋", "癌症", "有病", "烦你", "你爹", "屮", "cnm"}, zero.OnlyToMe).SetBlock(true).
|
||||
engine.OnKeywordGroup([]string{"他妈", "公交车", "你妈", "操", "屎", "去死", "快死", "我日", "逼", "尼玛", "艾滋", "癌症", "有病", "烦你", "你爹", "屮", "cnm"}, zero.OnlyToMe, getdb).SetBlock(true).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
text := getRandomCurseByLevel(maxLevel).Text
|
||||
ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text(text))
|
||||
|
||||
@@ -8,9 +8,9 @@ type curse struct {
|
||||
Level string `db:"level"`
|
||||
}
|
||||
|
||||
var db = &sql.Sqlite{}
|
||||
var db sql.Sqlite
|
||||
|
||||
func getRandomCurseByLevel(level string) (c curse) {
|
||||
_ = db.Find("curse", &c, "where level = '"+level+"' ORDER BY RANDOM() limit 1")
|
||||
_ = db.Find("curse", &c, "WHERE level = ? ORDER BY RANDOM() limit 1", level)
|
||||
return
|
||||
}
|
||||
|
||||
31
plugin/dailynews/dailynews.go
Normal file
31
plugin/dailynews/dailynews.go
Normal file
@@ -0,0 +1,31 @@
|
||||
// Package dailynews 今日早报
|
||||
package dailynews
|
||||
|
||||
import (
|
||||
"github.com/FloatTech/floatbox/web"
|
||||
ctrl "github.com/FloatTech/zbpctrl"
|
||||
"github.com/FloatTech/zbputils/control"
|
||||
zero "github.com/wdvxdr1123/ZeroBot"
|
||||
"github.com/wdvxdr1123/ZeroBot/message"
|
||||
)
|
||||
|
||||
const api = "https://uapis.cn/api/v1/daily/news-image"
|
||||
|
||||
func init() {
|
||||
engine := control.AutoRegister(&ctrl.Options[*zero.Ctx]{
|
||||
DisableOnDefault: false,
|
||||
Brief: "今日早报",
|
||||
Help: "- 今日早报",
|
||||
PrivateDataFolder: "dailynews",
|
||||
})
|
||||
|
||||
engine.OnFullMatch(`今日早报`).SetBlock(true).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
data, err := web.GetData(api)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return
|
||||
}
|
||||
ctx.SendChain(message.ImageBytes(data))
|
||||
})
|
||||
}
|
||||
@@ -4,24 +4,23 @@ package deepdanbooru
|
||||
import (
|
||||
"crypto/md5"
|
||||
"encoding/hex"
|
||||
"strings"
|
||||
|
||||
"github.com/FloatTech/AnimeAPI/danbooru"
|
||||
"github.com/FloatTech/AnimeAPI/saucenao"
|
||||
"github.com/FloatTech/floatbox/file"
|
||||
"github.com/FloatTech/imgfactory"
|
||||
ctrl "github.com/FloatTech/zbpctrl"
|
||||
"github.com/FloatTech/zbputils/control"
|
||||
"github.com/FloatTech/zbputils/file"
|
||||
"github.com/FloatTech/zbputils/img/writer"
|
||||
"github.com/FloatTech/zbputils/ctxext"
|
||||
zero "github.com/wdvxdr1123/ZeroBot"
|
||||
"github.com/wdvxdr1123/ZeroBot/message"
|
||||
"github.com/wdvxdr1123/ZeroBot/utils/helper"
|
||||
|
||||
"github.com/FloatTech/zbputils/control/order"
|
||||
)
|
||||
|
||||
func init() { // 插件主体
|
||||
engine := control.Register("danbooru", order.AcquirePrio(), &control.Options{
|
||||
DisableOnDefault: false,
|
||||
Help: "二次元图片标签识别\n" +
|
||||
"- 鉴赏图片[图片]",
|
||||
engine := control.AutoRegister(&ctrl.Options[*zero.Ctx]{
|
||||
DisableOnDefault: false,
|
||||
Brief: "二次元图片标签识别",
|
||||
Help: "- 鉴赏图片[图片]",
|
||||
PrivateDataFolder: "danbooru",
|
||||
})
|
||||
|
||||
@@ -32,24 +31,21 @@ func init() { // 插件主体
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
ctx.SendChain(message.Text("少女祈祷中..."))
|
||||
for _, url := range ctx.State["image_url"].([]string) {
|
||||
name := ""
|
||||
r, err := saucenao.SauceNAO(url)
|
||||
t, st, err := tagurl("", url)
|
||||
if err != nil {
|
||||
name = "未知图片"
|
||||
} else {
|
||||
name = r[0].Title
|
||||
}
|
||||
t, err := danbooru.TagURL(name, url)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR:", err))
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return
|
||||
}
|
||||
digest := md5.Sum(helper.StringToBytes(url))
|
||||
f := cachefolder + hex.EncodeToString(digest[:])
|
||||
if file.IsNotExist(f) {
|
||||
_ = writer.SavePNG2Path(f, t)
|
||||
_ = imgfactory.SavePNG2Path(f, t)
|
||||
}
|
||||
m := message.Message{ctxext.FakeSenderForwardNode(ctx, message.Image("file:///"+file.BOTPATH+"/"+f))}
|
||||
m = append(m, ctxext.FakeSenderForwardNode(ctx, message.Text("tags: ", strings.Join(st.tseq, ","))))
|
||||
if id := ctx.Send(m).ID(); id == 0 {
|
||||
ctx.SendChain(message.Text("ERROR: 可能被风控或下载图片用时过长,请耐心等待"))
|
||||
}
|
||||
ctx.SendChain(message.Image("file:///" + file.BOTPATH + "/" + f))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
124
plugin/danbooru/tag.go
Normal file
124
plugin/danbooru/tag.go
Normal file
@@ -0,0 +1,124 @@
|
||||
package deepdanbooru
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"image"
|
||||
"net/url"
|
||||
"sort"
|
||||
|
||||
"github.com/FloatTech/floatbox/file"
|
||||
"github.com/FloatTech/floatbox/web"
|
||||
"github.com/FloatTech/gg"
|
||||
"github.com/FloatTech/imgfactory"
|
||||
"github.com/FloatTech/zbputils/control"
|
||||
"github.com/FloatTech/zbputils/img/text" // jpg png gif
|
||||
_ "golang.org/x/image/webp" // webp
|
||||
)
|
||||
|
||||
const api = "https://nsfwtag.azurewebsites.net/api/tag?limit=0.5&url="
|
||||
|
||||
type sorttags struct {
|
||||
tags map[string]float64
|
||||
tseq []string
|
||||
}
|
||||
|
||||
func newsorttags(tags map[string]float64) (s *sorttags) {
|
||||
t := make([]string, 0, len(tags))
|
||||
for k := range tags {
|
||||
t = append(t, k)
|
||||
}
|
||||
return &sorttags{tags: tags, tseq: t}
|
||||
}
|
||||
|
||||
func (s *sorttags) Len() int {
|
||||
return len(s.tags)
|
||||
}
|
||||
|
||||
func (s *sorttags) Less(i, j int) bool {
|
||||
v1 := s.tseq[i]
|
||||
v2 := s.tseq[j]
|
||||
return s.tags[v1] >= s.tags[v2]
|
||||
}
|
||||
|
||||
// Swap swaps the elements with indexes i and j.
|
||||
func (s *sorttags) Swap(i, j int) {
|
||||
s.tseq[j], s.tseq[i] = s.tseq[i], s.tseq[j]
|
||||
}
|
||||
|
||||
func tagurl(name, u string) (im image.Image, st *sorttags, err error) {
|
||||
ch := make(chan []byte, 1)
|
||||
go func() {
|
||||
var data []byte
|
||||
data, err = web.GetData(u)
|
||||
ch <- data
|
||||
}()
|
||||
|
||||
data, err := web.GetData(api + url.QueryEscape(u))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if len(data) < 4 {
|
||||
err = errors.New("data too short")
|
||||
return
|
||||
}
|
||||
tags := make(map[string]float64)
|
||||
err = json.Unmarshal(data[1:len(data)-1], &tags)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
longestlen := 0
|
||||
for k := range tags {
|
||||
if len(k) > longestlen {
|
||||
longestlen = len(k)
|
||||
}
|
||||
}
|
||||
longestlen++
|
||||
|
||||
st = newsorttags(tags)
|
||||
sort.Sort(st)
|
||||
|
||||
boldfd, err := file.GetLazyData(text.BoldFontFile, control.Md5File, true)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
consfd, err := file.GetLazyData(text.ConsolasFontFile, control.Md5File, true)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
data = <-ch
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
img, _, err := image.Decode(bytes.NewReader(data))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
img = imgfactory.Limit(img, 1280, 720)
|
||||
|
||||
canvas := gg.NewContext(img.Bounds().Size().X, img.Bounds().Size().Y+int(float64(img.Bounds().Size().X)*0.2)+len(tags)*img.Bounds().Size().X/25)
|
||||
canvas.SetRGB(1, 1, 1)
|
||||
canvas.Clear()
|
||||
canvas.DrawImage(img, 0, 0)
|
||||
if err = canvas.ParseFontFace(boldfd, float64(img.Bounds().Size().X)*0.1); err != nil {
|
||||
return
|
||||
}
|
||||
canvas.SetRGB(0, 0, 0)
|
||||
canvas.DrawString(name, float64(img.Bounds().Size().X)*0.02, float64(img.Bounds().Size().Y)+float64(img.Bounds().Size().X)*0.1)
|
||||
i := float64(img.Bounds().Size().Y) + float64(img.Bounds().Size().X)*0.2
|
||||
if err = canvas.ParseFontFace(consfd, float64(img.Bounds().Size().X)*0.04); err != nil {
|
||||
return
|
||||
}
|
||||
rate := float64(img.Bounds().Size().X) * 0.04
|
||||
for _, k := range st.tseq {
|
||||
canvas.DrawString(fmt.Sprintf("* %-*s -%.3f-", longestlen, k, tags[k]), float64(img.Bounds().Size().X)*0.04, i)
|
||||
i += rate
|
||||
}
|
||||
im = canvas.Image()
|
||||
return
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user