Compare commits
922 Commits
2.1.2
...
3.0.0-beta
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f33956f585 | ||
|
|
5f0de0d513 | ||
|
|
9c97d4a41c | ||
|
|
cd4b7df1d7 | ||
|
|
926b77a429 | ||
|
|
7c30ba6b03 | ||
|
|
cd85bd087c | ||
|
|
53ae4b1ed7 | ||
|
|
e526a94520 | ||
|
|
d708485918 | ||
|
|
c73a3e85aa | ||
|
|
61ddf9515c | ||
|
|
5b98480950 | ||
|
|
25fa833495 | ||
|
|
4b2af374f5 | ||
|
|
6238224e8c | ||
|
|
fe890025c5 | ||
|
|
78612b6e58 | ||
|
|
4957731760 | ||
|
|
fba39219cf | ||
|
|
b5329aef7c | ||
|
|
4d6fc097c2 | ||
|
|
863c2d8c7a | ||
|
|
3596d6077d | ||
|
|
ed40799fe3 | ||
|
|
32afd38ad4 | ||
|
|
e0049fc7da | ||
|
|
f8c4bc01bc | ||
|
|
0c13cded89 | ||
|
|
690f8aed10 | ||
|
|
5d473eddcf | ||
|
|
2d6896adde | ||
|
|
8f8002608e | ||
|
|
9c3e6ce8bd | ||
|
|
3a04c98f62 | ||
|
|
e8c8bd0ba7 | ||
|
|
939b02fdbc | ||
|
|
cd52bfbfa5 | ||
|
|
c78b7fa2b0 | ||
|
|
e5b3509ebf | ||
|
|
d45d34430d | ||
|
|
26e172680d | ||
|
|
429eae64c6 | ||
|
|
19aab968ae | ||
|
|
7d9a8a5ee1 | ||
|
|
1abdfc761d | ||
|
|
43634974fc | ||
|
|
5893d18149 | ||
|
|
a145814923 | ||
|
|
366b49ca82 | ||
|
|
dbbc6f75d8 | ||
|
|
316c29628a | ||
|
|
11cd4ef56e | ||
|
|
fe8b38f968 | ||
|
|
8477bf153f | ||
|
|
2f94f5258d | ||
|
|
eb05cea443 | ||
|
|
af26086019 | ||
|
|
a0950a4be3 | ||
|
|
9f970857ae | ||
|
|
1a1fc8c558 | ||
|
|
fd7b9928ba | ||
|
|
da1bbee24c | ||
|
|
3086bcdf6d | ||
|
|
50d6e75e71 | ||
|
|
413d783d53 | ||
|
|
85240c753b | ||
|
|
c2c5433220 | ||
|
|
5cd28964e6 | ||
|
|
d87b7dc0f7 | ||
|
|
1d27091558 | ||
|
|
3dafbf4fbd | ||
|
|
3a78f5e2b1 | ||
|
|
3f26648a3c | ||
|
|
a62e950870 | ||
|
|
aee7fa332a | ||
|
|
c1a0fae6e6 | ||
|
|
ed31edcc5c | ||
|
|
4aea90cb39 | ||
|
|
a58c3aadab | ||
|
|
f2fb40b67c | ||
|
|
18df76f650 | ||
|
|
dce126136b | ||
|
|
d32f7b0ff8 | ||
|
|
d55362e190 | ||
|
|
6bcedea720 | ||
|
|
ccc801e6cb | ||
|
|
a2404482a3 | ||
|
|
4c41dd09d2 | ||
|
|
ab83194bbe | ||
|
|
6013571267 | ||
|
|
2636bc2e35 | ||
|
|
a2b013402b | ||
|
|
206f041d29 | ||
|
|
ce885a7a61 | ||
|
|
b0d0d5eba9 | ||
|
|
d45c4e24fd | ||
|
|
49fffcc464 | ||
|
|
e89d5ad1ac | ||
|
|
11aed8c6be | ||
|
|
46d2f895e2 | ||
|
|
ed6b65eb88 | ||
|
|
8efb63a334 | ||
|
|
13b5c44627 | ||
|
|
2a3c953c36 | ||
|
|
fd8b3721ae | ||
|
|
3734f5d476 | ||
|
|
088f963ad1 | ||
|
|
c8a6bc69d3 | ||
|
|
8584cc647c | ||
|
|
d8ac604592 | ||
|
|
9acb5e760b | ||
|
|
96879bf415 | ||
|
|
d7e815d670 | ||
|
|
dfcb8a4550 | ||
|
|
96fa6b105c | ||
|
|
b86f51ab46 | ||
|
|
8473a1152d | ||
|
|
65acfaa0bd | ||
|
|
c58e08998a | ||
|
|
9cf905421e | ||
|
|
c65694402f | ||
|
|
0d24ae6192 | ||
|
|
383e0e22af | ||
|
|
87840930e0 | ||
|
|
05b3321af7 | ||
|
|
19e380d1fb | ||
|
|
d1f69556b7 | ||
|
|
80ba34351c | ||
|
|
81a5e48b26 | ||
|
|
aa28675016 | ||
|
|
f8ecfa2745 | ||
|
|
efda3f4408 | ||
|
|
a5b29ac398 | ||
|
|
73151db726 | ||
|
|
aab57ce216 | ||
|
|
f140784a50 | ||
|
|
579269cb5b | ||
|
|
ed5a9c6c12 | ||
|
|
ca430123c9 | ||
|
|
bc68aff654 | ||
|
|
19e3280e29 | ||
|
|
a813e80b32 | ||
|
|
05b3446f59 | ||
|
|
60ce722010 | ||
|
|
1fc329c670 | ||
|
|
4035ffcffb | ||
|
|
b41871a2a2 | ||
|
|
a44a7e86d7 | ||
|
|
716734d26f | ||
|
|
b3e97febe1 | ||
|
|
520db4b550 | ||
|
|
c6c3efade4 | ||
|
|
0b7f5f9f05 | ||
|
|
33a14dee8b | ||
|
|
bddc54b93d | ||
|
|
2e0f5e2c51 | ||
|
|
2c6f6067ca | ||
|
|
dcf41091ef | ||
|
|
8040a329cb | ||
|
|
6e43d4092f | ||
|
|
0ac2985fc1 | ||
|
|
1e037b9610 | ||
|
|
38b70db2dc | ||
|
|
b35d546555 | ||
|
|
5e3d07abe6 | ||
|
|
1d131871f1 | ||
|
|
5e8f80b626 | ||
|
|
9128efbaa1 | ||
|
|
3545e1173e | ||
|
|
9bf95223a1 | ||
|
|
3f2bd69002 | ||
|
|
4554bbc85b | ||
|
|
8901c31631 | ||
|
|
61ece52edc | ||
|
|
fb936e97ac | ||
|
|
8ca4f7400a | ||
|
|
61b48676ae | ||
|
|
257e59b79f | ||
|
|
c19791c31d | ||
|
|
c99747c1f6 | ||
|
|
a9b6989966 | ||
|
|
c9bf0fb13c | ||
|
|
04247048d4 | ||
|
|
b2a6c2f5e0 | ||
|
|
865c306e14 | ||
|
|
588fc919d9 | ||
|
|
8ff7da4d23 | ||
|
|
da516b487d | ||
|
|
06615cbb29 | ||
|
|
463ee0cced | ||
|
|
208ccd6642 | ||
|
|
cd2bb1b570 | ||
|
|
52a195aca2 | ||
|
|
fbc3cf00a2 | ||
|
|
ede5f456a8 | ||
|
|
2633e91d4d | ||
|
|
39b3a74d34 | ||
|
|
3ecd224ce6 | ||
|
|
49c7033eaa | ||
|
|
20ca848df9 | ||
|
|
5b0e3ed636 | ||
|
|
14840715bc | ||
|
|
ed4537e5d5 | ||
|
|
58d4b4427d | ||
|
|
8c6aba9361 | ||
|
|
1173949bba | ||
|
|
279956456c | ||
|
|
3f8c557a96 | ||
|
|
ff93201ab1 | ||
|
|
cd94539e99 | ||
|
|
adc1d176e3 | ||
|
|
44c25f7fdd | ||
|
|
3806983bc2 | ||
|
|
c6f877700e | ||
|
|
c2fb90dfc7 | ||
|
|
ca61e6a238 | ||
|
|
9399e54da8 | ||
|
|
385f217b96 | ||
|
|
65bb6a2473 | ||
|
|
a430cf59c4 | ||
|
|
4bf328ef67 | ||
|
|
ebb724415d | ||
|
|
daa07dcb2b | ||
|
|
635f081437 | ||
|
|
c7b4ca2a82 | ||
|
|
b5c91531fa | ||
|
|
8199e91ce0 | ||
|
|
44c32c1711 | ||
|
|
5a3da43640 | ||
|
|
352f3e920e | ||
|
|
5f4a00c77e | ||
|
|
aeea6f5dc1 | ||
|
|
ea74a531b4 | ||
|
|
02003b2bd3 | ||
|
|
723dfd30a2 | ||
|
|
aa29dc99f3 | ||
|
|
74ba4d0145 | ||
|
|
5705f5227f | ||
|
|
3bb34b46ee | ||
|
|
4d0ea99f47 | ||
|
|
fe70c07113 | ||
|
|
27c6bf1864 | ||
|
|
f4ded4c6d7 | ||
|
|
4a2c7f5ecb | ||
|
|
7a051380e2 | ||
|
|
35dec947df | ||
|
|
6b34427855 | ||
|
|
59a9f30089 | ||
|
|
779564ebb6 | ||
|
|
a4f992b9e5 | ||
|
|
cf3f09600b | ||
|
|
83c1d63408 | ||
|
|
43337c34c8 | ||
|
|
bb431ccf53 | ||
|
|
ba69cc93ee | ||
|
|
406de8f4df | ||
|
|
fd05fe6e25 | ||
|
|
10a0ee9142 | ||
|
|
836d4dc1ef | ||
|
|
a18fcdb21e | ||
|
|
6c9cd06e4a | ||
|
|
44c1ac054c | ||
|
|
9d77d60887 | ||
|
|
8f7fd97ebe | ||
|
|
87e3b2249c | ||
|
|
5e12af77a4 | ||
|
|
47244373d7 | ||
|
|
a72530598a | ||
|
|
2679facf24 | ||
|
|
cf9db4b0d4 | ||
|
|
f341c8585e | ||
|
|
28f7b47034 | ||
|
|
99954f9140 | ||
|
|
85b21d63ed | ||
|
|
ef443d9912 | ||
|
|
db2b22c822 | ||
|
|
24156884e4 | ||
|
|
6413567353 | ||
|
|
84f84dad40 | ||
|
|
dd01653da9 | ||
|
|
085472a12c | ||
|
|
e9b6965678 | ||
|
|
1a9680c09a | ||
|
|
40f8fe22ee | ||
|
|
be6002dd64 | ||
|
|
96ebb770b6 | ||
|
|
9bcdb254c9 | ||
|
|
87e71f1eb8 | ||
|
|
451ce104c5 | ||
|
|
7920711988 | ||
|
|
6e78f2caf2 | ||
|
|
2727b056eb | ||
|
|
975d0e3540 | ||
|
|
eb7e700e7c | ||
|
|
18892a14c2 | ||
|
|
6cbde80a1c | ||
|
|
6fc875aa72 | ||
|
|
29f5c3c758 | ||
|
|
afa6411004 | ||
|
|
67526c30da | ||
|
|
dc67dbc489 | ||
|
|
05ac2feebd | ||
|
|
6f30c04899 | ||
|
|
dfce8e9749 | ||
|
|
aae326133b | ||
|
|
15383a6b92 | ||
|
|
5d8a1deb35 | ||
|
|
b09d12aad5 | ||
|
|
488acc3e69 | ||
|
|
32534c870d | ||
|
|
74af1516ac | ||
|
|
07b2f175f3 | ||
|
|
1a1c65afce | ||
|
|
90282b858e | ||
|
|
f819922d8a | ||
|
|
aa8fdf557e | ||
|
|
4896cf0ad3 | ||
|
|
2e61e2a366 | ||
|
|
172a7444c9 | ||
|
|
3ea5b16487 | ||
|
|
e83a169fd6 | ||
|
|
53c1c0103c | ||
|
|
e467402a15 | ||
|
|
d5fc16dccb | ||
|
|
3be84edb5a | ||
|
|
fe3bb22311 | ||
|
|
86c2cf96a9 | ||
|
|
67ba8340cc | ||
|
|
ac69640253 | ||
|
|
64965bcc93 | ||
|
|
2eb73f3746 | ||
|
|
8f761c31e3 | ||
|
|
528b61862a | ||
|
|
c7003a7139 | ||
|
|
75e65b082e | ||
|
|
22d38b783d | ||
|
|
ebdada57a6 | ||
|
|
28f8850013 | ||
|
|
540977c637 | ||
|
|
4ba74e9f3e | ||
|
|
cecdb1681c | ||
|
|
2947da2a00 | ||
|
|
da6000b4da | ||
|
|
8be7207c3c | ||
|
|
0a806a74b1 | ||
|
|
d56f9d6e6e | ||
|
|
dc742763a8 | ||
|
|
0b9b87ba23 | ||
|
|
346be22ea9 | ||
|
|
b2c95d96b1 | ||
|
|
1c801bb205 | ||
|
|
163d240a9c | ||
|
|
19fc3a6d40 | ||
|
|
4c9bc002f4 | ||
|
|
b3a5d50ba8 | ||
|
|
f7919129b6 | ||
|
|
dccb1d91e2 | ||
|
|
41b058aeaf | ||
|
|
f2a12634a4 | ||
|
|
8d33f45388 | ||
|
|
cb7e230214 | ||
|
|
badd05a014 | ||
|
|
47ffe9a2ec | ||
|
|
4fe74eb5fe | ||
|
|
1c40896dc0 | ||
|
|
872b4ce1c6 | ||
|
|
c0fed1a391 | ||
|
|
d1f584e490 | ||
|
|
79f5011d58 | ||
|
|
e350edd41f | ||
|
|
23a6e56478 | ||
|
|
80050f11f2 | ||
|
|
25c46cc978 | ||
|
|
458c42ae7c | ||
|
|
febeeadc7b | ||
|
|
044643559d | ||
|
|
782e668abf | ||
|
|
fa6af88566 | ||
|
|
c106c4fc2a | ||
|
|
09780f64de | ||
|
|
81d4558b2f | ||
|
|
3de0e37fd5 | ||
|
|
fcfd2f8ba5 | ||
|
|
7e7e461237 | ||
|
|
2afab030bd | ||
|
|
5cbeee6e86 | ||
|
|
87bbffefc3 | ||
|
|
a2877cab70 | ||
|
|
4f10b6dbc3 | ||
|
|
24a213a8ff | ||
|
|
cf21044cf7 | ||
|
|
289a5d7051 | ||
|
|
b5ae76d298 | ||
|
|
598cf60e10 | ||
|
|
c4ca2103e4 | ||
|
|
4b55a3e525 | ||
|
|
376b81f841 | ||
|
|
ee6035c59a | ||
|
|
a757330788 | ||
|
|
bf141202f8 | ||
|
|
1be3127177 | ||
|
|
c74a43cd74 | ||
|
|
1b520d3c96 | ||
|
|
b7418a2a3a | ||
|
|
eee67cdb53 | ||
|
|
1d70bc4ed7 | ||
|
|
d06b0dd6d6 | ||
|
|
cbbac0554e | ||
|
|
5f16766997 | ||
|
|
dcd6cae0f6 | ||
|
|
cc43993d5b | ||
|
|
2478ffe331 | ||
|
|
18ae960f86 | ||
|
|
fcb226e357 | ||
|
|
58c04b7a7b | ||
|
|
a6401fa9ef | ||
|
|
2481124ada | ||
|
|
488fde47e5 | ||
|
|
e7e77c99d0 | ||
|
|
6fbd35ed33 | ||
|
|
fba2339280 | ||
|
|
bd8270a4ae | ||
|
|
5299061c87 | ||
|
|
e843aeac55 | ||
|
|
19c792209b | ||
|
|
277d442634 | ||
|
|
51bec043e3 | ||
|
|
b7595f6f22 | ||
|
|
55181d583f | ||
|
|
b5a5a4f32e | ||
|
|
a746779efa | ||
|
|
736b6ea0a5 | ||
|
|
884f07baa6 | ||
|
|
b496c3136e | ||
|
|
bbfb40f973 | ||
|
|
a73e1fc164 | ||
|
|
ef7acb7fa0 | ||
|
|
fcd108adbe | ||
|
|
c9c76bfd10 | ||
|
|
d9302a3120 | ||
|
|
0cb3308dd1 | ||
|
|
583b9d7cf7 | ||
|
|
d7728ebc25 | ||
|
|
b6742526aa | ||
|
|
4defd8ac1b | ||
|
|
d28672e2ad | ||
|
|
94d6a51358 | ||
|
|
54eabc948f | ||
|
|
6c9e26ce83 | ||
|
|
a934037179 | ||
|
|
396444a089 | ||
|
|
699444abdd | ||
|
|
088031f807 | ||
|
|
8ce6e2c111 | ||
|
|
e7f36b9fcf | ||
|
|
dab91f526e | ||
|
|
35e23a7cbc | ||
|
|
7df05df48f | ||
|
|
707a1ae628 | ||
|
|
a65fc4076b | ||
|
|
f487897cf2 | ||
|
|
869d9ed543 | ||
|
|
8f61ed7561 | ||
|
|
8cb6a32341 | ||
|
|
687260e53c | ||
|
|
a919ce4554 | ||
|
|
d19d7acedd | ||
|
|
a393b3aff5 | ||
|
|
8c2036b6b4 | ||
|
|
dc081861ad | ||
|
|
ec76d2ba7d | ||
|
|
47ce0a8e9c | ||
|
|
f1c730962b | ||
|
|
d95a5131f6 | ||
|
|
69637cf482 | ||
|
|
25d6ec3a92 | ||
|
|
8bf5ff4e7f | ||
|
|
fd4e4be3a8 | ||
|
|
e0a01ea2ca | ||
|
|
256ccb96a6 | ||
|
|
bfffdc7d21 | ||
|
|
3bbe21268b | ||
|
|
1537e88a74 | ||
|
|
0d9711e62d | ||
|
|
0a7c5f06c6 | ||
|
|
e7a8a32b73 | ||
|
|
62b703f2e4 | ||
|
|
f44921d136 | ||
|
|
cd42545c7d | ||
|
|
0df3fd7bed | ||
|
|
56241da281 | ||
|
|
05a04104dd | ||
|
|
50d27ee186 | ||
|
|
371926b5a6 | ||
|
|
97face2406 | ||
|
|
ea64b40011 | ||
|
|
6d23e489aa | ||
|
|
f4d75e211f | ||
|
|
48f6cc644d | ||
|
|
33f517333f | ||
|
|
7253a309c7 | ||
|
|
f2aff5882d | ||
|
|
0dd61a7270 | ||
|
|
420b8e6d67 | ||
|
|
9e08919ce0 | ||
|
|
a7324f6c49 | ||
|
|
721c0834db | ||
|
|
e37629aaea | ||
|
|
a43a4a429e | ||
|
|
f5d34198e7 | ||
|
|
46604293e9 | ||
|
|
58e405cccd | ||
|
|
1deea5bebc | ||
|
|
cad5636f92 | ||
|
|
90290bee94 | ||
|
|
9f7ebceeb9 | ||
|
|
60ea2c7e12 | ||
|
|
441c866160 | ||
|
|
33d3341bb3 | ||
|
|
1d778fd78e | ||
|
|
0cd73a6304 | ||
|
|
2fedfe5b18 | ||
|
|
77c140bee6 | ||
|
|
c06e38d71f | ||
|
|
9b59cec22a | ||
|
|
306e992789 | ||
|
|
8335cfed69 | ||
|
|
4702aa1987 | ||
|
|
145e1b31a0 | ||
|
|
f2e28de20c | ||
|
|
bcaaf88edb | ||
|
|
120698fb43 | ||
|
|
dd88585ffa | ||
|
|
93a8c450ad | ||
|
|
3451434997 | ||
|
|
0bab2e74b0 | ||
|
|
c1a877489b | ||
|
|
520099aaba | ||
|
|
9bd02b3dd2 | ||
|
|
ba64f2c189 | ||
|
|
fbb194a6c5 | ||
|
|
30b2d2134b | ||
|
|
7eea71e282 | ||
|
|
e40b3029e0 | ||
|
|
17492c78fa | ||
|
|
a9276d3f72 | ||
|
|
eecbe49955 | ||
|
|
be9874a9ad | ||
|
|
b4fcc057c5 | ||
|
|
d9499dc56b | ||
|
|
f6806c88ab | ||
|
|
2caca545ad | ||
|
|
ff0f11da7f | ||
|
|
daa1822bc5 | ||
|
|
d490892092 | ||
|
|
182a4e7b06 | ||
|
|
e9beed03ea | ||
|
|
8351527aea | ||
|
|
ddff663b0b | ||
|
|
31b4fb9201 | ||
|
|
1f7f0da383 | ||
|
|
9fa1079248 | ||
|
|
ae0cf5e3d7 | ||
|
|
672369c598 | ||
|
|
73687f248b | ||
|
|
65cc2a948a | ||
|
|
299f36dcc0 | ||
|
|
f5cbce5195 | ||
|
|
a3780667b2 | ||
|
|
a21742a326 | ||
|
|
40aacb2e61 | ||
|
|
ba18869c85 | ||
|
|
a454f2c523 | ||
|
|
7caebaa6ba | ||
|
|
580abd7317 | ||
|
|
3187431611 | ||
|
|
81c37c9a0a | ||
|
|
ba5bca20da | ||
|
|
533fe187bd | ||
|
|
c4da26c19d | ||
|
|
ca7bfcc8c6 | ||
|
|
6c7b582faf | ||
|
|
6f2e6135f1 | ||
|
|
37a92e03c1 | ||
|
|
35385f8916 | ||
|
|
a2fbdd8e0a | ||
|
|
bab610722f | ||
|
|
4cabc8d132 | ||
|
|
e8bd381907 | ||
|
|
42aac802dd | ||
|
|
d748a20445 | ||
|
|
5db3ff934d | ||
|
|
9bfa3ede20 | ||
|
|
127466e546 | ||
|
|
16aa30cac7 | ||
|
|
05a3d0111b | ||
|
|
9f4b5fb14a | ||
|
|
a716e68f87 | ||
|
|
2e127771ed | ||
|
|
c6bbba6051 | ||
|
|
443ed115b0 | ||
|
|
f450b8d863 | ||
|
|
acf570e652 | ||
|
|
4e9199c68e | ||
|
|
6f5575b155 | ||
|
|
700b854434 | ||
|
|
37b5d954e9 | ||
|
|
ac50e96568 | ||
|
|
86174c2e9f | ||
|
|
c1ae1dee06 | ||
|
|
c4aedf2958 | ||
|
|
736c8f2fc0 | ||
|
|
294084299e | ||
|
|
b1d30da4bc | ||
|
|
506f2e7c0c | ||
|
|
e13660dcdc | ||
|
|
4fbe2aa5af | ||
|
|
5ddc514545 | ||
|
|
3cfcbaec16 | ||
|
|
50f06e6a4f | ||
|
|
c7e2b15629 | ||
|
|
8e81f72584 | ||
|
|
0fb5ed00f6 | ||
|
|
bf7920cc15 | ||
|
|
971f03ae0f | ||
|
|
24b9d93920 | ||
|
|
8d81f4d5df | ||
|
|
b6e135a642 | ||
|
|
d3f4ade215 | ||
|
|
56d2a52706 | ||
|
|
b40dc1e05e | ||
|
|
bed3337866 | ||
|
|
28dc73d060 | ||
|
|
5ea1226ad0 | ||
|
|
e43f2a4158 | ||
|
|
d73e771ef3 | ||
|
|
c077e9418c | ||
|
|
2ce888c84c | ||
|
|
b4d175ff98 | ||
|
|
35e18d0481 | ||
|
|
f6cccb00e3 | ||
|
|
2a82c82039 | ||
|
|
a2e8a1b582 | ||
|
|
dbd78d4b86 | ||
|
|
8105892b6e | ||
|
|
bc7d5871e4 | ||
|
|
680e6a8c5f | ||
|
|
c364084cb2 | ||
|
|
63f8042746 | ||
|
|
48e6b2cea0 | ||
|
|
228309c571 | ||
|
|
ca3a3df3ba | ||
|
|
c7df37b17c | ||
|
|
41f03fbba4 | ||
|
|
b3089c1bba | ||
|
|
c5a6f1fea4 | ||
|
|
ba2777137b | ||
|
|
6d90be164a | ||
|
|
4da6f5859a | ||
|
|
15d4ea710a | ||
|
|
f0541c1f32 | ||
|
|
2b8cab1824 | ||
|
|
61c7972915 | ||
|
|
44a0eec74c | ||
|
|
e57753e44b | ||
|
|
74e91a2950 | ||
|
|
7ce3ef41df | ||
|
|
444a77933a | ||
|
|
78f78c607d | ||
|
|
69155002dc | ||
|
|
475d14fab7 | ||
|
|
f4d7e63358 | ||
|
|
f222d2b45b | ||
|
|
d0155fe1da | ||
|
|
4737d0b507 | ||
|
|
09bd0197bb | ||
|
|
f0f120bd32 | ||
|
|
c897da29c6 | ||
|
|
e347e254e8 | ||
|
|
12363aebf0 | ||
|
|
ff0b925313 | ||
|
|
a6b4bd9b80 | ||
|
|
485fa5476c | ||
|
|
689076d97c | ||
|
|
cca6102e91 | ||
|
|
095855162b | ||
|
|
326f934013 | ||
|
|
35b0c258fe | ||
|
|
6650846b15 | ||
|
|
a33d320f4c | ||
|
|
0bcfea6aa4 | ||
|
|
db6e63e91c | ||
|
|
a7f84fb53a | ||
|
|
ce7f2b1765 | ||
|
|
abbfb59eff | ||
|
|
b57fef16f9 | ||
|
|
1706afbcd0 | ||
|
|
d01bd69aa5 | ||
|
|
e6b9ae3ee1 | ||
|
|
73b6b8045d | ||
|
|
3c87abc6e8 | ||
|
|
e95925c129 | ||
|
|
82c44d6c40 | ||
|
|
e0a268e05e | ||
|
|
487892e1d9 | ||
|
|
7ab4e88359 | ||
|
|
4702b6ee75 | ||
|
|
20cd3aa66d | ||
|
|
391114bdef | ||
|
|
ffe1052ecc | ||
|
|
b4159152a7 | ||
|
|
8fc6e4b0f7 | ||
|
|
c8938b7a4b | ||
|
|
34db1626a5 | ||
|
|
7f0c97c5b9 | ||
|
|
74050c46e7 | ||
| 3ed1cb665a | |||
|
|
a6f33ba69d | ||
|
|
176b690417 | ||
|
|
e22b1b90ec | ||
|
|
a3c560790c | ||
| 570e2108dd | |||
|
|
59c0d95e5d | ||
|
|
9ceaecdc02 | ||
|
|
19d50898ef | ||
|
|
5b62ca62ae | ||
| fb528d30ce | |||
| 5f2d5ed334 | |||
| c20e459900 | |||
| 09220825cf | |||
|
|
3d4db23d27 | ||
|
|
4496b67dcc | ||
|
|
2a13298384 | ||
|
|
d6ec404d76 | ||
|
|
3235fd4dc1 | ||
|
|
293740fee2 | ||
|
|
d3c420ec84 | ||
|
|
85ef09d43c | ||
|
|
e4561d69c4 | ||
|
|
6b4d206099 | ||
|
|
50843edf6a | ||
|
|
3a1686f8da | ||
|
|
66dd91bb97 | ||
|
|
e020e5d593 | ||
|
|
3d62663281 | ||
|
|
8d9485c02e | ||
|
|
9fb45dd683 | ||
|
|
8a4924dba9 | ||
|
|
beaf7be606 | ||
|
|
7dbd21bdf4 | ||
|
|
9ce3056203 | ||
|
|
880b4e847c | ||
|
|
71e83d5bc8 | ||
|
|
432fd92cca | ||
|
|
907a9cea25 | ||
| 07391810ff | |||
| 5063421364 | |||
| 2b4d308783 | |||
| e2f49968b3 | |||
| 229778ebf9 | |||
|
|
d2c0972c93 | ||
|
|
d300b6e518 | ||
|
|
56cb7b2223 | ||
|
|
2fc42d5d60 | ||
|
|
999e90f709 | ||
|
|
45c6cd7d2a | ||
|
|
5aa0858021 | ||
|
|
e981da3932 | ||
|
|
2685be7306 | ||
|
|
a13c4628f5 | ||
|
|
b36417454c | ||
|
|
a6f5952dee | ||
|
|
d67dfe46f6 | ||
|
|
e57cc43500 | ||
|
|
481063285b | ||
|
|
d805523dbd | ||
|
|
58267f66fc | ||
|
|
48215f2e5e | ||
|
|
7e0fc1528a | ||
|
|
c185d20a93 | ||
|
|
7ec847e576 | ||
|
|
4ee16d4fc6 | ||
|
|
59d614a24e | ||
|
|
40037b3f4b | ||
|
|
f91c8b6205 | ||
|
|
8f73a99ff7 | ||
|
|
e0f07cb396 | ||
|
|
2950ab7472 | ||
|
|
0ab4053dfb | ||
|
|
745aa0f268 | ||
|
|
12bb93c2f0 | ||
| 95ca175901 | |||
| 71585ed29d | |||
|
|
231a377718 | ||
|
|
a80ee902a9 | ||
|
|
c2d3b5f92a | ||
|
|
64365af124 | ||
|
|
60619dbffc | ||
|
|
77e77e9cc3 | ||
|
|
d72b41a902 | ||
|
|
dfddaaea94 | ||
|
|
6b872c6f74 | ||
|
|
202c8aee77 | ||
|
|
ba397a1744 | ||
|
|
d699a152d5 | ||
|
|
beef44ea50 | ||
|
|
b991a2da7b | ||
|
|
bbe4addd83 | ||
|
|
600829645d | ||
|
|
68280cfe7e | ||
|
|
c5523aa95d | ||
|
|
93a68a5582 | ||
|
|
6155236d3c | ||
| 28f7f20728 | |||
| 235256d679 | |||
| 626d569858 | |||
| 0492179bdd | |||
| 1dfd1de5c1 | |||
| d15d01c32b | |||
|
|
c9f4278d9b | ||
|
|
6aa0540c9e | ||
|
|
9689dc9db1 | ||
|
|
c20e3324d4 | ||
| 303f44cd2b | |||
| 66b73973b4 | |||
|
|
0ff4e52ed3 | ||
| b6d1f724e9 | |||
| e77b9d4970 | |||
|
|
456b102c15 | ||
|
|
cc57997abc | ||
|
|
19e61c7cc3 | ||
|
|
f908513dca | ||
|
|
7dc39e6ada | ||
|
|
b0be53554d | ||
|
|
b98048bd39 | ||
|
|
fffd3fdc95 | ||
|
|
dea9ed2ccd | ||
|
|
de5744c9e4 | ||
|
|
a23f3d8f16 | ||
|
|
0c24bfdedd | ||
|
|
c0b95c6840 | ||
|
|
e977b09e20 | ||
|
|
4ff75cf199 | ||
|
|
24e70c70ce | ||
|
|
275a7bf00b | ||
|
|
455fc79818 | ||
|
|
8740c3c255 | ||
|
|
98bfca5bb9 | ||
|
|
fc8d01ad5f | ||
|
|
d9b8df1725 | ||
|
|
9b7802ac04 | ||
|
|
6e1f3e0406 | ||
|
|
a2d4bab062 | ||
|
|
f1cefad910 | ||
|
|
957c69bd1e | ||
|
|
2902c5e805 | ||
|
|
faf9f5d988 | ||
|
|
874f061468 | ||
|
|
69521a1f1f | ||
|
|
fb9dbed306 | ||
|
|
d42158ac90 | ||
|
|
ff3ebec562 | ||
|
|
ea79de617e | ||
|
|
9e1ad6a983 | ||
|
|
c17248df31 | ||
|
|
4c116ebd86 | ||
|
|
c490fe0c1c | ||
|
|
cefdf23799 | ||
|
|
7f70642606 | ||
|
|
1d5b2609f9 | ||
|
|
a206fe8b87 | ||
|
|
fb4f6c45ce | ||
|
|
c50ae245bd | ||
|
|
f6c2131ebf | ||
|
|
543d1d2922 | ||
|
|
bb61e6f6a2 | ||
|
|
2d1bbf6b48 | ||
|
|
67e42cfe3e | ||
|
|
429a2cf230 | ||
|
|
9ace85e604 | ||
|
|
f677b0e132 | ||
|
|
f137f044d0 | ||
|
|
77c12db31a | ||
|
|
b670cb29fe | ||
|
|
95d7bb071d | ||
|
|
eadb4c1dee | ||
|
|
6672a6c852 | ||
|
|
094feddda4 | ||
|
|
f86eddb298 | ||
|
|
a93b4917cd | ||
|
|
0f9767aa16 | ||
|
|
0c9f246690 | ||
|
|
517d258d61 | ||
|
|
61e3818563 | ||
|
|
776ec98a3e | ||
|
|
f3e844bb0a | ||
|
|
a55cd4ed05 | ||
|
|
8a985620f9 | ||
|
|
484ddf9dfa | ||
|
|
b611b4aad6 | ||
|
|
b9f973c718 | ||
|
|
cd6c971547 | ||
|
|
c68083484a | ||
|
|
f999e689bf | ||
|
|
187a08a621 | ||
|
|
c208298937 | ||
|
|
e9e3e5e129 | ||
|
|
1ef8225d10 | ||
|
|
ccadec23e4 | ||
|
|
0972a1959e | ||
|
|
ce74191947 | ||
|
|
4feeb9519c | ||
|
|
efee146215 | ||
|
|
96ce7b30d0 | ||
|
|
076339baec | ||
|
|
50026be73d | ||
|
|
a1ad634926 | ||
|
|
0a5defaf29 | ||
|
|
557efc47a8 | ||
|
|
c566f940e0 | ||
|
|
381062c6c5 |
15
.editorconfig
Normal file
@@ -0,0 +1,15 @@
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
||||
|
||||
[*.{yml,yaml}]
|
||||
indent_size = 2
|
||||
@@ -1,5 +0,0 @@
|
||||
#!/bin/bash
|
||||
if [ ! -d "/app/zhamao-framework/bin" ]; then
|
||||
cp -r /app/zhamao-framework-bak/* /app/zhamao-framework/
|
||||
fi
|
||||
php /app/zhamao-framework/bin/start
|
||||
21
.gitattributes
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
# Exclude build/test files from archive (reduce package size)
|
||||
/.github export-ignore
|
||||
/.phive export-ignore
|
||||
/build export-ignore
|
||||
/docs export-ignore
|
||||
/ext export-ignore
|
||||
/resources export-ignore
|
||||
/tests export-ignore
|
||||
/tests_old export-ignore
|
||||
/.editorconfig export-ignore
|
||||
/.gitattributes export-ignore
|
||||
/.gitignore export-ignore
|
||||
/.php-cs-fixer.php export-ignore
|
||||
/captainhook.json export-ignore
|
||||
/install-runtime.sh export-ignore
|
||||
/package.json export-ignore
|
||||
/phpstan.neon export-ignore
|
||||
/phpunit.xml.dist export-ignore
|
||||
|
||||
# Configure diff output
|
||||
*.php diff=php
|
||||
38
.github/ISSUE_TEMPLATE/1_Bug_report.yaml
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
name: 🐛 漏洞(BUG)报告
|
||||
description: ⚠️ 请不要直接在此提交安全漏洞
|
||||
labels: bug
|
||||
|
||||
body:
|
||||
- type: input
|
||||
id: affected-versions
|
||||
attributes:
|
||||
label: 受影响版本
|
||||
placeholder: x.y.z
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: description
|
||||
attributes:
|
||||
label: 描述
|
||||
description: 请详细地描述您的问题
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: reproduce-steps
|
||||
attributes:
|
||||
label: 复现步骤
|
||||
description: |
|
||||
请尽可能地提供可以复现此步骤的漏洞。
|
||||
如果步骤过长或难以描述,您可以自行建立一个用于复现漏洞的仓库。
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: possible-solution
|
||||
attributes:
|
||||
label: 解决方案
|
||||
description: 如果您对这个漏洞的成因或修复有任何意见的话,请在此提出
|
||||
- type: textarea
|
||||
id: additional-context
|
||||
attributes:
|
||||
label: 附加信息
|
||||
description: 其他可能有帮助的信息,如日志、截图等
|
||||
19
.github/ISSUE_TEMPLATE/2_Feature_request.yaml
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
name: 🚀 功能建议
|
||||
description: 新功能、改进的意见、草案
|
||||
labels: enhancement
|
||||
|
||||
body:
|
||||
- type: textarea
|
||||
id: description
|
||||
attributes:
|
||||
label: 描述
|
||||
description: 请提供简洁清楚的描述
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: example
|
||||
attributes:
|
||||
label: 例子
|
||||
description: |
|
||||
一个简单的例子,展示该功能将如何被使用(包括代码、配置文件等)
|
||||
如果这是针对已有功能的改进,请展示改进前后使用方式(或效能)的对比
|
||||
43
.github/workflows/coding-style.yml
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
name: Code Style
|
||||
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
pre_job:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
should_skip: ${{ steps.skip_check.outputs.should_skip }}
|
||||
steps:
|
||||
- id: skip_check
|
||||
uses: fkirc/skip-duplicate-actions@v5
|
||||
with:
|
||||
concurrent_skipping: same_content_newer
|
||||
skip_after_successful_duplicate: true
|
||||
paths: '["src/**", "tests/**"]'
|
||||
do_not_skip: '["workflow_dispatch", "schedule"]'
|
||||
|
||||
cs-fix:
|
||||
needs: pre_job
|
||||
if: ${{ needs.pre_job.outputs.should_skip != 'true' }}
|
||||
name: Code Style
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Setup PHP
|
||||
uses: sunxyw/workflows/setup-environment@main
|
||||
with:
|
||||
php-version: 8.0
|
||||
php-extensions: swoole, posix, json
|
||||
operating-system: ubuntu-latest
|
||||
use-cache: true
|
||||
|
||||
- name: Code Style
|
||||
uses: sunxyw/workflows/coding-style@main
|
||||
61
.github/workflows/increment-build-number.yml
vendored
Normal file
@@ -0,0 +1,61 @@
|
||||
name: Increment Build Number
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
types:
|
||||
- closed
|
||||
paths:
|
||||
- 'src/**.php'
|
||||
|
||||
jobs:
|
||||
incremental-build-number:
|
||||
if: github.event.pull_request.merged == true
|
||||
name: Incremental Build Number
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
ref: ${{ github.base_ref }}
|
||||
|
||||
- name: Setup PHP
|
||||
uses: sunxyw/workflows/setup-environment@main
|
||||
with:
|
||||
php-version: 8.1
|
||||
php-extensions: swoole, posix, json
|
||||
operating-system: ubuntu-latest
|
||||
use-cache: true
|
||||
|
||||
- name: Prepare Doxygen
|
||||
id: prepare-doxygen
|
||||
continue-on-error: true
|
||||
run: bin/prepare-doxygen before
|
||||
|
||||
- name: Generate Doxygen
|
||||
if: steps.prepare-doxygen.outcome == 'success'
|
||||
uses: mattnotmitt/doxygen-action@v1.9.5
|
||||
|
||||
- name: Finishing Doxygen
|
||||
if: steps.prepare-doxygen.outcome == 'success'
|
||||
run: bin/prepare-doxygen after
|
||||
|
||||
- name: Commit API Docs
|
||||
uses: stefanzweifel/git-auto-commit-action@v4
|
||||
with:
|
||||
commit_message: update api docs
|
||||
branch: ${{ github.base_ref }}
|
||||
|
||||
- name: Increment build number
|
||||
id: increment-build-number
|
||||
run: |
|
||||
sed -i -r 's/(.*)(\VERSION_ID\s=\s)([0-9]+)(.*)/echo "\1\2$((\3+1))\4"/ge' src/ZM/Framework.php
|
||||
BUILD_ID=$(cat src/ZM/Framework.php | grep "VERSION_ID = " | sed 's/[^0-9]//g')
|
||||
echo "::set-output name=build_id::$BUILD_ID"
|
||||
|
||||
- name: Commit build number
|
||||
uses: stefanzweifel/git-auto-commit-action@v4
|
||||
with:
|
||||
commit_message: increment build number (build ${{ steps.increment-build-number.outputs.build_id }})
|
||||
branch: ${{ github.base_ref }}
|
||||
43
.github/workflows/static-analysis.yml
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
name: Static Analysis
|
||||
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
pre_job:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
should_skip: ${{ steps.skip_check.outputs.should_skip }}
|
||||
steps:
|
||||
- id: skip_check
|
||||
uses: fkirc/skip-duplicate-actions@v5
|
||||
with:
|
||||
concurrent_skipping: same_content_newer
|
||||
skip_after_successful_duplicate: true
|
||||
paths: '["src/**", "tests/**"]'
|
||||
do_not_skip: '["workflow_dispatch", "schedule"]'
|
||||
|
||||
analyse:
|
||||
needs: pre_job
|
||||
if: ${{ needs.pre_job.outputs.should_skip != 'true' }}
|
||||
name: Static Analysis
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Setup PHP
|
||||
uses: sunxyw/workflows/setup-environment@main
|
||||
with:
|
||||
php-version: 8.0
|
||||
php-extensions: swoole, posix, json
|
||||
operating-system: ubuntu-latest
|
||||
use-cache: true
|
||||
|
||||
- name: Static Analysis
|
||||
uses: sunxyw/workflows/static-analysis@main
|
||||
55
.github/workflows/test.yml
vendored
Normal file
@@ -0,0 +1,55 @@
|
||||
name: Test
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- develop
|
||||
- '*-dev*'
|
||||
pull_request:
|
||||
types:
|
||||
- opened
|
||||
- reopened
|
||||
- review_requested
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
pre_job:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
should_skip: ${{ steps.skip_check.outputs.should_skip }}
|
||||
steps:
|
||||
- id: skip_check
|
||||
uses: fkirc/skip-duplicate-actions@v5
|
||||
with:
|
||||
concurrent_skipping: same_content_newer
|
||||
skip_after_successful_duplicate: true
|
||||
paths: '["src/**", "tests/**", "bin/phpunit-zm"]'
|
||||
do_not_skip: '["workflow_dispatch", "schedule"]'
|
||||
|
||||
test:
|
||||
needs: pre_job
|
||||
if: ${{ needs.pre_job.outputs.should_skip != 'true' }}
|
||||
strategy:
|
||||
matrix:
|
||||
operating-system: [ ubuntu-latest, windows-latest, macos-latest ]
|
||||
php-version: [ 8.0, 8.1, 8.2 ]
|
||||
name: PHP ${{ matrix.php-version }} Test (${{ matrix.operating-system }})
|
||||
runs-on: ${{ matrix.operating-system }}
|
||||
timeout-minutes: 10
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Setup PHP
|
||||
uses: sunxyw/workflows/setup-environment@main
|
||||
with:
|
||||
php-version: ${{ matrix.php-version }}
|
||||
php-extensions: swoole, posix, json
|
||||
operating-system: ${{ matrix.operating-system }}
|
||||
use-cache: true
|
||||
|
||||
- name: Test
|
||||
uses: sunxyw/workflows/test@main
|
||||
51
.github/workflows/vuepress-deploy.yml
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
name: Docs and Script Auto Deploy
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- 'docs/**'
|
||||
- 'ext/**'
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Deploy docs
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout master
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Deploy docs to GitHub Pages
|
||||
uses: jenkey2011/vuepress-deploy@master
|
||||
env:
|
||||
ACCESS_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
BUILD_SCRIPT: yarn && yarn docs:build
|
||||
BUILD_DIR: docs/.vuepress/dist/
|
||||
- name: Copy deployment to current folder
|
||||
run: |
|
||||
cp -r "${GITHUB_WORKSPACE}/docs/.vuepress/dist" "./deploy"
|
||||
- name: Deploy to Zhamao Server
|
||||
uses: easingthemes/ssh-deploy@main
|
||||
env:
|
||||
SSH_PRIVATE_KEY: ${{ secrets.ZHAMAO_XIN_PRIVATE_KEY }}
|
||||
ARGS: "-rltgoDzvO --delete"
|
||||
SOURCE: "deploy/"
|
||||
REMOTE_HOST: ${{ secrets.ZHAMAO_XIN_HOST }}
|
||||
REMOTE_USER: ${{ secrets.ZHAMAO_XIN_USER }}
|
||||
TARGET: ${{ secrets.FRAMEWORK_ZHAMAO_XIN_TARGET }}
|
||||
- name: deploy script file
|
||||
uses: wlixcc/SFTP-Deploy-Action@v1.2
|
||||
with:
|
||||
username: ${{ secrets.ZHAMAO_XIN_USER }}
|
||||
server: ${{ secrets.ZHAMAO_XIN_HOST }}
|
||||
ssh_private_key: ${{ secrets.ZHAMAO_XIN_PRIVATE_KEY }}
|
||||
local_path: './ext/go.sh'
|
||||
remote_path: ${{ secrets.ZHAMAO_XIN_MAIN_TARGET }}
|
||||
- name: deploy script file
|
||||
uses: wlixcc/SFTP-Deploy-Action@v1.2
|
||||
with:
|
||||
username: ${{ secrets.ZHAMAO_XIN_USER }}
|
||||
server: ${{ secrets.ZHAMAO_XIN_HOST }}
|
||||
ssh_private_key: ${{ secrets.ZHAMAO_XIN_PRIVATE_KEY }}
|
||||
local_path: './ext/v3.sh'
|
||||
remote_path: ${{ secrets.ZHAMAO_XIN_MAIN_TARGET }}
|
||||
89
.gitignore
vendored
@@ -1,12 +1,87 @@
|
||||
.idea/
|
||||
/src/test/
|
||||
/src/webconsole/config/
|
||||
/vendor/
|
||||
zm.json
|
||||
### Zhamao Framework ###
|
||||
/zm_data/
|
||||
composer.lock
|
||||
/resources/server.phar
|
||||
/distribute/
|
||||
/bin/.phpunit.result.cache
|
||||
/resources/zhamao.service
|
||||
/runtime/
|
||||
/tmp/
|
||||
/temp/
|
||||
/site/
|
||||
/plugins/
|
||||
/doxy/
|
||||
/walle/
|
||||
# 框架审计文件
|
||||
audit.log
|
||||
|
||||
# 进程锁文件
|
||||
.daemon_pid
|
||||
.zm_worker_*.pid
|
||||
|
||||
### Composer ###
|
||||
composer.phar
|
||||
/vendor/
|
||||
composer.lock
|
||||
|
||||
### Go CQHTTP ###
|
||||
/ext/go-cqhttp/data/
|
||||
/ext/go-cqhttp/logs/
|
||||
/ext/go-cqhttp/config.hjson
|
||||
/ext/go-cqhttp/device.json
|
||||
/ext/go-cqhttp/go-cqhttp
|
||||
/ext/go-cqhttp/session.token
|
||||
|
||||
### Git ###
|
||||
# 备份文件
|
||||
*.orig
|
||||
|
||||
# 解决 Merge 冲突时可能会自动生成的文件
|
||||
*.BACKUP.*
|
||||
*.BASE.*
|
||||
*.LOCAL.*
|
||||
*.REMOTE.*
|
||||
*_BACKUP_*.txt
|
||||
*_BASE_*.txt
|
||||
*_LOCAL_*.txt
|
||||
*_REMOTE_*.txt
|
||||
|
||||
# Git Hooks
|
||||
cghooks.lock
|
||||
|
||||
### VuePress ###
|
||||
/node_modules/
|
||||
/docs/.vuepress/dist/
|
||||
package-lock.json
|
||||
|
||||
### PHPUnit ###
|
||||
|
||||
# 生成的文件
|
||||
/bin/.phpunit.result.cache
|
||||
.phpunit.result.cache
|
||||
.phpunit.cache
|
||||
|
||||
# 本地配置
|
||||
/phpunit.xml
|
||||
|
||||
# 构建目录
|
||||
/build/
|
||||
|
||||
### PhpStorm ###
|
||||
# 兼容 PHPStorm 及其他 Jetbrains IDE
|
||||
|
||||
# 除了代码格式配置和运行配置之外的所有文件
|
||||
.idea/*
|
||||
|
||||
!.idea/codeStyles
|
||||
!.idea/runConfigurations
|
||||
|
||||
### Phive ###
|
||||
/.phive/
|
||||
/tools/
|
||||
|
||||
### ASDF ###
|
||||
/.tool-version
|
||||
|
||||
.DS_Store
|
||||
|
||||
### PHP CS Fixer ###
|
||||
.php-cs-fixer.cache
|
||||
|
||||
76
.php-cs-fixer.php
Normal file
@@ -0,0 +1,76 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @since 2.7.0
|
||||
*/
|
||||
|
||||
return (new PhpCsFixer\Config())
|
||||
->setRiskyAllowed(true)
|
||||
->setRules([
|
||||
'@PSR12' => true,
|
||||
'@Symfony' => true,
|
||||
'@PhpCsFixer' => true,
|
||||
'array_syntax' => [
|
||||
'syntax' => 'short',
|
||||
],
|
||||
'list_syntax' => [
|
||||
'syntax' => 'short',
|
||||
],
|
||||
'concat_space' => [
|
||||
'spacing' => 'one',
|
||||
],
|
||||
'blank_line_before_statement' => [
|
||||
'statements' => [
|
||||
'declare',
|
||||
],
|
||||
],
|
||||
'ordered_imports' => [
|
||||
'imports_order' => [
|
||||
'class',
|
||||
'function',
|
||||
'const',
|
||||
],
|
||||
'sort_algorithm' => 'alpha',
|
||||
],
|
||||
'single_line_comment_style' => [
|
||||
'comment_types' => [
|
||||
],
|
||||
],
|
||||
'yoda_style' => [
|
||||
'always_move_variable' => false,
|
||||
'equal' => false,
|
||||
'identical' => false,
|
||||
],
|
||||
'multiline_whitespace_before_semicolons' => [
|
||||
'strategy' => 'no_multi_line',
|
||||
],
|
||||
'constant_case' => [
|
||||
'case' => 'lower',
|
||||
],
|
||||
'class_attributes_separation' => true,
|
||||
'combine_consecutive_unsets' => true,
|
||||
'declare_strict_types' => true,
|
||||
'linebreak_after_opening_tag' => true,
|
||||
'lowercase_static_reference' => true,
|
||||
'no_useless_else' => true,
|
||||
'no_unused_imports' => true,
|
||||
'not_operator_with_successor_space' => false,
|
||||
'not_operator_with_space' => false,
|
||||
'ordered_class_elements' => true,
|
||||
'php_unit_strict' => false,
|
||||
'phpdoc_separation' => false,
|
||||
'single_quote' => true,
|
||||
'standardize_not_equals' => true,
|
||||
'multiline_comment_opening_closing' => true,
|
||||
'phpdoc_summary' => false,
|
||||
'php_unit_test_class_requires_covers' => false,
|
||||
])
|
||||
->setFinder(
|
||||
PhpCsFixer\Finder::create()
|
||||
->in(__DIR__ . '/src')
|
||||
->in(__DIR__ . '/tests')
|
||||
->in(__DIR__ . '/config')
|
||||
->in(__DIR__ . '/bin')
|
||||
);
|
||||
11
.phpstorm.meta.php
Normal file
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
namespace PHPSTORM_META {
|
||||
|
||||
use OneBot\V12\Object\Event\OneBotEvent;
|
||||
use ZM\Context\Context;
|
||||
|
||||
override(Context::__call(0), map([
|
||||
'getBotEvent' => OneBotEvent::class,
|
||||
]));
|
||||
}
|
||||
68
CONTRIBUTING.md
Normal file
@@ -0,0 +1,68 @@
|
||||
# 炸毛框架贡献指南
|
||||
|
||||
首先很高兴你愿意读这篇文章,因为我们需要更多的开发者来帮助我们开发和维护。
|
||||
|
||||
如果你还没加入我们的 [QQ 群(670821194)][qq-group],我们建议你加入一下,以便和其他开发者实时交流和协作。
|
||||
|
||||
## 测试
|
||||
|
||||
我们目前采用 PHPUnit 作为测试框架,如果你编写了新的功能或改变了现有的行为,我们希望你可以编写相应的测试代码。
|
||||
|
||||
## 提交变更
|
||||
|
||||
请使用 [GitHub Pull Request][new-pr] 提交你的更改,并附上恰当的说明(例如一个简明的列表列出所作的改动)。
|
||||
|
||||
同时,请不要忘记在提交前执行代码格式检查 `composer cs-fix` 和静态分析检查 `composer analyse`
|
||||
。虽然我们的自动化流程会在你推送到仓库后自动运行检查,但我仍然建议你提前在本地运行,以节省可能的修改成本。
|
||||
|
||||
对了,请尽量为你的每个函数、方法加上 PHPDoc 注释。
|
||||
|
||||
## 具体贡献流程
|
||||
|
||||
#### **你发现了个 BUG?**
|
||||
|
||||
- **如果你发现的是安全漏洞,请不要在 GitHub 上回报!** 请直接联系 [@crazywhalecc](https://github.com/crazywhalecc)。
|
||||
|
||||
- **确保该问题还没被其他人回报。** 你可以在 [Issue 列表][issues] 中搜索相关关键词来检查。
|
||||
|
||||
- 如果你没能找到一个已有的 Issue 来描述你遇到的问题,[请新建一个 Issue][new-issue]。请务必包含**标题和清晰的描述**
|
||||
,尽可能多的相关信息,以及一个**代码示例**或者一个**可执行的测试用例**,来描述你遇到的问题。
|
||||
|
||||
- 如果可以的话,请使用相关的 Issue 模板来创建 Issue。
|
||||
|
||||
#### **你修复了个 BUG?**
|
||||
|
||||
- 在 GitHub 上提交一个 Pull Request。
|
||||
|
||||
- 确保 PR 的描述清楚地描述了问题和解决方案。如果有相关的 Issue,请在 PR 描述中包含 Issue 编号。
|
||||
|
||||
#### **你想添加新功能或改变现有功能?**
|
||||
|
||||
- 虽然你可以直接提交 PR,但我们建议你先与我们的开发者讨论你的想法,以避免你的努力白费。你可以在 [QQ 群][qq-group]
|
||||
中与我们的开发者交流,或者在 [GitHub Issue][issues] 中提出你的想法。
|
||||
|
||||
- 如果你的想法被采纳,你可以在 GitHub 上[提交一个 Pull Request][new-pr]。
|
||||
|
||||
#### **你对源码有疑问?**
|
||||
|
||||
- 你可以在 [QQ 群][qq-group] 中与我们的开发者讨论,或者你也可以在 [GitHub Issue][issues] 中提出你的疑问。
|
||||
|
||||
#### **你想为文档做贡献?**
|
||||
|
||||
- 非常欢迎^^
|
||||
|
||||
- 你可以直接[提交一个 Pull Request][new-pr]。
|
||||
|
||||
炸毛框架是由志愿开发者共同维护的项目。我们欢迎你加入我们的开发。
|
||||
|
||||
感谢~ :heart: :heart: :heart:
|
||||
|
||||
炸毛框架开发团队
|
||||
|
||||
[issues]: https://github.com/zhamao-robot/zhamao-framework/issues
|
||||
|
||||
[new-pr]: https://github.com/zhamao-robot/zhamao-framework/pull/new/master
|
||||
|
||||
[new-issue]: https://github.com/zhamao-robot/zhamao-framework/issues/new
|
||||
|
||||
[qq-group]: https://jq.qq.com/?_wv=1027&k=KdIGy0UK
|
||||
@@ -1,3 +0,0 @@
|
||||
FROM zmbot/swoole:latest
|
||||
|
||||
# TODO: auto-setup entrypoint
|
||||
2
LICENSE
@@ -186,7 +186,7 @@
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
Copyright 2022 CrazyBot Developer Team
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
||||
151
README.md
@@ -1,95 +1,146 @@
|
||||
<div align="center">
|
||||
<img src="/resources/images/logo_trans.png" height = "150" alt="炸毛框架"><br>
|
||||
<img src="https://cdn.jsdelivr.net/gh/zhamao-robot/zhamao-framework/resources/images/logo_trans.png" width = "150" height = "150" alt="炸毛框架"><br>
|
||||
<h2>炸毛框架</h2>
|
||||
炸毛框架 (zhamao-framework) 是一个协程高性能的聊天机器人 + Web 服务器开发框架<br><br>
|
||||
炸毛框架 (zhamao-framework) 是一个高性能的聊天机器人 + Web 服务器开发框架<br><br>
|
||||
|
||||
[]()
|
||||
[](https://github.com/zhamao-robot/zhamao-framework/blob/master/LICENSE)
|
||||
[](https://packagist.org/packages/zhamao/framework)
|
||||
[]()
|
||||
<p align="center">
|
||||
<a href="https://onebot.dev/">
|
||||
<img src="https://img.shields.io/badge/OneBot-12-black?style=flat-square" alt="OneBot">
|
||||
</a>
|
||||
|
||||
[](https://github.com/zhamao-robot/zhamao-framework/search?q=stupid)
|
||||
[](https://github.com/zhamao-robot/zhamao-framework/search?q=TODO)
|
||||
<a href="https://github.com/zhamao-robot/zhamao-framework/actions">
|
||||
<img src="https://img.shields.io/github/actions/workflow/status/zhamao-robot/zhamao-framework/test.yml?branch=v3-develop&label=Test&style=flat-square" alt="Integration Test">
|
||||
</a>
|
||||
|
||||
<a href="https://packagist.org/packages/zhamao/framework">
|
||||
<img src="https://img.shields.io/packagist/dt/zhamao/framework?label=Downloads&style=flat-square" alt="下载数">
|
||||
</a>
|
||||
|
||||
<a href="https://github.com/zhamao-robot/zhamao-framework/releases">
|
||||
<img src="https://img.shields.io/packagist/v/zhamao/framework?include_prereleases&label=Release&style=flat-square" alt="最新版本">
|
||||
</a>
|
||||
|
||||
<a href="https://github.com/zhamao-robot/zhamao-framework/blob/master/LICENSE">
|
||||
<img src="https://img.shields.io/github/license/zhamao-robot/zhamao-framework?label=License&style=flat-square" alt="开源协议">
|
||||
</a>
|
||||
|
||||
<a href="https://github.com/zhamao-robot/zhamao-framework/search?q=TODO">
|
||||
<img src="https://img.shields.io/github/search/zhamao-robot/zhamao-framework/TODO?label=TODO%20Counter&style=flat-square" alt="TODO">
|
||||
</a>
|
||||
</p>
|
||||
|
||||
</div>
|
||||
|
||||
## 开发者注意
|
||||
**开发者 QQ 群:670821194**
|
||||
开发者 QQ 群:**670821194** [点击加入群聊](https://jq.qq.com/?_wv=1027&k=YkNI3AIr)
|
||||
|
||||
**当前 v2 版本已正式发布,此 master 分支为 2.0 版本,如需查看 v1 版本,请移步 `v1-legacy` 分支!**
|
||||
**如果有愿意一起开发框架本身的开发者,请提出 PR 或 Issue 参与开发!如果对框架本身的核心设计有更好的想法,可与作者成立开发组(目前仅 2 人),参与 OneBot V12 生态和框架本身的开发。**
|
||||
|
||||
**2.0 版本如果有问题请第一时间加群反馈!**
|
||||
|
||||
有关 3.0 版本的最新情况,请看这里:[Issue #22](https://github.com/zhamao-robot/zhamao-framework/issues/22)
|
||||
**相关正在进行的版本任务见 Projects 一栏!**
|
||||
|
||||
## 简介
|
||||
炸毛框架使用 PHP 编写,采用 Swoole 扩展为基础,主要面向 API 服务,聊天机器人(OneBot 兼容的 QQ 机器人对接),包含 Websocket、HTTP 等监听和请求库,用户代码采用模块化处理,使用注解可以方便地编写各类功能。
|
||||
|
||||
框架主要用途为 HTTP 服务器,机器人搭建框架。尤其对于 QQ 机器人消息处理较为方便和全面,提供了众多会话机制和内部调用机制,可以以各种方式设计你自己的模块。
|
||||
炸毛框架使用 PHP 编写,主要面向 API 服务,聊天机器人,包含 Websocket、HTTP
|
||||
等监听和请求库,用户代码采用模块化处理,使用注解可以方便地编写各类功能。
|
||||
|
||||
框架主要用途为 HTTP 服务器,机器人搭建框架。尤其对于聊天机器人消息处理较为方便和全面,提供了众多会话机制和内部调用机制,可以以各种方式设计你自己的插件。
|
||||
|
||||
```php
|
||||
/**
|
||||
* @CQCommand("你好")
|
||||
*/
|
||||
public function hello() {
|
||||
ctx()->reply("你好,我是炸毛!"); // 简单的命令式回复
|
||||
#[\BotCommand('你好')]
|
||||
public function hello(\BotContext $ctx) {
|
||||
$ctx->reply("你好,我是炸毛!"); // 简单的命令式回复
|
||||
}
|
||||
/**
|
||||
* @RequestMapping("/index")
|
||||
*/
|
||||
#[\Route('/index')]
|
||||
public function index() {
|
||||
return "<h1>hello!</h1>"; // 快速的 HTTP 服务开发
|
||||
}
|
||||
```
|
||||
|
||||
## 开始
|
||||
框架首先需要部署环境,可以参考下方文档中部署环境和框架的方法进行。
|
||||
|
||||
## 文档(v2 版本)
|
||||
查看文档:[https://docs-v2.zhamao.xin/](https://docs-v2.zhamao.xin/)
|
||||
框架目前支持 Linux、WSL、macOS、Windows 环境直接运行,其中 Linux、macOS 环境可直接使用下方一键安装脚本。
|
||||
|
||||
备用链接:[https://docs-v2.zhamao.me/](https://docs-v2.zhamao.me/)
|
||||
> 如果你想在其他环境安装部署,可使用 Docker 快速部署或手动安装环境后安装框架,详见文档。
|
||||
|
||||
自行构建文档:`mkdocs build -d distribute`
|
||||
```bash
|
||||
# Linux、macOS 下一键检测 PHP 环境、安装框架
|
||||
bash <(curl -fsSL https://zhamao.xin/v3.sh)
|
||||
|
||||
# 启动框架
|
||||
cd zhamao-v3
|
||||
./zhamao server
|
||||
```
|
||||
|
||||
一键安装脚本还有可以自定义参数的方式,比如:
|
||||
|
||||
```bash
|
||||
# 脚本默认会检测系统的PHP,如果想直接跳过检测,安装独立的PHP版本,则添加此环境变量
|
||||
export ZM_NO_LOCAL_PHP="yes"
|
||||
# 脚本如果安装独立版本PHP,默认版本为8.1,如果想使用其他版本,则添加此环境变量指定版本
|
||||
export ZM_DOWN_PHP_VERSION="8.2"
|
||||
# 脚本默认会将框架在当前目录下的 `zhamao-app` 目录进行安装,如果想使用其他目录,则添加此环境变量
|
||||
export ZM_CUSTOM_DIR="my-custom-app"
|
||||
# 脚本默认会对本项目使用阿里云国内加速镜像,如果想使用packagist源,则添加此环境变量
|
||||
export ZM_COMPOSER_PACKAGIST="yes"
|
||||
# 执行完前面的环境变量再执行一键安装脚本,就可以实现自定义参数!
|
||||
bash <(curl -fsSL https://zhamao.xin/v3.sh)
|
||||
```
|
||||
|
||||
关于其他安装方式,请参阅 [文档](https://framework.zhamao.xin/guide/installation.html) 。
|
||||
|
||||
## 文档
|
||||
|
||||
查看文档(国内自建):<https://framework.zhamao.xin/>
|
||||
|
||||
备用链接(国外托管):<https://framework.zhamao.me/>
|
||||
|
||||
## 特点
|
||||
- 支持多账号
|
||||
- 使用 Swoole 多工作进程机制和协程加持,尽可能简单的情况下提升了性能
|
||||
- 灵活的注解事件绑定机制
|
||||
- 支持下断点调试(Psysh)
|
||||
- 易用的上下文,模块内随处可用
|
||||
- 采用模块化编写,可单独拆装功能
|
||||
- 常驻内存,全局缓存变量随处使用
|
||||
- 自带 MySQL、Refis 等数据库连接池等数据库连接方案
|
||||
- 自带 HTTP 服务器、WebSocket 服务器可复用,可以构建属于自己的 HTTP API 接口
|
||||
- 静态文件服务器
|
||||
|
||||
## 从 v1 升级
|
||||
炸毛框架 v2 相对 v1 版本改动了不少内容,其中包括框架底层机制、注解事件分发、调试、命名空间等变化,详情可查看上方文档。
|
||||
|
||||
如果旧版框架使用过程中无问题且对新功能暂无需求,可以继续使用 v1 版本,后续也将维护安全类更新和修复致命 bug。
|
||||
- 原生支持多个机器人客户端同时连接
|
||||
- 灵活的注解事件绑定机制,可同时使用 Annotation 和原生 Attribute 注解
|
||||
- 完善的插件系统,可编写插件后打包或分发,供他人使用
|
||||
- 采用插件化编写,可自由搭配其他 Composer 组件,也可单文件面向过程编写
|
||||
- 支持模块打包、热加载,分享模块更方便
|
||||
- 常驻内存,全局缓存变量随处使用,提供多种缓存方案
|
||||
- 自带 MySQL、SQLite、Redis 等数据库连接池
|
||||
- 本身为 HTTP 服务器、WebSocket 服务器,可以构建属于自己的 HTTP API 接口
|
||||
- 可选自带 PHP 环境,无需手动编译安装,by [crazywhalecc/static-php-cli](https://github.com/crazywhalecc/static-php-cli)
|
||||
|
||||
## 贡献和捐赠
|
||||
如果你在使用过程中发现任何问题,可以提交 Issue 或自行 Fork 后修改并提交 Pull Request。目前项目仅一人维护,耗费精力较大,所以非常欢迎对框架的贡献。
|
||||
|
||||
如果你在使用过程中发现任何问题,可以提交 Issue 或自行 Fork 后修改并提交 Pull Request。
|
||||
|
||||
目前项目仅两人维护,耗费精力较大,所以非常欢迎对框架的贡献。
|
||||
|
||||
本项目为作者闲暇时间开发,如果觉得好用,不妨进行捐助~你的捐助会让我更加有动力完善插件,感谢你的支持!
|
||||
|
||||
我们会将捐赠的资金用于本项目驱动的炸毛机器人和框架文档的服务器开销上。
|
||||
我们会将捐赠的资金用于本项目驱动的炸毛机器人和框架文档的服务器开销上。[捐赠列表](https://github.com/zhamao-robot/thanks)
|
||||
|
||||
如果您不想直接参与框架的开发,也可以分享你编写的模块,帮助完善框架生态。
|
||||
|
||||
### 支付宝
|
||||

|
||||
|
||||
如果你对我们的周边感兴趣,我们还有炸毛机器人定制 logo 的雨伞,详情咨询作者 QQ,我们会作为您捐助了本项目!
|
||||

|
||||
|
||||
## 关于
|
||||
|
||||
框架和 SDK 是 炸毛机器人 项目的核心框架开源部分。炸毛机器人是作者写的一个高性能机器人,曾获全国计算机设计大赛一等奖。
|
||||
|
||||
欢迎随时在 HTTP-API 插件群里提问,当然更好的话可以加作者 QQ(627577391)或提交 Issue 进行疑难解答。
|
||||
作者的炸毛机器人已从2018年初起稳定运行了**五年**,并且持续迭代。
|
||||
|
||||
本项目在更新内容时,请及时关注 GitHub 动态,更新前请将自己的模块代码做好备份。
|
||||
可以提交 [Issue](https://github.com/zhamao-robot/zhamao-framework/issues/new/choose) 或 [加群(670821194)](https://jq.qq.com/?_wv=1027&k=YkNI3AIr) 进行疑难解答。
|
||||
|
||||
项目框架采用 Apache-2.0 协议开源,在分发或重写修改等操作时需遵守协议。项目模块部分(`Module` 文件夹) 在非借鉴框架内代码时可不遵守 Apache-2.0 协议进行分发和修改(声明版权)。
|
||||
本项目在更新内容时,请及时关注 GitHub 动态,更新前请将自己的插件或项目代码做好备份。
|
||||
|
||||
项目框架采用 Apache-2.0 协议开源,在分发或重写修改等操作时需遵守协议。项目插件部分(除 `src/Globals`、`src/ZM` 文件夹外的其他文件夹) 在非借鉴框架内代码时可不遵守 Apache-2.0 协议进行分发和修改(声明版权)。
|
||||
|
||||
**注意**:在你使用 mirai 等 `AGPL-3.0` 协议的机器人软件与框架连接时,使用本框架需要将你编写或修改的部分使用 `AGPL-3.0` 协议重新分发。
|
||||
|
||||
在贡献代码时,请保管好自己的全局配置文件中的敏感信息,请勿将带有个人信息的配置文件上传 GitHub 等网站。
|
||||
|
||||
感谢 JetBrains 为此开源项目提供 PhpStorm 开发工具支持:
|
||||
|
||||
<img src="https://resources.jetbrains.com/storage/products/company/brand/logos/PhpStorm.svg" width="300">
|
||||
|
||||
感谢开发者 @sunxyw 中为项目开发规范化提出的一些建议。
|
||||
|
||||

|
||||
|
||||
14
SECURITY.md
@@ -1,14 +0,0 @@
|
||||
# Security Policy
|
||||
|
||||
## Supported Versions
|
||||
|
||||
| Version | Supported |
|
||||
| ------- | ------------------ |
|
||||
| 2.0 | :white_check_mark: |
|
||||
| 1.6.x | :white_check_mark: |
|
||||
| 1.1.x | :x: |
|
||||
| 1.0.x | :x: |
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
If you find a bug which is safety related, you should post a new issue named **Security Issue**, and I will check it as soon as possible.
|
||||
@@ -1,65 +0,0 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
/**
|
||||
* Copyright: Swlib
|
||||
* Author: Twosee <twose@qq.com>
|
||||
* Date: 2018/4/14 下午10:58
|
||||
*/
|
||||
|
||||
Co::set([
|
||||
'log_level' => SWOOLE_LOG_INFO,
|
||||
'trace_flags' => 0
|
||||
]);
|
||||
|
||||
if (!ini_get('date.timezone')) {
|
||||
ini_set('date.timezone', 'UTC');
|
||||
}
|
||||
|
||||
foreach ([
|
||||
__DIR__ . '/../../../autoload.php',
|
||||
__DIR__ . '/../../autoload.php',
|
||||
__DIR__ . '/../vendor/autoload.php',
|
||||
__DIR__ . '/vendor/autoload.php'
|
||||
] as $file
|
||||
) {
|
||||
if (file_exists($file)) {
|
||||
define('PHPUNIT_COMPOSER_INSTALL', $file);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!defined('PHPUNIT_COMPOSER_INSTALL')) {
|
||||
fwrite(
|
||||
STDERR,
|
||||
'You need to set up the project dependencies using Composer:' . PHP_EOL . PHP_EOL .
|
||||
' composer install' . PHP_EOL . PHP_EOL .
|
||||
'You can learn all about Composer on https://getcomposer.org/.' . PHP_EOL
|
||||
);
|
||||
|
||||
die(1);
|
||||
} else {
|
||||
if (array_reverse(explode('/', __DIR__))[0] ?? '' === 'test') {
|
||||
$vendor_dir = dirname(PHPUNIT_COMPOSER_INSTALL);
|
||||
$bin_unit = "{$vendor_dir}/bin/phpunit";
|
||||
$unit_uint = "{$vendor_dir}/phpunit/phpunit/phpunit";
|
||||
if (file_exists($bin_unit)) {
|
||||
@unlink($bin_unit);
|
||||
@symlink(__FILE__, $bin_unit);
|
||||
}
|
||||
if (file_exists($unit_uint)) {
|
||||
@unlink($unit_uint);
|
||||
@symlink(__FILE__, $unit_uint);
|
||||
}
|
||||
}
|
||||
}
|
||||
require PHPUNIT_COMPOSER_INSTALL;
|
||||
$starttime = microtime(true);
|
||||
go(function (){
|
||||
try{
|
||||
PHPUnit\TextUI\Command::main(false);
|
||||
} catch(Exception $e) {
|
||||
echo $e->getMessage().PHP_EOL;
|
||||
}
|
||||
});
|
||||
Swoole\Event::wait();
|
||||
echo "Took ".round(microtime(true) - $starttime, 4). "s\n";
|
||||
65
bin/phpunit-zm
Executable file
@@ -0,0 +1,65 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
/** For Swoole coroutine tests */
|
||||
|
||||
// 如果改成 true,则会在终端显示所有炸毛框架的 Log
|
||||
const ZM_TEST_LOG_DEBUG = false;
|
||||
|
||||
use OneBot\Driver\Event\Process\WorkerStartEvent;
|
||||
use PHPUnit\TextUI\Command;
|
||||
use Swoole\Atomic;
|
||||
use Tests\ZMResultPrinter;
|
||||
use ZM\Command\Server\ServerStartCommand;
|
||||
use ZM\Event\EventProvider;
|
||||
use ZM\Framework;
|
||||
use ZM\Logger\ConsoleLogger;
|
||||
use ZM\Store\MockAtomic;
|
||||
|
||||
// 引入自动加载
|
||||
require $_composer_autoload_path ?? dirname(__DIR__) . '/vendor/autoload.php';
|
||||
|
||||
// 声明一个全局的原子计数,用于保存整个进程的退出状态码
|
||||
global $_swoole_atomic;
|
||||
// Windows 没有 Swoole,所以就使用了一个假的原子计数器,反正 Windows 只有单进程
|
||||
$_swoole_atomic = class_exists('\\Swoole\\Atomic') ? new Atomic() : new MockAtomic();
|
||||
|
||||
// 注册 Logger 等级
|
||||
ob_logger_register(new ConsoleLogger(ZM_TEST_LOG_DEBUG ? 'debug' : 'error'));
|
||||
|
||||
// 使用框架自身的 EventProvider
|
||||
global $ob_event_provider;
|
||||
$ob_event_provider = new EventProvider();
|
||||
|
||||
// 注册一个最低级别的 WorkerStart 事件,用于在框架的事件初始化之后开始运行 PHPUnit
|
||||
ob_event_provider()->addEventListener(WorkerStartEvent::getName(), function () {
|
||||
register_shutdown_function(function () {
|
||||
$error = error_get_last();
|
||||
// 下面这段代码的作用就是,不是错误引发的退出时照常退出即可
|
||||
if (($error['type'] ?? 0) != 0) {
|
||||
logger()->emergency(zm_internal_errcode('E00027') . 'Internal fatal error: ' . $error['message'] . ' at ' . $error['file'] . "({$error['line']})");
|
||||
}
|
||||
Framework::getInstance()->stop();
|
||||
});
|
||||
$_SERVER['argv'][] = '--printer';
|
||||
$_SERVER['argv'][] = ZMResultPrinter::class;
|
||||
try {
|
||||
// 不退出,而是返回 code
|
||||
$retcode = Command::main(false);
|
||||
} finally {
|
||||
global $_swoole_atomic;
|
||||
$_swoole_atomic->set($retcode ?? 0);
|
||||
Framework::getInstance()->stop($retcode ?? 0);
|
||||
}
|
||||
}, 1);
|
||||
|
||||
try {
|
||||
$options = ServerStartCommand::exportOptionArray();
|
||||
$options['driver'] = 'workerman';
|
||||
$options['worker-num'] = 1;
|
||||
$options['private-mode'] = true;
|
||||
(new Framework($options))->init()->start();
|
||||
exit($_swoole_atomic->get());
|
||||
} catch (Throwable $e) {
|
||||
echo $e->getMessage() . PHP_EOL;
|
||||
exit(1);
|
||||
}
|
||||
3
bin/phpunit-zm.bat
Normal file
@@ -0,0 +1,3 @@
|
||||
@echo off
|
||||
|
||||
php %~dp0phpunit-zm %*
|
||||
76
bin/prepare-doxygen
Executable file
@@ -0,0 +1,76 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
|
||||
function find_valid_root(string $current_dir, int $max_loop): string
|
||||
{
|
||||
if ($max_loop <= 0) {
|
||||
return '';
|
||||
}
|
||||
if (!file_exists($current_dir . '/composer.json')) {
|
||||
return find_valid_root(dirname($current_dir), $max_loop - 1);
|
||||
}
|
||||
return $current_dir;
|
||||
}
|
||||
|
||||
$root = find_valid_root(getcwd(), 3);
|
||||
if (empty($root)) {
|
||||
echo '找不到有效的根目录';
|
||||
exit(1);
|
||||
}
|
||||
|
||||
chdir($root);
|
||||
|
||||
require_once $root . '/vendor/autoload.php';
|
||||
|
||||
// 获取参数,可为 before 或 after
|
||||
$param = $argv[1] ?? 'before';
|
||||
|
||||
if ($param === 'before') {
|
||||
// Doxyfile file source: https://pastebin.com/raw/N8tJ9kWE
|
||||
// Posted by: sunxyw
|
||||
|
||||
// 从剪贴板获取 Doxyfile 内容并保存到临时文件
|
||||
logger()->info('正在获取 Doxyfile 内容');
|
||||
$doxyfile = file_get_contents('https://pastebin.com/raw/N8tJ9kWE');
|
||||
$doxyfile = str_replace('<zm_version>', \ZM\Framework::VERSION, $doxyfile);
|
||||
file_put_contents('Doxyfile', $doxyfile);
|
||||
|
||||
// 应用 Awesome 样式
|
||||
// 来源:https://github.com/jothepro/doxygen-awesome-css.git
|
||||
// 优先使用本地文件
|
||||
logger()->info('正在应用 Awesome 样式');
|
||||
\ZM\Store\FileSystem::createDir('doxy/css');
|
||||
if (
|
||||
file_exists('doxy/css/doxygen-awesome.css') &&
|
||||
(md5_file('doxy/css/doxygen-awesome.css') === '326a1447f9693d1b3876f59de837a7c0')
|
||||
) {
|
||||
logger()->info('本地 Awesome 样式文件已存在,跳过下载');
|
||||
} else {
|
||||
logger()->info('正在下载 Awesome 样式文件');
|
||||
$cmd = [
|
||||
'git clone https://github.com/jothepro/doxygen-awesome-css.git doxy/css',
|
||||
'cd doxy/css',
|
||||
'git checkout v2.1.0',
|
||||
'cd ../..',
|
||||
];
|
||||
$cmd = implode(' && ', $cmd);
|
||||
exec($cmd);
|
||||
}
|
||||
} elseif ($param === 'after') {
|
||||
// 删除临时文件
|
||||
unlink('Doxyfile');
|
||||
|
||||
// 清除旧文档
|
||||
exec('rm -rf docs/.vuepress/public/doxy');
|
||||
|
||||
// 授予目录权限:doxy/html
|
||||
exec('sudo chmod -R 644 doxy/html');
|
||||
|
||||
// 移动新文档到 docs 目录
|
||||
exec('mv doxy/html docs/.vuepress/public/doxy');
|
||||
|
||||
logger()->info('文档生成完成');
|
||||
} else {
|
||||
logger()->error('参数错误');
|
||||
exit(1);
|
||||
}
|
||||
28
bin/start
@@ -1,28 +0,0 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
|
||||
use ZM\ConsoleApplication;
|
||||
|
||||
// 这行是用于开发者自己电脑的调试功能
|
||||
|
||||
$symbol = sha1(is_file("/flag2") ? file_get_contents("/flag2") : '1') == '6252c0ec7fcbd544c3d6f5f0a162f60407d7a896' || mb_strpos(getcwd(), "/private/tmp");
|
||||
|
||||
// 首先得判断是直接从library模式启动的框架还是从composer引入library启动的框架
|
||||
// 判断方法:判断当前目录上面有没有 /vendor 目录,如果没有 /vendor 目录说明是从 composer 引入的
|
||||
// 否则就是直接从 framework 项目启动的
|
||||
if (!is_dir(__DIR__ . '/../vendor') || $symbol) {
|
||||
define("LOAD_MODE", 1); //composer项目模式
|
||||
define("LOAD_MODE_COMPOSER_PATH", getcwd());
|
||||
/** @noinspection PhpIncludeInspection */
|
||||
require_once LOAD_MODE_COMPOSER_PATH . "/vendor/autoload.php";
|
||||
} elseif (substr(__DIR__, 0, 7) == 'phar://') {
|
||||
define("LOAD_MODE", 2); //phar模式
|
||||
// 会废弃phar启动的方式,在2.0
|
||||
} else {
|
||||
define("LOAD_MODE", 0);
|
||||
require_once __DIR__ . "/../vendor/autoload.php";
|
||||
}
|
||||
// 终端的命令行功能启动!!
|
||||
$application = new ConsoleApplication("zhamao-framework");
|
||||
$application->initEnv();
|
||||
$application->run();
|
||||
31
bin/systemd
@@ -1,31 +0,0 @@
|
||||
#!/usr/bin/env php
|
||||
<?php /** @since 1.2 */
|
||||
switch ($argv[1] ?? '') {
|
||||
case '--generate':
|
||||
case '':
|
||||
generate($argv);
|
||||
break;
|
||||
case '--help':
|
||||
case '-h':
|
||||
default:
|
||||
echo "\nUsage: " . $argv[0] . " [OPTION]\n";
|
||||
echo "\nzhamao-framework systemd generator.";
|
||||
echo "\n\n -h, --help\t\tShow this help menu";
|
||||
echo "\n --generate\tGenerate a systemd service file\n\n";
|
||||
break;
|
||||
}
|
||||
|
||||
function generate($argv) {
|
||||
$s = "[Unit]\nDescription=zhamao-framework Daemon\nAfter=rc-local.service\n\n[Service]\nType=simple";
|
||||
$s .= "\nUser=" . exec("whoami");
|
||||
$s .= "\nGroup=" . exec("groups | awk '{print $1}'");
|
||||
$s .= "\nWorkingDirectory=" . getcwd();
|
||||
if ($argv[0] == "systemd" && !file_exists(getcwd() . '/systemd'))
|
||||
$s .= "\nExecStart=" . getcwd() . "/vendor/bin/start server --disable-console-input";
|
||||
else
|
||||
$s .= "\nExecStart=" . getcwd() . "/bin/start server --disable-console-input";
|
||||
$s .= "\nRestart=always\n\n[Install]\nWantedBy=multi-user.target\n";
|
||||
@mkdir(getcwd() . "/resources/");
|
||||
file_put_contents(getcwd() . "/resources/zhamao.service", $s);
|
||||
echo "File successfully generated. Path: " . getcwd() . "/resources/zhamao.service\n";
|
||||
}
|
||||
118
bin/watcher
Executable file
@@ -0,0 +1,118 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
_red="\e[33m"
|
||||
_green="\e[32m"
|
||||
_reset="\e[0m"
|
||||
|
||||
unix_s=$(uname -s)
|
||||
|
||||
unix_release=$(
|
||||
marked_release=""
|
||||
if [ "$unix_s" = "Linux" ]; then
|
||||
echo $HOME | grep com.termux >/dev/null
|
||||
if [ $? == 0 ]; then
|
||||
marked_release="termux"
|
||||
elif [ -f "/etc/redhat-release" ]; then
|
||||
if [ "$(cat /etc/redhat-release | awk '{print $1}' | grep -v '^$')" = "CentOS" ]; then
|
||||
marked_release="CentOS"
|
||||
else
|
||||
marked_release="unknown"
|
||||
fi
|
||||
elif [ -f "/etc/os-release" ]; then
|
||||
cat /etc/os-release | grep Alpine > /dev/null
|
||||
if [ $? == 0 ]; then
|
||||
marked_release="Alpine"
|
||||
fi
|
||||
fi
|
||||
if [ "$marked_release" = "" ]; then
|
||||
if [ -f "/etc/issue" ]; then
|
||||
marked_release=$(cat /etc/issue | grep -v '^$' | awk '{print $1}')
|
||||
else
|
||||
marked_release="unknown"
|
||||
fi
|
||||
fi
|
||||
elif [ "$unix_s" = "Darwin" ]; then
|
||||
marked_release=$(sw_vers | grep ProductName | awk '{print $2" "$3" "$4}')
|
||||
fi
|
||||
echo $marked_release
|
||||
)
|
||||
unix_release=$(echo $unix_release | xargs)
|
||||
|
||||
function echo_error() {
|
||||
echo -e "${_red}$1${_reset}"
|
||||
}
|
||||
|
||||
function echo_info() {
|
||||
echo -e "${_green}$1${_reset}"
|
||||
}
|
||||
|
||||
function install_test() {
|
||||
which fswatch >/dev/null
|
||||
if [ $? != 0 ]; then
|
||||
operate_confirm "fswatch还没有安装,是否确认安装?" && install_fswatch
|
||||
fi
|
||||
}
|
||||
function install_fswatch() {
|
||||
if [ "$unix_s" = "Linux" ]; then
|
||||
case $unix_release in
|
||||
"Kali" | "Ubuntu" | "Debian" | "Raspbian" | 'Pop!_OS')
|
||||
sudo apt-get install fswatch -y ;;
|
||||
#"termux") pkg install $1 -y ;;
|
||||
"CentOS")
|
||||
curl -o fswatch.tgz https://download.fastgit.org/emcrisostomo/fswatch/releases/download/1.16.0/fswatch-1.16.0.tar.gz && \
|
||||
tar -xzf fswatch.tgz && \
|
||||
cd fswatch-1.16.0 && \
|
||||
./configure && \
|
||||
make && \
|
||||
sudo make install && \
|
||||
cd .. && \
|
||||
rm -rf fswatch.tgz fswatch-1.16.0
|
||||
;;
|
||||
#"Alpine") apk add $1 ;;
|
||||
*)
|
||||
echo_error "不支持的 Linux 发行版:$unix_release"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
elif [ "$unix_s" = "Darwin" ]; then
|
||||
brew install fswatch
|
||||
else
|
||||
echo_error "不支持的操作系统:$unix_s"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
function operate_confirm() {
|
||||
echo -n $(echo_info "$1 [Y/n] ")
|
||||
read operate
|
||||
operate=$(echo $operate | tr A-Z a-z)
|
||||
if [[ "$operate" = "y" || "$operate" = "" ]]; then
|
||||
return 0
|
||||
else
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
echo_info "当前系统:$unix_release"
|
||||
install_test
|
||||
if [ $? -ne 0 ]; then
|
||||
exit 1
|
||||
else
|
||||
echo_info "程序路径:$(which fswatch)"
|
||||
fi
|
||||
watch_dir="./src"
|
||||
if [ ! -d "$watch_dir" ]; then
|
||||
echo_error "src目录不存在!"
|
||||
exit 1
|
||||
else
|
||||
echo_info "监听目录:$watch_dir"
|
||||
fi
|
||||
_pid=$(cat .daemon_pid | awk -F"\"pid\": " '{print $2}' | grep -v ^$ | sed 's/,//g')
|
||||
if [ "$_pid" = "" ]; then
|
||||
echo_error "未检测到框架进程"
|
||||
exit 1
|
||||
fi
|
||||
fswatch $watch_dir | while read file
|
||||
do
|
||||
echo "Detect file change: $file"
|
||||
kill -USR1 $_pid
|
||||
done
|
||||
31
bin/zhamao
Executable file
@@ -0,0 +1,31 @@
|
||||
#!/bin/sh
|
||||
|
||||
# shellcheck disable=SC2068
|
||||
# shellcheck disable=SC2181
|
||||
# author: crazywhalecc
|
||||
# since: 2.5.0
|
||||
|
||||
if [ "$ZM_CUSTOM_PHP_PATH" != "" ]; then
|
||||
echo "* Using PHP executable: ""$ZM_CUSTOM_PHP_PATH"
|
||||
executable="$ZM_CUSTOM_PHP_PATH"
|
||||
elif [ -f "$(pwd)/runtime/php" ]; then
|
||||
executable="$(pwd)/runtime/php"
|
||||
echo "* Framework started with built-in php."
|
||||
else
|
||||
which php >/dev/null 2>&1
|
||||
if [ $? -eq 0 ]; then
|
||||
executable=$(which php)
|
||||
else
|
||||
echo '[ErrCode:E00014] Cannot find any PHP runtime, please use command "./install-runtime.sh" or install PHP manually!'
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -f "$(pwd)/src/entry.php" ]; then
|
||||
$executable "$(pwd)/src/entry.php" $@
|
||||
elif [ -f "$(pwd)/vendor/zhamao/framework/src/entry.php" ]; then
|
||||
$executable "$(pwd)/vendor/zhamao/framework/src/entry.php" $@
|
||||
else
|
||||
echo "[ErrCode:E00015] Cannot find zhamao-framework entry file!"
|
||||
exit 1
|
||||
fi
|
||||
27
bin/zhamao.bat
Normal file
@@ -0,0 +1,27 @@
|
||||
@echo off
|
||||
@REM Check if ZM_CUSTOM_PHP_PATH is set
|
||||
IF /i "%ZM_CUSTOM_PHP_PATH%" neq "" (
|
||||
@REM Set the path to the custom PHP
|
||||
echo "* Using custom PHP executable: %ZM_CUSTOM_PHP_PATH%"
|
||||
SET executable=%ZM_CUSTOM_PHP_PATH%
|
||||
) ELSE IF exist ./runtime/php.exe (
|
||||
@REM Set the path to the built-in PHP
|
||||
echo "* Using built-in PHP executable"
|
||||
SET executable=.\runtime\php.exe
|
||||
) ELSE (
|
||||
@REM Set the path to the system PHP
|
||||
echo "* Using system PHP executable"
|
||||
SET executable=php
|
||||
)
|
||||
|
||||
IF exist src/entry.php (
|
||||
@REM Run the PHP entry point
|
||||
%executable% src/entry.php %*
|
||||
) ELSE IF exist vendor/zhamao/framework/src/entry.php (
|
||||
@REM Run the PHP entry point
|
||||
%executable% vendor/zhamao/framework/src/entry.php %*
|
||||
) ELSE (
|
||||
@REM No entry point found
|
||||
echo "[ErrCode:E00015] Cannot find zhamao-framework entry file!"
|
||||
exit /b 1
|
||||
)
|
||||
47
captainhook.json
Normal file
@@ -0,0 +1,47 @@
|
||||
{
|
||||
"pre-push": {
|
||||
"enabled": true,
|
||||
"actions": [
|
||||
{
|
||||
"action": "composer analyse"
|
||||
},
|
||||
{
|
||||
"action": "composer test"
|
||||
}
|
||||
]
|
||||
},
|
||||
"pre-commit": {
|
||||
"enabled": true,
|
||||
"actions": [
|
||||
{
|
||||
"action": "composer cs-fix -- --config=.php-cs-fixer.php --dry-run --diff {$STAGED_FILES|of-type:php}",
|
||||
"conditions": [
|
||||
{
|
||||
"exec": "\\CaptainHook\\App\\Hook\\Condition\\FileStaged\\OfType",
|
||||
"args": ["php"]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"post-change": {
|
||||
"enabled": true,
|
||||
"actions": [
|
||||
{
|
||||
"action": "composer install",
|
||||
"options": [],
|
||||
"conditions": [
|
||||
{
|
||||
"exec": "\\CaptainHook\\App\\Hook\\Condition\\FileChanged\\Any",
|
||||
"args": [
|
||||
[
|
||||
"composer.json",
|
||||
"composer.lock"
|
||||
]
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
116
composer.json
@@ -1,57 +1,103 @@
|
||||
{
|
||||
"name": "zhamao/framework",
|
||||
"description": "High performance QQ robot and web server development framework",
|
||||
"minimum-stability": "stable",
|
||||
"description": "High performance chat robot and web server development framework",
|
||||
"license": "Apache-2.0",
|
||||
"version": "2.1.2",
|
||||
"extra": {
|
||||
"exclude_annotate": [
|
||||
"src/ZM"
|
||||
]
|
||||
},
|
||||
"authors": [
|
||||
{
|
||||
"name": "whale",
|
||||
"email": "crazysnowcc@gmail.com"
|
||||
"name": "jerry",
|
||||
"email": "admin@zhamao.me"
|
||||
},
|
||||
{
|
||||
"name": "swift",
|
||||
"email": "hugo_swift@yahoo.com"
|
||||
"name": "sunxyw",
|
||||
"email": "dev@sunxyw.xyz"
|
||||
}
|
||||
],
|
||||
"prefer-stable": true,
|
||||
"bin": [
|
||||
"bin/start",
|
||||
"bin/phpunit-swoole"
|
||||
],
|
||||
"require": {
|
||||
"php": ">=7.2",
|
||||
"doctrine/annotations": "~1.10",
|
||||
"php": "^8.0 || ^8.1 || ^8.2",
|
||||
"ext-json": "*",
|
||||
"psy/psysh": "@stable",
|
||||
"symfony/polyfill-ctype": "^1.20",
|
||||
"symfony/polyfill-mbstring": "^1.20",
|
||||
"symfony/console": "^5.1",
|
||||
"zhamao/connection-manager": "*@dev",
|
||||
"zhamao/console": "^1.0",
|
||||
"zhamao/config": "^1.0",
|
||||
"zhamao/request": "*@dev",
|
||||
"symfony/routing": "^5.1",
|
||||
"symfony/polyfill-php80": "^1.20"
|
||||
"ext-tokenizer": "*",
|
||||
"doctrine/dbal": "^2.13.1",
|
||||
"dragonmantank/cron-expression": "^3.3",
|
||||
"jelix/version": "^2.0",
|
||||
"koriym/attributes": "^1.0",
|
||||
"nunomaduro/collision": "^6.3",
|
||||
"onebot/libonebot": "^0.5",
|
||||
"php-di/php-di": "^7",
|
||||
"psr/container": "^2.0",
|
||||
"psr/simple-cache": "^3.0",
|
||||
"psy/psysh": "^0.11.8",
|
||||
"symfony/console": "^6.0",
|
||||
"symfony/polyfill-ctype": "^1.19",
|
||||
"symfony/polyfill-mbstring": "^1.19",
|
||||
"symfony/routing": "~6.0 || ~5.0 || ~4.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"captainhook/captainhook": "^5.12",
|
||||
"friendsofphp/php-cs-fixer": "^3.2 != 3.7.0",
|
||||
"jangregor/phpstan-prophecy": "^1.0",
|
||||
"jetbrains/phpstorm-attributes": "^1.0",
|
||||
"mikey179/vfsstream": "^1.6",
|
||||
"phpspec/prophecy-phpunit": "^2.0",
|
||||
"phpstan/extension-installer": "^1.1",
|
||||
"phpstan/phpstan": "^1.1",
|
||||
"phpstan/phpstan-deprecation-rules": "^1.0",
|
||||
"phpstan/phpstan-phpunit": "^1.1",
|
||||
"phpunit/phpunit": "^8.5 || ^9.0",
|
||||
"roave/security-advisories": "dev-latest",
|
||||
"swoole/ide-helper": "^4.5"
|
||||
},
|
||||
"replace": {
|
||||
"symfony/polyfill-php80": "*"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-ctype": "*",
|
||||
"ext-mbstring": "*"
|
||||
"ext-ctype": "Use C/C++ extension instead of polyfill will be more efficient",
|
||||
"ext-mbstring": "Use C/C++ extension instead of polyfill will be more efficient",
|
||||
"ext-pdo_mysql": "If you use mysql in framework, you will need this extension",
|
||||
"ext-redis": "If you use Redis in framework, you will need this extension",
|
||||
"league/climate": "Display columns and status in terminal"
|
||||
},
|
||||
"minimum-stability": "dev",
|
||||
"prefer-stable": true,
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"ZM\\": "src/ZM"
|
||||
},
|
||||
"files": [
|
||||
"src/ZM/global_functions.php"
|
||||
"src/Globals/global_functions.php",
|
||||
"src/Globals/global_defines_app.php",
|
||||
"src/Globals/global_class_alias.php"
|
||||
]
|
||||
},
|
||||
"require-dev": {
|
||||
"swoole/ide-helper": "@dev"
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"Module\\": "src/Module",
|
||||
"Tests\\": "tests"
|
||||
}
|
||||
},
|
||||
"bin": [
|
||||
"bin/phpunit-zm",
|
||||
"bin/zhamao",
|
||||
"bin/zhamao.bat"
|
||||
],
|
||||
"config": {
|
||||
"allow-plugins": {
|
||||
"phpstan/extension-installer": true
|
||||
},
|
||||
"optimize-autoloader": true,
|
||||
"sort-packages": true
|
||||
},
|
||||
"extra": {
|
||||
"zm": {
|
||||
"exclude-annotation-path": [
|
||||
"src/ZM",
|
||||
"tests"
|
||||
]
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"post-autoload-dump": "vendor/bin/captainhook install -f -s",
|
||||
"analyse": "phpstan analyse --memory-limit 300M",
|
||||
"cs-fix": "PHP_CS_FIXER_FUTURE_MODE=1 php-cs-fixer fix",
|
||||
"test": "bin/phpunit-zm --no-coverage"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
28
config/config.php
Normal file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* Config 配置类的配置文件
|
||||
* 由于 Config 类是第一批被加载的类,因此本文件存在以下限制:
|
||||
* 1. 只能使用 PHP 格式
|
||||
* 2. 无法利用容器及依赖注入
|
||||
* 3. 必须存在于本地,无法使用远程配置(后续版本可能会支持)
|
||||
*/
|
||||
return [
|
||||
'repository' => [
|
||||
\OneBot\Config\Repository::class, // 配置仓库,须实现 \OneBot\Config\RepositoryInterface 接口
|
||||
[], // 传入的参数,依序传入构造函数
|
||||
],
|
||||
'loader' => [
|
||||
\OneBot\Config\Loader\DelegateLoader::class, // 配置加载器,须实现 \OneBot\Config\LoaderInterface 接口
|
||||
[], // 传入的参数,依序传入构造函数
|
||||
],
|
||||
'source' => [
|
||||
'extensions' => ['php', 'yaml', 'yml', 'json', 'toml'], // 配置文件扩展名
|
||||
'paths' => [
|
||||
SOURCE_ROOT_DIR . '/config', // 配置文件所在目录
|
||||
// 可以添加多个配置文件目录
|
||||
],
|
||||
],
|
||||
];
|
||||
28
config/container.php
Normal file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use OneBot\Driver\Driver;
|
||||
use OneBot\Driver\Process\ProcessManager;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use ZM\Framework;
|
||||
|
||||
/*
|
||||
* 这里是容器的配置文件,你可以在这里配置容器的绑定和其他一些参数。
|
||||
* 选用的容器是 PHP-DI,你可以在这里查看文档:https://php-di.org/doc/
|
||||
* 我们建议你在使用容器前先阅读以下章节:
|
||||
* - 基本使用方式:https://php-di.org/doc/container.html
|
||||
* - 注册绑定方式:https://php-di.org/doc/php-definitions.html#definition-types
|
||||
* - 最佳使用实践:https://php-di.org/doc/best-practices.html
|
||||
* 同时,你也可以选择查阅我们(炸毛框架)的相关文档,其会包含一些简单的使用方法和示例。
|
||||
* 关于框架在不同事件(或周期中)默认注册的绑定,请参阅我们的文档,或直接查看 {@see \ZM\Container\ContainerRegistrant}
|
||||
*/
|
||||
return [
|
||||
// 这里定义的是全局容器的绑定,不建议在此处直接调用框架、应用内部的类或方法,因为这些类可能还没有被加载或初始化
|
||||
// 你可以使用匿名函数来延迟加载
|
||||
'definitions' => [
|
||||
'worker_id' => fn () => ProcessManager::getProcessId(),
|
||||
Driver::class => fn () => Framework::getInstance()->getDriver(),
|
||||
LoggerInterface::class => fn () => logger(),
|
||||
],
|
||||
];
|
||||
@@ -1,117 +1,125 @@
|
||||
<?php
|
||||
/** @noinspection PhpFullyQualifiedNameUsageInspection */
|
||||
/** @noinspection PhpComposerExtensionStubsInspection */
|
||||
global $config;
|
||||
|
||||
/** bind host */
|
||||
$config['host'] = '0.0.0.0';
|
||||
declare(strict_types=1);
|
||||
|
||||
/** bind port */
|
||||
$config['port'] = 20001;
|
||||
/* 启动框架的底层驱动(原生支持 swoole、workerman 两种) */
|
||||
$config['driver'] = 'workerman';
|
||||
|
||||
/** 框架开到公网或外部的HTTP访问链接,通过 DataProvider::getFrameworkLink() 获取 */
|
||||
$config['http_reverse_link'] = "http://127.0.0.1:" . $config['port'];
|
||||
|
||||
/** 框架是否启动debug模式 */
|
||||
$config['debug_mode'] = false;
|
||||
|
||||
/** 存放框架内文件数据的目录 */
|
||||
$config['zm_data'] = realpath(__DIR__ . "/../") . '/zm_data/';
|
||||
|
||||
/** 存放各个模块配置文件的目录 */
|
||||
$config['config_dir'] = $config['zm_data'] . 'config/';
|
||||
|
||||
/** 存放崩溃和运行日志的目录 */
|
||||
$config['crash_dir'] = $config['zm_data'] . 'crash/';
|
||||
|
||||
/** 对应swoole的server->set参数 */
|
||||
$config['swoole'] = [
|
||||
'log_file' => $config['crash_dir'] . 'swoole_error.log',
|
||||
'worker_num' => swoole_cpu_num(), //如果你只有一个 OneBot 实例连接到框架并且代码没有复杂的CPU密集计算,则可把这里改为1使用全局变量
|
||||
'dispatch_mode' => 2, //包分配原则,见 https://wiki.swoole.com/#/server/setting?id=dispatch_mode
|
||||
'max_coroutine' => 300000,
|
||||
//'task_worker_num' => 4,
|
||||
//'task_enable_coroutine' => true
|
||||
];
|
||||
|
||||
/** 轻量字符串缓存,默认开启 */
|
||||
$config['light_cache'] = [
|
||||
'size' => 1024, //最多允许储存的条数(需要2的倍数)
|
||||
'max_strlen' => 16384, //单行字符串最大长度(需要2的倍数)
|
||||
'hash_conflict_proportion' => 0.6, //Hash冲突率(越大越好,但是需要的内存更多)
|
||||
'persistence_path' => $config['zm_data'].'_cache.json',
|
||||
'auto_save_interval' => 900
|
||||
];
|
||||
|
||||
/** MySQL数据库连接信息,host留空则启动时不创建sql连接池 */
|
||||
$config['sql_config'] = [
|
||||
'sql_host' => '',
|
||||
'sql_port' => 3306,
|
||||
'sql_username' => 'name',
|
||||
'sql_database' => 'db_name',
|
||||
'sql_password' => '',
|
||||
'sql_options' => [
|
||||
PDO::ATTR_STRINGIFY_FETCHES => false,
|
||||
PDO::ATTR_EMULATE_PREPARES => false
|
||||
/* 要启动的服务器监听端口及协议 */
|
||||
$config['servers'] = [
|
||||
[
|
||||
'host' => '0.0.0.0',
|
||||
'port' => 20001,
|
||||
'type' => 'websocket',
|
||||
],
|
||||
[
|
||||
'host' => '0.0.0.0',
|
||||
'port' => 20002,
|
||||
'type' => 'http',
|
||||
'flag' => 20002,
|
||||
],
|
||||
'sql_no_exception' => false,
|
||||
'sql_default_fetch_mode' => PDO::FETCH_ASSOC // added in 1.5.6
|
||||
];
|
||||
|
||||
/** Redis连接信息,host留空则启动时不创建Redis连接池 */
|
||||
$config['redis_config'] = [
|
||||
'host' => '',
|
||||
'port' => 6379,
|
||||
'timeout' => 1,
|
||||
'db_index' => 0,
|
||||
'auth' => ''
|
||||
/* Workerman 驱动相关配置 */
|
||||
$config['workerman_options'] = [
|
||||
'worker_num' => 1, // 如果你只有一个 OneBot 实例连接到框架并且代码没有复杂的CPU密集计算,则可把这里改为1使用全局变量
|
||||
];
|
||||
|
||||
/** onebot连接约定的token */
|
||||
$config["access_token"] = '';
|
||||
|
||||
/** HTTP服务器固定请求头的返回 */
|
||||
$config['http_header'] = [
|
||||
'X-Powered-By' => 'zhamao-framework',
|
||||
'Content-Type' => 'text/html; charset=utf-8'
|
||||
/* Swoole 驱动相关配置 */
|
||||
$config['swoole_options'] = [
|
||||
'coroutine_hook_flags' => SWOOLE_HOOK_ALL & (~SWOOLE_HOOK_CURL), // 协程 Hook 内容
|
||||
'swoole_set' => [
|
||||
'worker_num' => 1, // 如果你只有一个 OneBot 实例连接到框架并且代码没有复杂的CPU密集计算,则可把这里改为1使用全局变量
|
||||
'dispatch_mode' => 2, // 包分配原则,见 https://wiki.swoole.com/#/server/setting?id=dispatch_mode
|
||||
'max_coroutine' => 300000, // 允许最大的协程数
|
||||
'max_wait_time' => 5, // 安全退出模式下允许等待 Worker 的最长秒数
|
||||
// 'task_worker_num' => 4, // 启动 TaskWorker 进程的数量(默认不启动)
|
||||
// 'task_enable_coroutine' => true // TaskWorker 是否开启协程
|
||||
],
|
||||
'swoole_server_mode' => SWOOLE_PROCESS, // Swoole Server 启动模式,默认为 SWOOLE_PROCESS
|
||||
];
|
||||
|
||||
/** HTTP服务器在指定状态码下回复的页面(默认) */
|
||||
$config['http_default_code_page'] = [
|
||||
'404' => '404.html'
|
||||
/* 默认存取炸毛数据的目录(相对目录时,代表WORKING_DIR下的目录,绝对目录按照绝对目录来) */
|
||||
$config['data_dir'] = WORKING_DIR . '/zm_data';
|
||||
|
||||
/* 框架本体运行时的一些可调配置 */
|
||||
$config['runtime'] = [
|
||||
'reload_delay_time' => 800,
|
||||
'annotation_reader_ignore' => [ // 设置注解解析器忽略的注解名或命名空间,防止解析到不该解析的
|
||||
'name' => [
|
||||
'mixin',
|
||||
],
|
||||
'namespace' => [],
|
||||
],
|
||||
'timezone' => 'Asia/Shanghai',
|
||||
];
|
||||
|
||||
/** zhamao-framework在框架启动时初始化的atomic们 */
|
||||
$config['init_atomics'] = [
|
||||
//'custom_atomic_name' => 0, //自定义添加的Atomic
|
||||
];
|
||||
|
||||
/** 终端日志显示等级(0-4) */
|
||||
$config["info_level"] = 2;
|
||||
|
||||
/** 上下文接口类 implemented from ContextInterface */
|
||||
/* 上下文接口类 implemented from ContextInterface */
|
||||
$config['context_class'] = \ZM\Context\Context::class;
|
||||
|
||||
/** 静态文件访问 */
|
||||
$config['static_file_server'] = [
|
||||
'status' => false,
|
||||
'document_root' => realpath(__DIR__ . "/../") . '/resources/html',
|
||||
'document_index' => [
|
||||
'index.html'
|
||||
]
|
||||
/* 允许加载插件形式 */
|
||||
$config['plugin'] = [
|
||||
'enable' => true,
|
||||
'load_dir' => 'plugins',
|
||||
];
|
||||
|
||||
/** 注册 Swoole Server 事件注解的类列表 */
|
||||
$config['server_event_handler_class'] = [
|
||||
\ZM\Event\ServerEventHandler::class,
|
||||
/* 内部默认启用的插件 */
|
||||
$config['native_plugin'] = [
|
||||
'onebot12' => true, // OneBot v12 协议支持
|
||||
'onebot12-ban-other-ws' => true, // OneBot v12 协议支持,禁止其他 WebSocket 连接
|
||||
'command-manual' => true,
|
||||
];
|
||||
|
||||
/** 服务器启用的外部第三方和内部插件 */
|
||||
$config['modules'] = [
|
||||
'onebot' => [
|
||||
'status' => true,
|
||||
'single_bot_mode' => false
|
||||
], // QQ机器人事件解析器,如果取消此项则默认为 true 开启状态,否则你手动填写 false 才会关闭
|
||||
/* 静态文件读取器 */
|
||||
$config['file_server'] = [
|
||||
'enable' => true,
|
||||
'document_root' => $config['data_dir'] . '/public/',
|
||||
'document_index' => 'index.html',
|
||||
'document_code_page' => [
|
||||
'404' => '404.html',
|
||||
'500' => '500.html',
|
||||
],
|
||||
];
|
||||
|
||||
/* MySQL 和 SQLite3 数据库连接配置,框架将自动生成连接池,支持多个连接池 */
|
||||
$config['database'] = [
|
||||
'sqlite_db1' => [
|
||||
'enable' => false,
|
||||
'type' => 'sqlite',
|
||||
'dbname' => 'a.db',
|
||||
'pool_size' => 10,
|
||||
],
|
||||
'default' => [
|
||||
'enable' => false,
|
||||
'type' => 'mysql',
|
||||
'host' => '127.0.0.1', // 填写数据库服务器地址后才会创建数据库连接
|
||||
'port' => 3306,
|
||||
'username' => 'root',
|
||||
'password' => 'ZhamaoTEST',
|
||||
'dbname' => 'zm',
|
||||
'charset' => 'utf8mb4',
|
||||
'pool_size' => 64,
|
||||
],
|
||||
];
|
||||
|
||||
/* Redis 连接配置,框架将自动生成连接池,支持多个连接池 */
|
||||
$config['redis'] = [
|
||||
'default' => [
|
||||
'enable' => false,
|
||||
'host' => '127.0.0.1',
|
||||
'port' => 6379,
|
||||
'index' => 0,
|
||||
'auth' => '',
|
||||
'pool_size' => 10,
|
||||
],
|
||||
];
|
||||
|
||||
/* KV 数据库的配置 */
|
||||
$config['kv'] = [
|
||||
'use' => \LightCache::class, // 默认在单进程模式下使用 LightCache,多进程需要使用 ZMRedis
|
||||
'light_cache_dir' => $config['data_dir'] . '/lc', // 默认的 LightCache 保存持久化数据的位置
|
||||
'light_cache_autosave_time' => 600, // LightCache 自动保存时间(秒)
|
||||
'redis_config' => 'default',
|
||||
];
|
||||
|
||||
return $config;
|
||||
|
||||
152
config/global_old.php
Normal file
@@ -0,0 +1,152 @@
|
||||
<?php
|
||||
|
||||
/** @noinspection PhpComposerExtensionStubsInspection */
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/* bind host */
|
||||
$config['host'] = '0.0.0.0';
|
||||
|
||||
/* bind port */
|
||||
$config['port'] = 20001;
|
||||
|
||||
/* 框架开到公网或外部的HTTP访问链接,通过 DataProvider::getFrameworkLink() 获取 */
|
||||
$config['http_reverse_link'] = 'http://127.0.0.1:' . $config['port'];
|
||||
|
||||
/* 框架是否启动debug模式,当debug模式为true时,启用热更新(需要安装inotify扩展) */
|
||||
$config['debug_mode'] = false;
|
||||
|
||||
/* 存放框架内文件数据的目录 */
|
||||
$config['zm_data'] = realpath(WORKING_DIR) . '/zm_data/';
|
||||
|
||||
/* 存放各个模块配置文件的目录 */
|
||||
$config['config_dir'] = $config['zm_data'] . 'config/';
|
||||
|
||||
/* 存放崩溃和运行日志的目录 */
|
||||
$config['crash_dir'] = $config['zm_data'] . 'crash/';
|
||||
|
||||
/* 对应swoole的server->set参数 */
|
||||
$config['swoole'] = [
|
||||
'log_file' => $config['crash_dir'] . 'swoole_error.log',
|
||||
// 'worker_num' => swoole_cpu_num(), //如果你只有一个 OneBot 实例连接到框架并且代码没有复杂的CPU密集计算,则可把这里改为1使用全局变量
|
||||
'dispatch_mode' => 2, // 包分配原则,见 https://wiki.swoole.com/#/server/setting?id=dispatch_mode
|
||||
'max_coroutine' => 300000,
|
||||
'max_wait_time' => 5,
|
||||
// 'task_worker_num' => 4,
|
||||
// 'task_enable_coroutine' => true
|
||||
];
|
||||
|
||||
/* 一些框架与框架运行时设置的调整 */
|
||||
$config['runtime'] = [
|
||||
'swoole_coroutine_hook_flags' => SWOOLE_HOOK_ALL & (~SWOOLE_HOOK_CURL),
|
||||
'swoole_server_mode' => SWOOLE_PROCESS,
|
||||
'middleware_error_policy' => 1,
|
||||
'reload_delay_time' => 800,
|
||||
'global_middleware_binding' => [],
|
||||
'save_console_log_file' => false, // 改为目标路径,则将 Console 输出的日志保存到文件
|
||||
'annotation_reader_ignore' => [ // 设置注解解析器忽略的注解名或命名空间,防止解析到不该解析的
|
||||
'name' => [
|
||||
'mixin',
|
||||
],
|
||||
'namespace' => [],
|
||||
],
|
||||
];
|
||||
|
||||
/* 轻量字符串缓存,默认开启 */
|
||||
$config['light_cache'] = [
|
||||
'size' => 512, // 最多允许储存的条数(需要2的倍数)
|
||||
'max_strlen' => 32768, // 单行字符串最大长度(需要2的倍数)
|
||||
'hash_conflict_proportion' => 0.6, // Hash冲突率(越大越好,但是需要的内存更多)
|
||||
'persistence_path' => $config['zm_data'] . '_cache.json',
|
||||
'auto_save_interval' => 900,
|
||||
];
|
||||
|
||||
/* 大容量跨进程变量存储(2.2.0可用) */
|
||||
$config['worker_cache'] = [
|
||||
'worker' => 0,
|
||||
'transaction_timeout' => 30000,
|
||||
];
|
||||
|
||||
/* MySQL数据库连接信息,host留空则启动时不创建sql连接池 */
|
||||
$config['mysql_config'] = [
|
||||
'host' => '',
|
||||
'port' => 3306,
|
||||
'unix_socket' => null,
|
||||
'username' => 'root',
|
||||
'password' => '123456',
|
||||
'dbname' => '',
|
||||
'charset' => 'utf8mb4',
|
||||
'pool_size' => 64,
|
||||
'options' => [
|
||||
PDO::ATTR_STRINGIFY_FETCHES => false,
|
||||
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
|
||||
],
|
||||
];
|
||||
|
||||
/* Redis连接信息,host留空则启动时不创建Redis连接池 */
|
||||
$config['redis_config'] = [
|
||||
'host' => '',
|
||||
'port' => 6379,
|
||||
'timeout' => 1,
|
||||
'db_index' => 0,
|
||||
'auth' => '',
|
||||
];
|
||||
|
||||
/* onebot连接约定的token */
|
||||
$config['access_token'] = '';
|
||||
|
||||
/* HTTP服务器固定请求头的返回 */
|
||||
$config['http_header'] = [
|
||||
'Server' => 'zhamao-framework',
|
||||
'Content-Type' => 'text/html; charset=utf-8',
|
||||
];
|
||||
|
||||
/* HTTP服务器在指定状态码下回复的页面(默认) */
|
||||
$config['http_default_code_page'] = [
|
||||
'404' => '404.html',
|
||||
];
|
||||
|
||||
/* zhamao-framework在框架启动时初始化的atomic们 */
|
||||
$config['init_atomics'] = [
|
||||
// 'custom_atomic_name' => 0, //自定义添加的Atomic
|
||||
];
|
||||
|
||||
/* 终端日志显示等级(0-4) */
|
||||
$config['info_level'] = 2;
|
||||
|
||||
/* 上下文接口类 implemented from ContextInterface */
|
||||
$config['context_class'] = \ZM\Context\Context::class;
|
||||
|
||||
/* 静态文件访问 */
|
||||
$config['static_file_server'] = [
|
||||
'status' => false,
|
||||
'document_root' => realpath(__DIR__ . '/../') . '/resources/html',
|
||||
'document_index' => [
|
||||
'index.html',
|
||||
],
|
||||
];
|
||||
|
||||
/* 机器人解析模块,关闭后无法使用如CQCommand等注解(上面的modules即将废弃) */
|
||||
$config['onebot'] = [
|
||||
'status' => true,
|
||||
'single_bot_mode' => false,
|
||||
'message_level' => 99,
|
||||
'message_convert_string' => true,
|
||||
'message_command_policy' => 'interrupt',
|
||||
];
|
||||
|
||||
/* 一个远程简易终端,使用nc直接连接即可,但是不建议开放host为0.0.0.0(远程连接) */
|
||||
$config['remote_terminal'] = [
|
||||
'status' => false,
|
||||
'host' => '127.0.0.1',
|
||||
'port' => 20002,
|
||||
'token' => '',
|
||||
];
|
||||
|
||||
/* 模块(插件)加载器的相关设置 */
|
||||
$config['module_loader'] = [
|
||||
'enable_hotload' => false,
|
||||
'load_path' => $config['zm_data'] . 'modules',
|
||||
];
|
||||
|
||||
return $config;
|
||||
@@ -1,6 +1,6 @@
|
||||
______
|
||||
|__ / |__ __ _ _ __ ___ __ _ ___
|
||||
/ /| '_ \ / _` | '_ ` _ \ / _` |/ _ \
|
||||
/ /_| | | | (_| | | | | | | (_| | (_) |
|
||||
/____|_| |_|\__,_|_| |_| |_|\__,_|\___/
|
||||
______
|
||||
|__ / |__ __ _ _ __ ___ __ _ ___
|
||||
/ /| '_ \ / _` | '_ ` _ \ / _` |/ _ \
|
||||
/ /_| | | | (_| | | | | | | (_| | (_) |
|
||||
/____|_| |_|\__,_|_| |_| |_|\__,_|\___/
|
||||
|
||||
|
||||
96
docker-compose.yml
Normal file
@@ -0,0 +1,96 @@
|
||||
version: "3.9"
|
||||
|
||||
services:
|
||||
bot:
|
||||
build:
|
||||
context: docker/zhamao
|
||||
dockerfile: ./Dockerfile
|
||||
container_name: zhamao-bot
|
||||
depends_on:
|
||||
- database-postgres
|
||||
- database-redis
|
||||
networks:
|
||||
- bot-net
|
||||
ports:
|
||||
- "20001-20005:20001-20005/tcp"
|
||||
volumes:
|
||||
- ".:/bot"
|
||||
|
||||
walle-q:
|
||||
build:
|
||||
context: docker/walle-q
|
||||
dockerfile: ./Dockerfile
|
||||
container_name: zhamao-bot-walle-q
|
||||
depends_on:
|
||||
- bot
|
||||
networks:
|
||||
- bot-net
|
||||
volumes:
|
||||
- "./docker/volumes/walle-q-data:/bot"
|
||||
|
||||
database-postgres:
|
||||
container_name: zhamao-bot-db-postgres
|
||||
env_file:
|
||||
- docker/environment.env
|
||||
image: postgres:14.4-alpine
|
||||
networks:
|
||||
- bot-net
|
||||
ports:
|
||||
- "5432:5432/tcp"
|
||||
restart: always
|
||||
volumes:
|
||||
- "./docker/volumes/postgres-data:/var/lib/postgresql/data"
|
||||
- "./docker/volumes/postgres-init:/docker-entrypoint-initdb.d"
|
||||
|
||||
database-redis:
|
||||
command:
|
||||
- redis-server
|
||||
- --requirepass
|
||||
- "$${REDIS_PASSWORD}"
|
||||
container_name: zhamao-bot-db-redis
|
||||
env_file:
|
||||
- docker/environment.env
|
||||
image: redis:7.0-alpine
|
||||
networks:
|
||||
- bot-net
|
||||
ports:
|
||||
- "6379:6379/tcp"
|
||||
restart: always
|
||||
volumes:
|
||||
- "./docker/volumes/redis-data:/data"
|
||||
|
||||
db-admin-adminer:
|
||||
container_name: zhamao-bot-dbadmin-adminer
|
||||
depends_on:
|
||||
database-postgres:
|
||||
condition: service_started
|
||||
database-redis:
|
||||
condition: service_started
|
||||
env_file:
|
||||
- docker/environment.env
|
||||
image: adminer:latest
|
||||
networks:
|
||||
- bot-net
|
||||
ports:
|
||||
- "15432:8080/tcp"
|
||||
profiles:
|
||||
- with-adminer
|
||||
|
||||
db-admin-redis-insight:
|
||||
container_name: zhamao-bot-dbadmin-redis-insight
|
||||
depends_on:
|
||||
database-postgres:
|
||||
condition: service_started
|
||||
database-redis:
|
||||
condition: service_started
|
||||
image: redislabs/redisinsight:latest
|
||||
networks:
|
||||
- bot-net
|
||||
ports:
|
||||
- "16379:8001/tcp"
|
||||
profiles:
|
||||
- with-adminer
|
||||
|
||||
networks:
|
||||
bot-net:
|
||||
name: bot-net
|
||||
2
docker/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
/volumes
|
||||
environment.env
|
||||
7
docker/environment.env.example
Normal file
@@ -0,0 +1,7 @@
|
||||
REDIS_PASSWORD={{$REDIS_PASSWORD}}
|
||||
|
||||
POSTGRES_USER={{$DB_ROOT_USER}}
|
||||
POSTGRES_PASSWORD={{$DB_ROOT_PASSWORD}}
|
||||
POSTGRES_APPLICATION_USER={{$DB_USER}}
|
||||
POSTGRES_APPLICATION_USER_PASSWORD={{$DB_PASSWORD}}
|
||||
POSTGRES_APPLICATION_DATABASE={{$DB_NAME}}
|
||||
14
docker/walle-q/Dockerfile
Normal file
@@ -0,0 +1,14 @@
|
||||
FROM alpine:latest
|
||||
|
||||
MAINTAINER sunxyw <me@sunxyw.xyz>
|
||||
|
||||
RUN apk add --no-cache curl
|
||||
RUN mkdir -p /bot && \
|
||||
curl -fsSL https://github.com/onebot-walle/walle-q/releases/latest/download/walle-q-i686-linux-musl.tar.gz -O && \
|
||||
tar -zxvf walle-q-i686-linux-musl.tar.gz -C /bot && \
|
||||
rm -rf walle-q-i686-linux-musl.tar.gz && \
|
||||
chmod +x /bot/walle-q-i686-linux-musl
|
||||
|
||||
ENV TZ=Asia/Shanghai
|
||||
|
||||
ENTRYPOINT [ "/bot/walle-q-i686-linux-musl" ]
|
||||
14
docker/zhamao/Dockerfile
Normal file
@@ -0,0 +1,14 @@
|
||||
FROM phpswoole/swoole:5.0-php8.1
|
||||
|
||||
MAINTAINER sunxyw <me@sunxyw.xyz>
|
||||
|
||||
WORKDIR "/bot"
|
||||
|
||||
ENV TZ=Asia/Shanghai
|
||||
|
||||
EXPOSE 20001-20005
|
||||
|
||||
RUN docker-php-ext-install pcntl
|
||||
|
||||
ENTRYPOINT [ "./zhamao" ]
|
||||
CMD [ "server" ]
|
||||
141
docs/.vuepress/components/ChatBox.vue
Normal file
@@ -0,0 +1,141 @@
|
||||
<template>
|
||||
<div class="doc-chat-container">
|
||||
<div class="doc-chat-content">
|
||||
<div v-for="i in chat" v-bind="i">
|
||||
<div class="doc-chat-row" v-if="i.type === 0">
|
||||
<div class="doc-chat-box">{{ i.content }}</div>
|
||||
<img class="doc-chat-avatar" src="https://api.btstu.cn/sjtx/api.php" alt=""/>
|
||||
</div>
|
||||
<div class="doc-chat-row doc-chat-row-robot" v-else-if="i.type === 1">
|
||||
<img class="doc-chat-avatar" src="https://docs-v1.zhamao.xin/logo.png" alt=""/>
|
||||
<div class="doc-chat-box doc-chat-box-robot">
|
||||
<span v-for="(p,index) in i.content.split('\n')">{{p}}<br v-if="index !== i.content.length - 1"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="doc-chat-row doc-chat-banner" v-else-if="i.type === 2">
|
||||
{{ i.content }}
|
||||
</div>
|
||||
<div class="doc-chat-row doc-chat-row-robot" v-else-if="i.type === 3">
|
||||
<img class="doc-chat-avatar" src="https://docs-v1.zhamao.xin/logo.png" alt=""/>
|
||||
<div class="doc-chat-box doc-chat-box-robot">
|
||||
<img :src="i.content" alt="" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!--
|
||||
type:
|
||||
0: 我方发送消息
|
||||
1: 机器人回复消息
|
||||
2: 显示一个横幅系统消息
|
||||
3: 机器人回复一个图片
|
||||
-->
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "ChatBox",
|
||||
props: ['myChats'],
|
||||
data() {
|
||||
return {
|
||||
chat: this.myChats,
|
||||
multiline: ''
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.doc-chat-content {
|
||||
padding: 12px;
|
||||
}
|
||||
.doc-chat-container {
|
||||
border-radius: 6px;
|
||||
max-width: 550px;
|
||||
min-height: 30px;
|
||||
/*noinspection CssUnresolvedCustomProperty*/
|
||||
background-color: #f2f4f5;
|
||||
|
||||
margin-bottom: 1em;
|
||||
box-shadow: 0 3px 1px -2px rgba(0,0,0,.2), 0 2px 2px 0 rgba(0,0,0,.14), 0 1px 5px 0 rgba(0,0,0,.12);
|
||||
}
|
||||
|
||||
.doc-chat-row {
|
||||
margin: 0;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
flex: 1 1 auto;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.doc-chat-banner {
|
||||
justify-content: center;
|
||||
background: rgba(0,0,0,0.1);
|
||||
width: max-content;
|
||||
margin: 8px auto;
|
||||
padding: 4px 14px;
|
||||
border-radius: 8px;
|
||||
color: gray;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.doc-chat-row-robot {
|
||||
justify-content: flex-start !important;
|
||||
}
|
||||
|
||||
.doc-chat-box {
|
||||
color: #000000de;
|
||||
position: relative;
|
||||
width: fit-content;
|
||||
max-width: 55%;
|
||||
border-radius: .5rem;
|
||||
padding: .4rem .6rem;
|
||||
margin: .4rem .8rem;
|
||||
background-color: #fff;
|
||||
line-height: 1.5;
|
||||
font-size: 16px;
|
||||
outline: none;
|
||||
overflow-wrap: break-word;
|
||||
white-space: normal;
|
||||
box-shadow: 0 2px 12px 0 rgba(0,0,0,.1);
|
||||
}
|
||||
|
||||
.doc-chat-box:after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
right: auto;
|
||||
top: 0;
|
||||
width: 8px;
|
||||
height: 12px;
|
||||
color: #fff;
|
||||
border: 0 solid transparent;
|
||||
border-bottom: 7px solid;
|
||||
border-radius: 0 0 8px 0;
|
||||
left: calc(100% - 4px);
|
||||
box-sizing: inherit;
|
||||
}
|
||||
|
||||
.doc-chat-box-robot:after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
right: calc(100% - 4px);
|
||||
top: 0;
|
||||
width: 8px;
|
||||
height: 12px;
|
||||
color: #fff;
|
||||
border: 0 solid transparent;
|
||||
border-bottom: 7px solid;
|
||||
border-radius: 0 0 0 8px;
|
||||
left: auto;
|
||||
box-sizing: inherit;
|
||||
}
|
||||
|
||||
.doc-chat-avatar {
|
||||
background-color: aquamarine;
|
||||
width: 36px !important;
|
||||
height: 36px !important;
|
||||
border-radius: 18px;
|
||||
}
|
||||
</style>
|
||||
65
docs/.vuepress/config.js
Normal file
@@ -0,0 +1,65 @@
|
||||
module.exports = {
|
||||
title: '炸毛框架 v3',
|
||||
description: '一个高性能聊天机器人 + Web 框架',
|
||||
theme: 'antdocs',
|
||||
markdown: {
|
||||
lineNumbers: true
|
||||
},
|
||||
head: [
|
||||
['link', { rel: 'icon', href: '/logo_trans.png' }],
|
||||
['script', {}, `
|
||||
var _hmt = _hmt || [];
|
||||
(function () {
|
||||
var hm = document.createElement("script");
|
||||
hm.src = "https://hm.baidu.com/hm.js?f0f276cefa10aa31a20ae3815a50b795";
|
||||
var s = document.getElementsByTagName("script")[0];
|
||||
s.parentNode.insertBefore(hm, s);
|
||||
})();
|
||||
`]
|
||||
],
|
||||
themeConfig: {
|
||||
repo: 'zhamao-robot/zhamao-framework',
|
||||
logo: '/logo_trans.png',
|
||||
docsDir: 'docs',
|
||||
editLinks: true,
|
||||
lastUpdated: '上次更新',
|
||||
activeHeaderLinks: false,
|
||||
nav: [
|
||||
{ text: '指南', link: '/guide/' },
|
||||
{ text: '事件', link: '/event/' },
|
||||
{ text: 'API 文档', link: '/doxy/', target: '_blank' },
|
||||
{ text: '炸毛框架 v2', link: 'https://docs-v2.zhamao.xin/' }
|
||||
],
|
||||
sidebar: {
|
||||
'/guide/': [
|
||||
{
|
||||
title: '指南',
|
||||
collapsable: false,
|
||||
sidebarDepth: 1,
|
||||
children: [
|
||||
'',
|
||||
'installation',
|
||||
'configuration',
|
||||
'structure',
|
||||
'get_started',
|
||||
]
|
||||
}
|
||||
],
|
||||
'/event/': [
|
||||
{
|
||||
title: '事件',
|
||||
collapsable: false,
|
||||
sidebarDepth: 1,
|
||||
children: [
|
||||
'',
|
||||
'bot',
|
||||
'http',
|
||||
'middleware',
|
||||
'framework',
|
||||
'extend',
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
1
docs/.vuepress/public/CNAME
Normal file
@@ -0,0 +1 @@
|
||||
framework.zhamao.me
|
||||
BIN
docs/.vuepress/public/index_demo.png
Normal file
|
After Width: | Height: | Size: 449 KiB |
BIN
docs/.vuepress/public/logo_trans.png
Normal file
|
After Width: | Height: | Size: 61 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 28 KiB |
1
docs/.vuepress/public/robots.txt
Normal file
@@ -0,0 +1 @@
|
||||
User-agent: *
|
||||
1
docs/.vuepress/styles/palette.less
Normal file
@@ -0,0 +1 @@
|
||||
@accentColor: #3665aa;
|
||||
@@ -1 +0,0 @@
|
||||
# FAQ
|
||||
44
docs/README.md
Normal file
@@ -0,0 +1,44 @@
|
||||
---
|
||||
home: true
|
||||
heroImage: ./logo_trans.png
|
||||
actions:
|
||||
- text: 快速上手
|
||||
link: /guide/
|
||||
type: primary
|
||||
size: large
|
||||
- text: 快速上手(v2 旧版)
|
||||
link: https://docs-v2.zhamao.xin/
|
||||
type: primary
|
||||
ghost: true
|
||||
size: large
|
||||
features:
|
||||
- title: 高性能
|
||||
details: 基于 PHP 的 Swoole 高性能扩展,利用 WebSocket 进行与 OneBot 协议兼容的聊天机器人软件的通信,还有数据库连接池、内存缓存、多任务进程等特色,大幅增强性能。
|
||||
- title: 易于开发
|
||||
details: 所有功能采用模块化设计,除特殊情况外几乎所有功能都不需要修改框架内任意代码,框架采用灵活的注解进行各类事件绑定,同时支持下断点调试。
|
||||
- title: 接口直观
|
||||
details: 支持命令、普通文本、正则匹配、自然语言处理等多种对话解析方式,利用协程巧妙实现了直观的交互式会话模式,同时支持多种富文本的处理。
|
||||
footer: |
|
||||
Apache-2.0 Licensed | Copyright © 2019-2022 Zhamao Developer Team | <a href="http://beian.miit.gov.cn">沪ICP备2021010446号-1</a>
|
||||
---
|
||||
|
||||
# 快速上手
|
||||
|
||||
## 安装框架和环境
|
||||
|
||||
此命令可一键以模板安装框架!(仅限 Linux 和 macOS)
|
||||
|
||||
```bash
|
||||
bash <(curl -fsSL https://zhamao.xin/v3.sh)
|
||||
```
|
||||
|
||||
## 运行框架
|
||||
|
||||
```bash
|
||||
cd zhamao-app/
|
||||
./zhamao server
|
||||
```
|
||||
|
||||
## 效果图
|
||||
|
||||

|
||||
@@ -1,3 +0,0 @@
|
||||
# 进阶开发
|
||||
## 深入
|
||||
还没填坑,敬请期待!
|
||||
@@ -1,29 +0,0 @@
|
||||
# 内部类文件手册
|
||||
|
||||
这个章节写明了在框架使用过程中可能涉及到的框架内部或 Swoole、其他 composer 依赖组件的内部类,这里会根据类的命名空间一一说明。
|
||||
|
||||
## Swoole\Http\Request
|
||||
|
||||
此类是 Swoole 内部的一个类,一般在收到 HTTP 请求时,在 `@RequestMapping` 或 `@OnRequestEvent()` 两个注解下可用,用作获取 GET、POST参数,上传到后端的文件、Cookies 等。详见 [Swoole 文档 - Request](http://wiki.swoole.com/#/http_server?id=httprequest) 。
|
||||
|
||||
### 属性
|
||||
|
||||
- `$fd`:获取当前连接的文件描述符 ID。
|
||||
- `$header`:`HTTP` 请求的头部信息。类型为数组,所有 `key` 均为小写。
|
||||
- `$server`:`HTTP` 请求相关的服务器信息。
|
||||
- `$cookie`:获取 Cookies。
|
||||
- `$get`:获取 GET 参数。
|
||||
- `$post`:获取 POST 参数。
|
||||
- `$files`:获取上传的文件信息
|
||||
|
||||
### 方法
|
||||
|
||||
- `rawContent()`:获取 POST 包原始二进制内容,相当于原生 PHP 的 ` file_get_contents("php://input");` 。
|
||||
- `getData()`:获取完整的原始 `Http` 请求报文。包括 `Http Header` 和 `Http Body`
|
||||
|
||||
### 示例
|
||||
|
||||
```php
|
||||
TODO:先放一放。
|
||||
```
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
# 从炸毛框架 V1 升级
|
||||
|
||||
> 这里只写明可能在升级过程中会影响原先代码执行的部分,不包含新增的特性等。
|
||||
|
||||
### 需要改变命名空间的类
|
||||
|
||||
- `Framework\Console` -> `ZM\Console\Console`
|
||||
- `Swlib\Util\SingletonTrait` -> `ZM\Utils\SingletonTrait`
|
||||
- `ZM\Annotation\Http\Before` -> `ZM\Annotation\Http\HandleBefore`
|
||||
- `ZM\Annotation\Http\After` -> `ZM\Annotation\Http\HandleAfter`
|
||||
- `@SwooleEventAt` -> `@OnSwooleEvent`
|
||||
- 删除 `@SwooleEventAfter`
|
||||
- 删除 `ModBase`
|
||||
- `@HandleEvent` -> `@SwooleHandler`
|
||||
- `ZM\Utils\ZMRobot` -> `\ZM\API\ZMRobot`
|
||||
|
||||
### 方法名称变更
|
||||
|
||||
- `ZM\Console::stackTrace()` -> `ZM\Console::trace()`
|
||||
|
||||
### 注解的变化
|
||||
|
||||
`@OnSwooleEvent`(原 `@SwooleEventAt`)中,`rule` 参数不再是自定义语法的东西了(比如之前的 `connectType:qq` 之类的鸡肋语法),直接是可执行的 PHP 代码,比如 `3 == 4`,`connectIsQQ()` 之类的。
|
||||
|
||||
去除 `@CQAPISend`,因为目前没什么意义。
|
||||
|
||||
`@CQCommand` 中,`regexMatch` 变成 `pattern`,`fullMatch` 变成 `regex`,消除歧义(第一个是 * 号匹配符进行匹配的,第二个是标准的正则表达式匹配)。同时新增 `start_with`,`end_with`,`keyword` 平行选项。
|
||||
|
||||
`@OnTick` 注解新增第二个参数 `worker_id`,其中默认是 0,代表只在 `#0` 号工作进程上运行计时器。
|
||||
|
||||
### 中间件编写的改变
|
||||
|
||||
原先的 Middleware 是需要含有 `getName()` 方法才合法,现在不需要了,但是对 `@MiddlewareClass` 注解需要增加参数,也就是说原先 `getName()` 返回的名称现在需要写到 `@MiddlewareClass("xxx")` 这样的形式。
|
||||
|
||||
### ZMBuf 的变化
|
||||
|
||||
由于 2.0 框架使用了多进程模型,所以不能使用原先适用于单进程下全局变量的方式(ZMBuf)进行存取变量,所以 ZMBuf 下的所有方法都需要更改,其中 `get, set` 等对缓存操作的模型请根据 2.0 的文档变更使用 `Redis` 或内置的多进程共享内存可用的 `LightCache` 轻量缓存。
|
||||
|
||||
而获取全局配置文件,如 `global.php` 文件,也发生了变化,新框架引入了 `ZMConfig` 对象,可以快速地区分各类环境变量从而读取不同的配置文件。比如我们获取原先的 global 配置文件中的一项:`ZMBuf::globals("port")`,在 2.0 中需要使用 `ZMConfig::get("global", "port")` 方式。以此类推,`ZMBuf::config("xxx")` 也直接变为 `ZMConfig::get("xxx")` 了。
|
||||
@@ -1,103 +0,0 @@
|
||||
|
||||
.md-header-nav__button.md-logo {
|
||||
padding: .2rem;
|
||||
margin: .2rem;
|
||||
}
|
||||
|
||||
.md-header-nav__button.md-logo img, .md-header-nav__button.md-logo svg {
|
||||
width: 1.6rem;
|
||||
height: 1.6rem;
|
||||
}
|
||||
|
||||
.doc-chat-container {
|
||||
border-radius: 6px;
|
||||
width: 100%;
|
||||
min-height: 30px;
|
||||
/*noinspection CssUnresolvedCustomProperty*/
|
||||
background-color: var(--md-code-bg-color);
|
||||
padding: 12px;
|
||||
margin-right: auto;
|
||||
margin-left: auto;
|
||||
box-shadow: 0 3px 1px -2px rgba(0,0,0,.2), 0 2px 2px 0 rgba(0,0,0,.14), 0 1px 5px 0 rgba(0,0,0,.12);
|
||||
}
|
||||
|
||||
.doc-chat-row {
|
||||
margin: 0;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
flex: 1 1 auto;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.doc-chat-banner {
|
||||
justify-content: center;
|
||||
background: rgba(0,0,0,0.1);
|
||||
width: max-content;
|
||||
margin: 8px auto;
|
||||
padding: 4px 14px;
|
||||
border-radius: 8px;
|
||||
color: gray;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.doc-chat-row-robot {
|
||||
justify-content: flex-start !important;
|
||||
}
|
||||
|
||||
.doc-chat-box {
|
||||
color: #000000de;
|
||||
position: relative;
|
||||
width: fit-content;
|
||||
max-width: 55%;
|
||||
border-radius: .5rem;
|
||||
padding: .4rem .6rem;
|
||||
margin: .4rem .8rem;
|
||||
background-color: #fff;
|
||||
line-height: 1.5;
|
||||
font-size: 16px;
|
||||
outline: none;
|
||||
overflow-wrap: break-word;
|
||||
white-space: normal;
|
||||
box-shadow: 0 2px 12px 0 rgba(0,0,0,.1);
|
||||
}
|
||||
|
||||
.doc-chat-box:after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
right: auto;
|
||||
top: 0;
|
||||
width: 8px;
|
||||
height: 12px;
|
||||
color: #fff;
|
||||
border: 0 solid transparent;
|
||||
border-bottom: 7px solid;
|
||||
border-radius: 0 0 8px 0;
|
||||
left: calc(100% - 4px);
|
||||
box-sizing: inherit;
|
||||
}
|
||||
|
||||
.doc-chat-box-robot:after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
right: calc(100% - 4px);
|
||||
top: 0;
|
||||
width: 8px;
|
||||
height: 12px;
|
||||
color: #fff;
|
||||
border: 0 solid transparent;
|
||||
border-bottom: 7px solid;
|
||||
border-radius: 0 0 0 8px;
|
||||
left: auto;
|
||||
box-sizing: inherit;
|
||||
}
|
||||
|
||||
.doc-chat-avatar {
|
||||
background-color: aquamarine;
|
||||
width: 36px !important;
|
||||
height: 36px !important;
|
||||
border-radius: 18px;
|
||||
}
|
||||
|
||||
.md-typeset .admonition, .md-typeset details {
|
||||
font-size: .72rem;
|
||||
}
|
||||
|
Before Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 42 KiB |
|
Before Width: | Height: | Size: 98 KiB |
@@ -1,299 +0,0 @@
|
||||
# 上下文
|
||||
|
||||
上下文作为整个框架中最重要的内容之一,请务必理解和完整地阅读此部分!
|
||||
|
||||
一个上下文描述了一个事件和所关联的对象的环境。例如:你在处理 HTTP 请求的 `@RequestMapping` 绑定的事件中,你需要获取请求的 HTTP 头和 Cookie,再比如你在处理 QQ 机器人发来的命令 `@CQCommand("随机数")` 的时候,在这个方法内,你需要获取发来的人的 QQ 号码。以上我们将处理以上运行环境的对象叫做上下文。
|
||||
|
||||
由于 Swoole 的协程加持,我们利用了协程 ID 绑定对象来进行构造上下文。
|
||||
|
||||
以默认的机器人收发消息为例,通过对默认模块的了解,我们可以知道,在绑定 `@CQCommand` 等类似事件后,你可以用上下文获取发来这条消息的人的 QQ 号码:
|
||||
|
||||
```php
|
||||
/**
|
||||
* @CQCommand("你好")
|
||||
*/
|
||||
public function hello() {
|
||||
$user_id = ctx()->getUserId();
|
||||
ctx()->reply("你好啊,".$user_id.",很高兴认识你!");
|
||||
}
|
||||
```
|
||||
|
||||
`context()` 就是获取上下文对象的全局函数,它还有简写:`ctx()`。
|
||||
|
||||
当然,上下文中的方法不是每个都能在任何时候使用的。例如 `getUserId()` 你不能在 `@RequestMapping` 注解的函数中使用,因为它不是机器人消息的上下文。下面说明上下文对象的方法中,每个都会说明每个方法可以在哪些事件中使用:
|
||||
|
||||
## getServer() - 获取 Server 对象
|
||||
|
||||
获取 Swoole WebSocker Server 对象。此对象是 Swoole 的对象,详情见 [Swoole 文档](https://wiki.swoole.com/#/websocket_server)。
|
||||
|
||||
可以使用的事件:`@OnMessageEvent()`,`@OnOpenEvent()`,`@OnCloseEvent()`,`@OnStart()` 以及所有 HTTP API 发来的事件:`@CQCommand()`,`@CQMessage()` 等。
|
||||
|
||||
## getFrame() - 获取 WS 数据帧
|
||||
|
||||
获取 `\Swoole\Websocket\Frame` 对象,此对象是 Swoole 的对象,详情见 [Swoole 文档](https://wiki.swoole.com/#/websocket_server?id=swoolewebsocketframe)。
|
||||
|
||||
可以使用的事件:`@OnMessageEvent()` 以及所有 HTTP API 发来的事件:`@CQCommand()`,`@CQMessage()` 等,
|
||||
|
||||
## getFd() - 返回 fd 值
|
||||
|
||||
获取当前连入 Swoole 服务器的连接文件描述符 ID。返回 int。一般代表连接号,可用来绑定对应链接。
|
||||
|
||||
可以使用的事件:所有 **getFrame()** 可以使用的,`@OnOpenEvent()`,`@OnCloseEvent()`
|
||||
|
||||
!!! tip "提示"
|
||||
|
||||
值得注意的是,由于机器人客户端和炸毛框架的连接是通过 WebSocket 进行的,而 WebSocket 是长连接,所以同一个机器人一次连接下收发消息所用的连接是同一个,所以 Fd 也是相同的。同理,炸毛框架的内部来区分多个机器人也是通过这一 Fd 进行判定的。
|
||||
|
||||
=== "代码"
|
||||
|
||||
```php
|
||||
/**
|
||||
* @CQCommand("测试fd")
|
||||
*/
|
||||
public function testfd() {
|
||||
ctx()->reply("当前机器人连接的fd是:".ctx()->getFd()",机器人QQ是:".ctx()->getRobotId());
|
||||
}
|
||||
```
|
||||
|
||||
=== "效果"
|
||||
|
||||
<chat-box>
|
||||
^ 假设我们和连接55555的机器人的私聊
|
||||
) 测试fd
|
||||
( 当前机器人连接的fd是:1,机器人QQ是:55555
|
||||
^ 假设切到了另一个机器人(66666)的私聊
|
||||
) 测试fd
|
||||
( 当前机器人连接的fd是:2,机器人QQ是:66666
|
||||
</chat-box>
|
||||
|
||||
## getData() - 获取事件完整数据
|
||||
|
||||
返回 CQHTTP 事件上报的原始数据包,已经被解析成数组,可以直接操作。
|
||||
|
||||
可以使用的事件:所有 HTTP API 发来的事件:`@CQCommand()`,`@CQMessage()` 等。
|
||||
|
||||
```php
|
||||
/**
|
||||
* @CQMessage(user_id=123456)
|
||||
*/
|
||||
public function onMessage() {
|
||||
$data = ctx()->getData();
|
||||
ctx()->reply("消息类型是:" . $data["message_type"]);
|
||||
}
|
||||
```
|
||||
|
||||
<chat-box>
|
||||
^ 假设我是QQ为123456的用户,私聊发消息
|
||||
) 哈咯!!
|
||||
( 消息类型是:private
|
||||
</chat-box>
|
||||
|
||||
## getRequest() - HTTP 请求对象
|
||||
|
||||
返回 `\Swoole\Http\Request` 对象,可在 `@RequestMapping` 中使用,获取 Cookie,请求头,GET 参数什么的。[Swoole 文档](https://wiki.swoole.com/#/http_server?id=httprequest)。
|
||||
|
||||
可以使用的事件:`@RequestMapping()`,`@OnRequestEvent()`,`@OnOpenEvent()`。
|
||||
|
||||
## getResponse() - HTTP 响应对象
|
||||
|
||||
返回 `\Swoole\Http\Response` 对象的增强版,可在 HTTP 请求相关的事件中使用,返回内容和设置 Cookie 什么的。[Swoole 文档](https://wiki.swoole.com/#/http_server?id=httpresponse)。
|
||||
|
||||
可以使用的事件:`@RequestMapping()`,`@OnRequestEvent()`。
|
||||
|
||||
下面是使用以上两个功能的组合示例:
|
||||
|
||||
```php
|
||||
/**
|
||||
* @RequestMapping("/ping")
|
||||
*/
|
||||
public function ping() {
|
||||
$name = ctx()->getRequest()->get["name"] ?? "unknown";
|
||||
ctx()->getResponse()->end("Hello ".$name."!");
|
||||
}
|
||||
```
|
||||
|
||||
## getConnection() - WS 连接对象
|
||||
|
||||
返回此上下文相关联的 WebSocket 连接对象。
|
||||
|
||||
可以使用的事件:所有 **getFrame()** 可以使用的都可以使用。
|
||||
|
||||
## getCid() - 上下文 ID
|
||||
|
||||
返回当前上下文所绑定的协程 ID,此 ID 和 `\Co::getCid()` 返回值一样。
|
||||
|
||||
## getRobot() - 获取机器人 API 对象
|
||||
|
||||
返回当前上下文关联的机器人 API 调用对象 [ZMRobot](robot-api.md)。
|
||||
|
||||
可以使用的事件:所有 HTTP API 发来的事件:`@CQCommand()`,`@CQMessage()` 等。
|
||||
|
||||
```php
|
||||
ctx()->getRobot()->sendPrivateMsg(123456, "发送私聊消息");
|
||||
```
|
||||
|
||||
<chat-box>
|
||||
^ 正在和机器人聊天
|
||||
( 发送私聊消息
|
||||
</chat-box>
|
||||
|
||||
## getMessage() - 获取消息
|
||||
|
||||
获取 data 数据中的 `message` 消息,用于快速获取用户消息事件的消息内容。
|
||||
|
||||
可以使用的事件:`@CQCommand()`,`@CQMessage`,`@CQBefore("message")`,`@CQAfter("message")`
|
||||
|
||||
=== "代码"
|
||||
```php
|
||||
/**
|
||||
* @CQMessage(group_id=33333)
|
||||
*/
|
||||
public function groupRepeat() {
|
||||
ctx()->reply(ctx()->getMessage());
|
||||
}
|
||||
```
|
||||
|
||||
=== "效果"
|
||||
<chat-box>
|
||||
^ 现在在群33333内,机器人已经成了复读机
|
||||
) 来世还做复读机!!!
|
||||
( 来世还做复读机!!!
|
||||
) 你不许复读!
|
||||
( 你不许复读!
|
||||
</chat-box>
|
||||
|
||||
## getUserId() - 获取用户 QQ 号
|
||||
|
||||
获取发消息的用户的 QQ 号码。
|
||||
|
||||
可以使用的事件:所有 **含有** `user_id` 上报参数的 OneBot 事件。
|
||||
|
||||
```php
|
||||
/**
|
||||
* @CQCommand("whoami")
|
||||
*/
|
||||
public function whoami() {
|
||||
ctx()->reply("你是".ctx()->getUserId()); //返回:你是123456
|
||||
}
|
||||
```
|
||||
|
||||
## getGroupId() - 获取 QQ 群号
|
||||
|
||||
获取发消息来自的 QQ 群号。
|
||||
|
||||
可以使用的事件:所有含有 `group_id` 上报参数的 OneBot 事件。
|
||||
|
||||
## getMessageType() - 消息类型
|
||||
|
||||
获取消息类型,同参数 `message_type`。
|
||||
|
||||
可以使用的事件:所有 `post_type` 为 `message` 的响应事件,如 `@CQMessage`,`@CQCommand`。
|
||||
|
||||
## getRobotId() - 机器人 QQ 号
|
||||
|
||||
获取事件上报的机器人自己的 QQ 号码。
|
||||
|
||||
可以使用的事件:所有 OneBot 发来的事件:`@CQCommand()`,`@CQNotice()` 等。
|
||||
|
||||
## setMessage() - 设置消息
|
||||
|
||||
与 `getMessage()` 对应,用于更改上下文中保存的事件信息,可以用于消息变更和过滤。
|
||||
|
||||
## setUserId() - 设置用户 ID
|
||||
|
||||
与上同理,更改 `user_id`。
|
||||
|
||||
## setGroupId() - 设置群号
|
||||
|
||||
与上同理。
|
||||
|
||||
## setMessageType() - 设置类型
|
||||
|
||||
与上同理,修改消息类型。
|
||||
|
||||
## setData() - 设置数据包
|
||||
|
||||
与上同理,与 `getData()` 对应,用于更改上下文中的 `data`。
|
||||
|
||||
## getCache() - 上下文缓存
|
||||
|
||||
获取保存在上下文中的临时缓存变量。当相关联的事件结束后,数据会从内存中被释放。用于同一事件的多个函数中的信息传递。
|
||||
|
||||
- 参数:`$key`,缓存变量的键名
|
||||
- 返回:`mixed`,存入缓存的变量值。
|
||||
|
||||
```php
|
||||
$a = ctx()->getCache("block_continue");
|
||||
// 如果变量不存在,则返回 null
|
||||
```
|
||||
|
||||
## setCache() - 上下文缓存
|
||||
|
||||
与 `getCache()` 对应,是设置内容的。
|
||||
|
||||
```php
|
||||
ctx()->setCache("abc", "asdasd");
|
||||
$result = ctx()->getCache("abc"); // asdasd
|
||||
```
|
||||
|
||||
## reply() - 快速回复
|
||||
|
||||
快速回复当前用户消息内容。
|
||||
|
||||
- 参数1:`$msg`,字符串,你要回复的消息内容
|
||||
- 参数2:`$yield = false`,可选,当为 `true` 时,会协程等待后返回 **消息回复** 的结果,包括 API 状态码、消息 `message_id` 等。
|
||||
|
||||
```php
|
||||
$r = ctx()->reply("我又好了。");
|
||||
if($r["retcode"] == 0) Console::success("消息发送成功!");
|
||||
```
|
||||
|
||||
## finalReply() - 快速回复
|
||||
|
||||
快速回复用户消息,并阻止其他模块接下来继续处理此事件。
|
||||
|
||||
参数同 `reply()`。
|
||||
|
||||
## waitMessage()
|
||||
|
||||
- 参数:`waitMessage($prompt = "", $timeout = 600, $timeout_prompt = "")`
|
||||
- 用途:等待用户输入消息
|
||||
|
||||
`$prompt` 参数为回复用户的文本内容,`$timeout` 是等待用户回复的超时时间(秒),`$timeout_prompt` 是超时后回复用户的文本。
|
||||
|
||||
这个功能可以让开发机器人的代码逻辑和实际贴合,避免回调地狱、拼接参数、上下文脱节等问题,比如下方的示例,可以仅仅用两行代码实现一个问答式的对话过程。
|
||||
|
||||
用法示例:
|
||||
|
||||
```php
|
||||
/**
|
||||
* @CQCommand("自我介绍")
|
||||
*/
|
||||
function yourName(){
|
||||
$r = ctx()->waitMessage("你叫啥名字呀?", 600, "你都10分钟不理我了,嘤嘤嘤");
|
||||
ctx()->finalReply("好的,可爱的机器人记住你叫 ".$r." 啦!以后多聊天哦!");
|
||||
}
|
||||
```
|
||||
|
||||
<chat-box>
|
||||
) 自我介绍
|
||||
( 你叫啥名字呀?
|
||||
) jerry
|
||||
( 好的,可爱的机器人记住你叫 jerry 啦!以后多聊天哦!
|
||||
) 自我介绍
|
||||
( 你叫啥名字呀?
|
||||
^ 10分钟没理机器人
|
||||
( 你都10分钟不理我了,嘤嘤嘤
|
||||
</chat-box>
|
||||
|
||||
## getArgs()
|
||||
|
||||
TODO:还没写到这里,下次更新,今晚太困了。
|
||||
|
||||
## copy()
|
||||
|
||||
t
|
||||
|
||||
## getOption() - 获取匹配参数内容
|
||||
|
||||
|
||||
@@ -1,424 +0,0 @@
|
||||
# CQ 码(多媒体消息)
|
||||
|
||||
消息中的多媒体内容使用 CQ 码来表示,形如 `[CQ:face,id=178]`。其中,`[CQ:]` 是固定格式;`face` 是「功能名」,除了 `face` 还有许多不同的功能名;`id=178` 是「参数」,某些功能不需要参数,而另一些需要多个参数,当有多个参数时,参数间使用逗号分隔。
|
||||
|
||||
## 格式
|
||||
|
||||
一些 CQ 码的例子如下:
|
||||
|
||||
```
|
||||
[CQ:shake]
|
||||
[CQ:face,id=178]
|
||||
[CQ:share,title=标题,url=http://baidu.com]
|
||||
```
|
||||
|
||||
更多 CQ 码功能请参考 [消息段类型](https://github.com/howmanybots/onebot/blob/master/v11/specs/message/segment.md)。
|
||||
|
||||
!!! warning "注意"
|
||||
|
||||
CQ 码中不应有多余的空格,例如不应该使用 `[CQ:face, id=178]`。
|
||||
|
||||
CQ 码的参数值可以包含空格、换行、除 `[],&` 之外的特殊符号等。在解析时,应直接取 `[CQ:` 后、第一个 `,` 或 `]` 前的部分为功能名,第一个 `,` 之后到 `]` 之间的部分为参数,按 `,` 分割后,每个部分第一个 `=` 前的内容为参数名,之后的部分为参数值。例如 `[CQ:share,title=标题中有=等号,url=http://baidu.com]` 中,功能名为 `share`,`title` 参数值为 `标题中有=等号`,`url` 参数值为 `http://baidu.com`。
|
||||
|
||||
## 转义
|
||||
|
||||
CQ 码中包含一些特殊字符:`[`、`]`、`,` 等,而 CQ 码又是可能混杂在纯文本内容之中的,因此消息中的纯文本内容需要对特殊字符进行转义,以避免歧义。具体的转义规则如下:
|
||||
|
||||
| 转义前 | 转义后 |
|
||||
| ------ | ------- |
|
||||
| `&` | `&` |
|
||||
| `[` | `[` |
|
||||
| `]` | `]` |
|
||||
|
||||
另一方面,CQ 码内部的参数值也可能出现特殊字符,也是需要转义的。由于 `,`(半角逗号)在 CQ 码中用于分隔参数,因此除了上面的转义规则,还需要对 `,` 进行转义,如下:
|
||||
|
||||
| 转义前 | 转义后 |
|
||||
| ------ | ------- |
|
||||
| `&` | `&` |
|
||||
| `[` | `[` |
|
||||
| `]` | `]` |
|
||||
| `,` | `,` |
|
||||
|
||||
例如,一个链接分享消息的 CQ 码可能如下:
|
||||
|
||||
```
|
||||
[CQ:share,title=震惊,小伙睡觉前居然...,url=http://baidu.com/?a=1&b=2]
|
||||
```
|
||||
|
||||
## 封装调用
|
||||
|
||||
框架提供了 CQ 码的封装,你可以在任何位置使用封装好的 CQ 码生成器。
|
||||
|
||||
生成器是一个静态类,里面的方法全部是静态调用,命名空间是:`ZM\API\CQ`。
|
||||
|
||||
例如,给用户发送图片这样写就好啦!只需要将添加图片的地方拼到回复用户的字符串里。如果只发图片,整个字符串里只能有 CQ 码。
|
||||
|
||||
```php
|
||||
<?php
|
||||
namespace Module\Example;
|
||||
use ZM\API\CQ;
|
||||
use ZM\Annotation\CQ\CQCommand;
|
||||
class Hello {
|
||||
/**
|
||||
* @CQCommand("发送图片")
|
||||
*/
|
||||
public function msgRecv() {
|
||||
return CQ::image("https://zhamao.xin/file/hello.jpg");
|
||||
// 相当于返回:"[CQ:image,file=https://zhamao.xin/file/hello.jpg]"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
效果
|
||||
|
||||
<chat-box>
|
||||
) 发送图片
|
||||
[ https://zhamao.xin/file/hello.jpg
|
||||
</chat-box>
|
||||
|
||||
## CQ 码列表
|
||||
|
||||
### CQ::face() - 发送 QQ 表情
|
||||
|
||||
发送 QQ 原生表情。
|
||||
|
||||
定义:`CQ::face($id)`
|
||||
|
||||
参数:`$id` 为 QQ 表情对应的 ID 号,一些常见的表情 ID 对应的表情样式见 [炸毛框架 1.x 版本文档](https://docs-v1.zhamao.xin/face_list.html)。
|
||||
|
||||
```php
|
||||
/**
|
||||
* @CQCommand("打盹")
|
||||
*/
|
||||
public function faceTest() {
|
||||
ctx()->reply("正在打盹...");
|
||||
ctx()->reply(CQ::face(8));
|
||||
}
|
||||
```
|
||||
|
||||
<chat-box>
|
||||
) 打盹
|
||||
( 正在打盹...
|
||||
[ https://docs-v1.zhamao.xin/face/8.gif
|
||||
</chat-box>
|
||||
|
||||
!!! note "提示"
|
||||
对于不断更新的 QQ 版本下,可能会持续扩充新的 QQ 表情,如果上表没有新的表情的话,也可以使用消息接收的方式,让机器人收到表情后解析出来对应的 id 然后再发送。
|
||||
|
||||
|
||||
### CQ::image() - 发送图片
|
||||
|
||||
发送图片。
|
||||
|
||||
定义:`image($file, $cache = true, $flash = false, $proxy = true, $timeout = -1)`
|
||||
|
||||
参数
|
||||
|
||||
| 参数名 | 收 | 发 | 默认值 | 说明 |
|
||||
| --------- | ---- | ---- | ------- | ------------------------------------------------------------ |
|
||||
| `file` | ✓ | ✓ | 必填 | 图片文件名 |
|
||||
| `flash` | ✓ | ✓ | `false` | 图片类型,当参数为 true 时代表发送闪照 |
|
||||
| `cache` | | ✓ | `true` | 只在通过网络 URL 发送时有效,表示是否使用已缓存的文件,默认 `true` |
|
||||
| `proxy` | | ✓ | `true` | 只在通过网络 URL 发送时有效,表示是否通过代理下载文件(需通过环境变量或配置文件配置代理),默认 `true` |
|
||||
| `timeout` | | ✓ | `-1` | 只在通过网络 URL 发送时有效,单位秒,表示下载网络文件的超时时间,默认 -1 不超时 |
|
||||
|
||||
发送时,`file` 参数除了支持使用收到的图片文件名直接发送外,还支持:
|
||||
|
||||
- 绝对路径,例如 `file:///root/imagetest/1.png`,格式使用 [`file` URI](https://tools.ietf.org/html/rfc8089)
|
||||
- 网络 URL,例如 `http://i1.piimg.com/567571/fdd6e7b6d93f1ef0.jpg`
|
||||
- Base64 编码,例如 `base64://iVBORw0KGgoAAAANSUhEUgAAABQAAAAVCAIAAADJt1n/AAAAKElEQVQ4EWPk5+RmIBcwkasRpG9UM4mhNxpgowFGMARGEwnBIEJVAAAdBgBNAZf+QAAAAABJRU5ErkJggg==`
|
||||
|
||||
### CQ::record() - 发送语音
|
||||
|
||||
发送语音消息。
|
||||
|
||||
定义:`CQ::record($file, $magic = false, $cache = true, $proxy = true, $timeout = -1)`
|
||||
|
||||
参数
|
||||
|
||||
| 参数名 | 收 | 发 | 默认值 | 说明 |
|
||||
| --------- | ---- | ---- | ------- | ------------------------------------------------------------ |
|
||||
| `file` | ✓ | ✓ | 必填 | 音频文件名 |
|
||||
| `flash` | ✓ | ✓ | `false` | 图片类型,当参数为 true 时代表发送闪照 |
|
||||
| `cache` | | ✓ | `true` | 只在通过网络 URL 发送时有效,表示是否使用已缓存的文件,默认 `true` |
|
||||
| `proxy` | | ✓ | `true` | 只在通过网络 URL 发送时有效,表示是否通过代理下载文件(需通过环境变量或配置文件配置代理),默认 `true` |
|
||||
| `timeout` | | ✓ | `-1` | 只在通过网络 URL 发送时有效,单位秒,表示下载网络文件的超时时间,默认 -1 不超时 |
|
||||
|
||||
发送时,`file` 参数除了支持使用收到的语音文件名直接发送外,还支持其它形式,参考上方发送图片。
|
||||
|
||||
```php
|
||||
/**
|
||||
* @CQCommand("说你好")
|
||||
*/
|
||||
public function say() {
|
||||
ctx()->reply(CQ::record("https://zhamao.xin/file/hello.mp3"));
|
||||
}
|
||||
```
|
||||
|
||||
<chat-box>
|
||||
) 说你好
|
||||
( [语音消息,点击收听] 2'' )))
|
||||
</chat-box>
|
||||
|
||||
> 此 CQ 码只能用于单独一条文本消息中,如果混有其他字符串,则会吞掉其他字符串内容。
|
||||
|
||||
### CQ::at() - 群里@某人或全体
|
||||
|
||||
在群里 at 某个人或全体成员(全体成员需要有管理员权限)。
|
||||
|
||||
定义:`CQ::at($qq)`
|
||||
|
||||
参数:`$qq` 参数必填,如果填的是 QQ 号,则是单独 at 某人,如果是 `all`,则是 at 全体成员。
|
||||
|
||||
```php
|
||||
/**
|
||||
* @CQCommand("at测试")
|
||||
*/
|
||||
public function atTest() {
|
||||
ctx()->reply(CQ::at(627577391)." 你好啊!");
|
||||
}
|
||||
```
|
||||
|
||||
<chat-box>
|
||||
) at测试
|
||||
( @鲸鱼 你好啊!
|
||||
</chat-box>
|
||||
|
||||
### CQ::video() - 发送短视频
|
||||
|
||||
发送短视频。
|
||||
|
||||
定义:`CQ::video($file, $cache = true, $proxy = true, $timeout = -1)`
|
||||
|
||||
参数
|
||||
|
||||
| 参数名 | 收 | 发 | 默认值 | 说明 |
|
||||
| --------- | ---- | ---- | ------ | ------------------------------------------------------------ |
|
||||
| `file` | ✓ | ✓ | 必填 | 短视频文件名 |
|
||||
| `cache` | | ✓ | `true` | 只在通过网络 URL 发送时有效,表示是否使用已缓存的文件,默认 `true` |
|
||||
| `proxy` | | ✓ | `true` | 只在通过网络 URL 发送时有效,表示是否通过代理下载文件(需通过环境变量或配置文件配置代理),默认 `true` |
|
||||
| `timeout` | | ✓ | `-1` | 只在通过网络 URL 发送时有效,单位秒,表示下载网络文件的超时时间,默认 -1 不超时 |
|
||||
|
||||
发送时,`file` 参数除了支持使用收到的视频文件名直接发送外,还支持其它形式,参考上方发送图片。
|
||||
|
||||
> 此 CQ 码只能用于单独一条文本消息中,如果混有其他字符串,则会吞掉其他字符串内容。
|
||||
|
||||
### CQ::rps() - 猜拳
|
||||
|
||||
定义:`CQ::rps()`
|
||||
|
||||
用法:`CQ::rps()`
|
||||
|
||||
> 此 CQ 码只能用于单独一条文本消息中,如果混有其他字符串,则会吞掉其他字符串内容。
|
||||
|
||||
### CQ::dice() - 掷骰子
|
||||
|
||||
定义:`CQ::dice()`
|
||||
|
||||
用法:`CQ::dice()`
|
||||
|
||||
> 此 CQ 码只能用于单独一条文本消息中,如果混有其他字符串,则会吞掉其他字符串内容。
|
||||
|
||||
### CQ::shake() - 窗口抖动
|
||||
|
||||
定义:`CQ::shake()`
|
||||
|
||||
用法:`CQ::shake()`
|
||||
|
||||
> 此 CQ 码只能用于单独一条文本消息中,如果混有其他字符串,则会吞掉其他字符串内容。
|
||||
|
||||
### CQ::poke() - 戳一戳
|
||||
|
||||
发送戳一戳。
|
||||
|
||||
定义:`CQ::poke($type, $id, $name = "")`
|
||||
|
||||
参数
|
||||
|
||||
| 参数名 | 收 | 发 | 可能的值 | 说明 |
|
||||
| ------ | ---- | ---- | ------------------------------------------------------------ | ------ |
|
||||
| `type` | ✓ | ✓ | 见 [Mirai 的 PokeMessage 类](https://github.com/mamoe/mirai/blob/f5eefae7ecee84d18a66afce3f89b89fe1584b78/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/HummerMessage.kt#L49) | 类型 |
|
||||
| `id` | ✓ | ✓ | 同上 | ID |
|
||||
| `name` | ✓ | | 同上 | 表情名 |
|
||||
|
||||
例子:`CQ::poke(6,-1)`
|
||||
|
||||
效果:放大招
|
||||
|
||||
> 此 CQ 码只能用于单独一条文本消息中,如果混有其他字符串,则会吞掉其他字符串内容。
|
||||
|
||||
### CQ::anonymous() - 匿名发消息
|
||||
|
||||
匿名发消息。需要在允许匿名发消息的群里发。
|
||||
|
||||
!!! tip "提示"
|
||||
|
||||
当收到匿名消息时,需要通过 [消息事件的群消息](https://github.com/howmanybots/onebot/blob/master/v11/specs/event/message.md#群消息) 的 `anonymous` 字段判断。
|
||||
|
||||
定义:`CQ::anonymous($ignore = 1)`
|
||||
|
||||
```php
|
||||
/**
|
||||
* @CQCommand("匿名测试")
|
||||
*/
|
||||
public function anonymousTest() {
|
||||
ctx()->reply(CQ::anonymous()."匿名测试");
|
||||
}
|
||||
```
|
||||
|
||||
### CQ::share() - 链接分享
|
||||
|
||||
发送链接分享卡片,可自定义内容。
|
||||
|
||||
定义:`CQ::share($url, $title, $content = null, $image = null)`
|
||||
|
||||
参数
|
||||
|
||||
| 参数名 | 收 | 发 | 可能的值 | 说明 |
|
||||
| --------- | ---- | ---- | -------- | -------------- |
|
||||
| `url` | ✓ | ✓ | - | URL |
|
||||
| `title` | ✓ | ✓ | - | 标题 |
|
||||
| `content` | ✓ | ✓ | - | 可选,内容描述 |
|
||||
| `image` | ✓ | ✓ | - | 可选,图片 URL |
|
||||
|
||||
```php
|
||||
/**
|
||||
* @CQCommand("链接分享测试")
|
||||
*/
|
||||
public function shareTest() {
|
||||
ctx()->reply(CQ::share("https://baidu.com", "UC忽悠部", "震惊!我市一男子在光天化日之下..."));
|
||||
}
|
||||
```
|
||||
|
||||
### CQ::contact() - 推荐好友
|
||||
|
||||
发送推荐好友的卡片。
|
||||
|
||||
定义:`CQ::contact($type, $id)`
|
||||
|
||||
参数
|
||||
|
||||
| 参数名 | 收 | 发 | 可能的值 | 说明 |
|
||||
| ------ | ---- | ---- | ------------- | ---------------------- |
|
||||
| `type` | ✓ | ✓ | `qq`,`group` | 推荐好友或群 |
|
||||
| `id` | ✓ | ✓ | - | 被推荐人的 QQ 号或群号 |
|
||||
|
||||
```php
|
||||
/**
|
||||
* @CQCommand("我的名片")
|
||||
*/
|
||||
public function myCard() {
|
||||
ctx()->reply(CQ::contact("qq", ctx()->getUserId()));
|
||||
}
|
||||
```
|
||||
|
||||
### CQ::location() - 发送位置
|
||||
|
||||
发送位置,基于经纬度坐标发的。
|
||||
|
||||
定义:`CQ::location($lat, $lon, $title = "", $content = "")`
|
||||
|
||||
参数
|
||||
|
||||
| 参数名 | 收 | 发 | 可能的值 | 说明 |
|
||||
| --------- | ---- | ---- | -------- | -------------- |
|
||||
| `lat` | ✓ | ✓ | - | 纬度 |
|
||||
| `lon` | ✓ | ✓ | - | 经度 |
|
||||
| `title` | ✓ | ✓ | - | 可选,标题 |
|
||||
| `content` | ✓ | ✓ | - | 可选,内容描述 |
|
||||
|
||||
### CQ::music() - 音乐分享
|
||||
|
||||
分享音乐,通过卡片。
|
||||
|
||||
发送音乐分享卡片。此 CQ 码如果伴随着其他文字,则文字内容会被丢弃。
|
||||
|
||||
定义:`CQ::music($type, $id_or_url, $audio = null, $title = null, $content = null, $image = null)`
|
||||
|
||||
- `$type`: 发送类型
|
||||
- `$id_or_url`: 音乐的 id 或 音乐卡片点进去打开的链接
|
||||
- `$audio`: 音频文件的 HTTP 地址
|
||||
- `$title`: 音乐卡片的标题,建议 12 字以内
|
||||
- `$content`: 音乐卡片的简介内容(可选)
|
||||
- `$image`: 音乐卡片的图片的链接地址(可选)
|
||||
|
||||
如果 `$type` 参数为 `qq` 或 `163` 或 `xiami`,则必须且只和第二个参数 `$id_or_url` 配合使用。这三个为内置分享,需要先通过搜索功能获取对应平台歌曲的 id 后使用。
|
||||
|
||||
如果 `$type` 参数为 `custom`,则表明此音乐卡片为用户自定义,你可以根据自己的需要自定义卡片内容和音频。此时必须填写 `$id_or_url`, `$audio`, `$title` 三个参数。
|
||||
|
||||
```php
|
||||
ctx()->reply(CQ::music("163", "730806")); //一首我喜欢的歌
|
||||
// 以内置的发送类型发送音乐卡片,我这里挑了网易云音乐的一首歌。
|
||||
|
||||
ctx()->reply("custom", "https://baidu.com/", "https://zhamao.xin/file/hello.mp3", "我是Siri说出来的Hello", "不服来打我呀!", "https://zhamao.xin/file/hello.jpg");
|
||||
// 自定义整个卡片的每个内容
|
||||
```
|
||||
|
||||
### CQ::forward() - 合并转发
|
||||
|
||||
合并转发消息。
|
||||
|
||||
定义:`CQ::forward($id)`
|
||||
|
||||
参数
|
||||
|
||||
```
|
||||
[CQ:forward,id=123456]
|
||||
```
|
||||
|
||||
| 参数名 | 收 | 发 | 可能的值 | 说明 |
|
||||
| ------ | ---- | ---- | -------- | ------------------------------------------------------------ |
|
||||
| `id` | ✓ | | 必填 | 合并转发 ID,需通过 [`get_forward_msg` API](https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#get_forward_msg-获取合并转发消息) 获取具体内容 |
|
||||
|
||||
### CQ::node() - 合并转发自定义节点
|
||||
|
||||
接收时,此消息段不会直接出现在消息事件的 `message` 中,需通过 [`get_forward_msg` API](https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#get_forward_msg-获取合并转发消息) 获取。发送时,通过获取回来的 API 节点信息进行发送。
|
||||
|
||||
定义:`CQ::node($user_id, $nickname, $content)`
|
||||
|
||||
参数
|
||||
|
||||
| 参数名 | 收 | 发 | 可能的值 | 说明 |
|
||||
| ---------- | ---- | ---- | -------- | ------------------------------------------------------------ |
|
||||
| `user_id` | ✓ | ✓ | - | 发送者 QQ 号 |
|
||||
| `nickname` | ✓ | ✓ | - | 发送者昵称 |
|
||||
| `content` | ✓ | ✓ | - | 消息内容,支持发送消息时的 `message` 数据类型,见 [API 的参数](https://github.com/howmanybots/onebot/blob/master/v11/specs/api/#参数) |
|
||||
|
||||
```php
|
||||
/**
|
||||
* @CQCommand("node测试")
|
||||
*/
|
||||
public function nodeTest() {
|
||||
ctx()->reply(CQ::node(123456, "Jack", "[CQ:face,id=123]哈喽~"));
|
||||
}
|
||||
```
|
||||
|
||||
### CQ::xml() - XML 消息
|
||||
|
||||
发送 QQ 兼容的 XML 多媒体消息。
|
||||
|
||||
定义:`CQ::xml($data)`
|
||||
|
||||
参数:`$data` 为 xml 字符串
|
||||
|
||||
```php
|
||||
/**
|
||||
* @CQCommand("xml测试")
|
||||
*/
|
||||
public function xmlTest() {
|
||||
ctx()->reply(CQ::xml("<?xml ..."));
|
||||
}
|
||||
```
|
||||
|
||||
### CQ::json() - JSON 消息
|
||||
|
||||
发送 QQ 兼容的 JSON 多媒体消息。
|
||||
|
||||
定义:`CQ::json($data)`
|
||||
|
||||
参数同上,内含 JSON 字符串即可。
|
||||
|
||||
!!! tip "提示"
|
||||
|
||||
因为某些众所周知的原因,XML 和 JSON 的返回不提供实例,有兴趣的可以自行研究如何编写,文档不含任何相关教程。
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
# 框架组件
|
||||
|
||||
这里列举了框架内的你可能会用到的常用组件。
|
||||
@@ -1,790 +0,0 @@
|
||||
# 机器人 API(ZMRobot)
|
||||
|
||||
ZMRobot 类是封装好的 OneBot 标准的 API 接口调用类,可以在机器人连接后通过连接或者机器人 QQ 号获取对象并调用接口(如发送群消息、获取群列表等操作)。
|
||||
|
||||
| 属性项 | 属性值 | 备注 |
|
||||
| -------- | ---------------- | ------------------------------ |
|
||||
| 名称 | ZMRobot | |
|
||||
| 类型 | 实例化类 | `$r = new ZMRobot($conn)` |
|
||||
| 命名空间 | `ZM\API\ZMRobot` | 使用前先 `use ZM\API\ZMRobot;` |
|
||||
|
||||
## 属性
|
||||
|
||||
对象属性方法是对 API 的调整,例如是否以 `_async`、`_rate_limited` 后缀发送 API、设置协程返回还是异步返回结果等。
|
||||
|
||||
### ZMRobot::API_NORMAL
|
||||
|
||||
以默认(无后缀)方式请求 API。
|
||||
|
||||
### ZMRobot::API_ASYNC
|
||||
|
||||
以后缀 `_async` 方式异步请求 API。
|
||||
|
||||
### ZMRobot::API_RATE_LIMITED
|
||||
|
||||
以后缀 `_rate_limited` 方式请求 API。
|
||||
|
||||
## 方法
|
||||
|
||||
### setPrefix()
|
||||
|
||||
设置后缀。目前支持 `_async`、`_rate_limited`。
|
||||
|
||||
- **prefix**: `int` `默认:API_NORMAL`,可选 `ZMRobot::API_NORMAL`、`ZMRobot::API_ASYNC`、`ZMRobot::API_RATE_LIMITED`
|
||||
|
||||
设置后缀后,请求的 API 会发生变化。例如发送私聊消息:`sendPrivateMsg()`,请求的 API 为 `send_private_msg_async`,详见 [OneBot 文档](https://github.com/howmanybots/onebot/blob/master/v11/specs/api/README.md)。
|
||||
|
||||
### setCallback()
|
||||
|
||||
设置 API 结果返回方式。默认为 true,就是直接通过框架处理后接收回包直接返回给结果。如果为 false,则 API 请求后只返回是否成功推送出 WS 数据包。
|
||||
|
||||
### getSelfId()
|
||||
|
||||
获取当前对象的机器人 QQ 或 OneBot 实例的 ID。
|
||||
|
||||
```php
|
||||
$bot = ZMRobot::get(123456);
|
||||
echo $bot->getSelfId(); //123456
|
||||
```
|
||||
|
||||
### ZMRobot::get()
|
||||
|
||||
静态方法,用来通过机器人 QQ 或 OneBot 实例的 ID 获取 ZMRobot 对象。
|
||||
|
||||
参数:`$robot_id`,必填。
|
||||
|
||||
```php
|
||||
$r = ZMRobot::get(123456);
|
||||
$r->sendPrivateMsg(55555, "hello");
|
||||
```
|
||||
|
||||
### ZMRobot::getRandom()
|
||||
|
||||
静态方法,随机获取一个连接到框架的机器人(多个机器人实例连接到框架时适用)。
|
||||
|
||||
如果框架没有连接到任何机器人实例,则会抛出一个异常:`ZM\Exception\RobotNotFoundException`。
|
||||
|
||||
```php
|
||||
try {
|
||||
$bot = ZMRobot::getRandom();
|
||||
$bot->sendPrivateMsg(55555, "foo");
|
||||
} catch (\ZM\Exception\RobotNotFoundException $e) {
|
||||
echo "还没有机器人连接到框架!\n";
|
||||
}
|
||||
```
|
||||
|
||||
### ZMRobot::getAllRobot()
|
||||
|
||||
获取所有连接到框架的机器人的 ZMRobot 对象。
|
||||
|
||||
返回值:`ZMRobot[]`。
|
||||
|
||||
```php
|
||||
$all = ZMRobot::getAllRobot();
|
||||
foreach($all as $v) {
|
||||
$v->sendPrivateMsg(55555, "机器人轮流给一个人发消息啦!");
|
||||
}
|
||||
```
|
||||
|
||||
### __construct()
|
||||
|
||||
构造方法。
|
||||
|
||||
参数:`$connection`:炸毛框架内部的连接对象,必填参数。
|
||||
|
||||
```php
|
||||
//从上下文获取 Websocket 连接对象
|
||||
$conn = ctx()->getConnection();
|
||||
$bot = new ZMRobot($conn);
|
||||
```
|
||||
|
||||
## 返回结果处理
|
||||
|
||||
因为框架的机器人是兼容 OneBot 标准的(原 CQHTTP),所以每次接收发送 API 请求的结果都是大体一样的结构。我们以 `sendPrivateMsg()` 为例,因为发送出去的每一条消息都会在 OneBot 实例(如 CQHTTP 插件、go-cqhttp 等)中对应一个消息 ID,以供我们核查消息和后续撤回等操作需要。
|
||||
|
||||
```php
|
||||
$bot = ZMRobot::get("123456"); // 机器人QQ号
|
||||
$obj = $bot->sendGroupMsg("234567", "你好");
|
||||
echo json_encode($obj, 128|256);
|
||||
```
|
||||
|
||||
```json
|
||||
// 输出结果
|
||||
{
|
||||
"status": "ok",
|
||||
"retcode": 0,
|
||||
"data": {
|
||||
"message_id": 1243
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
如上,`$obj` 就是我们的回包内容,我们通过调用 `sendPrivateMsg()` 这个 API 后,会拿到机器人发送此条消息的消息 ID,然后可以通过它来进行其他操作(例如撤回)。
|
||||
|
||||
```php
|
||||
$result = $bot->deleteMsg($obj["data"]["message_id"]);
|
||||
vardump($result["retcode"]); //如果成功撤回,输出 int(0)
|
||||
```
|
||||
|
||||
### 状态码和 Data
|
||||
|
||||
状态码一般情况成功都是 0 或者 200,在过去,炸毛框架兼容 CQHTTP 插件时,错误码的标准按照 CYKU 给出的标准编写,不同的 OneBot 标准的实现,可能有不同的数值,需要根据你对接的机器人客户端进行适配。
|
||||
|
||||
结果中返回的 `data` 字段根据下方不同 API 的调用而不同,具体查看每个 API 写明的 `响应数据` 表格。
|
||||
|
||||
### response 表
|
||||
|
||||
| 字段名 | 数据类型 | 默认值 | 说明 |
|
||||
| --------- | -------- | ---------- | ----------------------- |
|
||||
| `status` | String | "ok" | 状态码说明 |
|
||||
| `retcode` | number | 0 | 返回状态码 |
|
||||
| `data` | array | 见 data 表 | 根据不同的 API 返回不同 |
|
||||
|
||||
## 机器人 API 方法
|
||||
|
||||
### sendPrivateMsg()
|
||||
|
||||
参数
|
||||
|
||||
| 字段名 | 数据类型 | 默认值 | 说明 |
|
||||
| ------------- | -------- | ------- | ------------------------------------------------------------ |
|
||||
| `user_id` | number | - | 对方 QQ 号 |
|
||||
| `message` | message | - | 要发送的内容 |
|
||||
| `auto_escape` | boolean | `false` | 消息内容是否作为纯文本发送(即不解析 CQ 码),只在 `message` 字段是字符串时有效 |
|
||||
|
||||
响应数据
|
||||
|
||||
| 字段名 | 数据类型 | 说明 |
|
||||
| ------------ | -------------- | ------- |
|
||||
| `message_id` | number (int32) | 消息 ID |
|
||||
|
||||
例子
|
||||
|
||||
=== "代码"
|
||||
|
||||
```php
|
||||
$bot = ZMRobot::get(123456); // 123456是你的机器人QQ
|
||||
$bot->sendPrivateMsg("627577391", "你好啊!你好你好!");
|
||||
```
|
||||
|
||||
=== "效果"
|
||||
|
||||
<chat-box>
|
||||
( 你好啊!你好你好!
|
||||
</chat-box>
|
||||
|
||||
|
||||
### sendGroupMsg()
|
||||
|
||||
发送群组消息。
|
||||
|
||||
参数
|
||||
|
||||
| 字段名 | 数据类型 | 默认值 | 说明 |
|
||||
| ------------- | -------- | ------- | ------------------------------------------------------------ |
|
||||
| `group_id` | number | - | 群号 |
|
||||
| `message` | message | - | 要发送的内容 |
|
||||
| `auto_escape` | boolean | `false` | 消息内容是否作为纯文本发送(即不解析 CQ 码),只在 `message` 字段是字符串时有效 |
|
||||
|
||||
响应数据
|
||||
|
||||
| 字段名 | 数据类型 | 说明 |
|
||||
| ------------ | -------------- | ------- |
|
||||
| `message_id` | number (int32) | 消息 ID |
|
||||
|
||||
### sendMsg()
|
||||
|
||||
发送消息。
|
||||
|
||||
参数
|
||||
|
||||
| 字段名 | 数据类型 | 默认值 | 说明 |
|
||||
| -------------- | -------- | ------- | ------------------------------------------------------------ |
|
||||
| `message_type` | string | - | 消息类型,支持 `private`、`group`、`discuss`,分别对应私聊、群组、讨论组,如不传入,则根据传入的 `*_id` 参数判断 |
|
||||
| `target_id` | number | - | 目标号码,如 QQ 号,群号,讨论组号 |
|
||||
| `message` | message | - | 要发送的内容 |
|
||||
| `auto_escape` | boolean | `false` | 消息内容是否作为纯文本发送(即不解析 CQ 码),只在 `message` 字段是字符串时有效 |
|
||||
|
||||
响应数据
|
||||
|
||||
| 字段名 | 数据类型 | 说明 |
|
||||
| ------------ | -------------- | ------- |
|
||||
| `message_id` | number (int32) | 消息 ID |
|
||||
|
||||
### deleteMsg()
|
||||
|
||||
撤回消息。
|
||||
|
||||
参数
|
||||
|
||||
| 字段名 | 数据类型 | 默认值 | 说明 |
|
||||
| ------------ | -------------- | ------ | ------- |
|
||||
| `message_id` | number (int32) | - | 消息 ID |
|
||||
|
||||
响应数据:无
|
||||
|
||||
### getMsg()
|
||||
|
||||
获取消息。
|
||||
|
||||
参数
|
||||
|
||||
| 字段名 | 数据类型 | 默认值 | 说明 |
|
||||
| --------- | -------- | ------ | -------------------------------- |
|
||||
| `message_id` | number | - | 消息 ID |
|
||||
|
||||
响应数据
|
||||
|
||||
| 字段名 | 数据类型 | 说明 |
|
||||
| -------------- | -------------- | ------------------------------------------------------------ |
|
||||
| `time` | number (int32) | 发送时间 |
|
||||
| `message_type` | string | 消息类型,同 [消息事件](https://github.com/howmanybots/onebot/blob/master/v11/specs/event/message.md) |
|
||||
| `message_id` | number (int32) | 消息 ID |
|
||||
| `real_id` | number (int32) | 消息真实 ID |
|
||||
| `sender` | object | 发送人信息,同 [消息事件](https://github.com/howmanybots/onebot/blob/master/v11/specs/event/message.md) |
|
||||
| `message` | message | 消息内容 |
|
||||
|
||||
### getForwardMsg()
|
||||
|
||||
获取合并转发消息。
|
||||
|
||||
参数
|
||||
|
||||
| 字段名 | 数据类型 | 说明 |
|
||||
| ------ | -------- | ----------- |
|
||||
| `id` | string | 合并转发 ID |
|
||||
|
||||
响应数据
|
||||
|
||||
| 字段名 | 类型 | 说明 |
|
||||
| --------- | ------- | ------------------------------------------------------------ |
|
||||
| `message` | message | 消息内容,使用 [消息的数组格式](https://github.com/howmanybots/onebot/blob/master/v11/specs/message/array.md) 表示,数组中的消息段全部为 [`node` 消息段](https://github.com/howmanybots/onebot/blob/master/v11/specs/message/segment.md#合并转发自定义节点) |
|
||||
|
||||
### sendLike()
|
||||
|
||||
发送好友赞。
|
||||
|
||||
参数
|
||||
|
||||
| 字段名 | 数据类型 | 默认值 | 说明 |
|
||||
| --------- | -------- | ------ | -------------------------------- |
|
||||
| `user_id` | number | - | 对方 QQ 号 |
|
||||
| `times` | number | 1 | 赞的次数,每个好友每天最多 10 次 |
|
||||
|
||||
响应数据
|
||||
|
||||
无
|
||||
|
||||
### setGroupKick()
|
||||
|
||||
群组踢人。
|
||||
|
||||
参数
|
||||
|
||||
| 字段名 | 数据类型 | 默认值 | 说明 |
|
||||
| -------------------- | -------- | ------- | ------------------ |
|
||||
| `group_id` | number | - | 群号 |
|
||||
| `user_id` | number | - | 要踢的 QQ 号 |
|
||||
| `reject_add_request` | boolean | `false` | 拒绝此人的加群请求 |
|
||||
|
||||
响应数据:无
|
||||
|
||||
### setGroupBan()
|
||||
|
||||
群组单人禁言。
|
||||
|
||||
参数
|
||||
|
||||
| 字段名 | 数据类型 | 默认值 | 说明 |
|
||||
| ---------- | -------- | --------- | -------------------------------- |
|
||||
| `group_id` | number | - | 群号 |
|
||||
| `user_id` | number | - | 要禁言的 QQ 号 |
|
||||
| `duration` | number | `30 * 60` | 禁言时长,单位秒,0 表示取消禁言 |
|
||||
|
||||
响应数据:无
|
||||
|
||||
### setGroupAnonymousBan()
|
||||
|
||||
群组匿名用户禁言。
|
||||
|
||||
参数
|
||||
|
||||
| 字段名 | 数据类型 | 默认值 | 说明 |
|
||||
| ------------------- | ---------------- | --------- | ------------------------------------------------------------ |
|
||||
| `group_id` | number | - | 群号 |
|
||||
| `anonymous_or_flag` | object 或 string | - | 要禁言的匿名用户对象(群消息上报的 `anonymous` 字段)或用户的 flag |
|
||||
| `duration` | number | `30 * 60` | 禁言时长,单位秒,无法取消匿名用户禁言 |
|
||||
|
||||
上面的 `anonymous_or_flag` 两者任选其一传入即可。
|
||||
|
||||
响应数据:无
|
||||
|
||||
### setGroupWholeBan()
|
||||
|
||||
群组全员禁言
|
||||
|
||||
参数
|
||||
|
||||
| 字段名 | 数据类型 | 默认值 | 说明 |
|
||||
| ---------- | -------- | ------ | -------- |
|
||||
| `group_id` | number | - | 群号 |
|
||||
| `enable` | boolean | `true` | 是否禁言 |
|
||||
|
||||
响应数据:无
|
||||
|
||||
### setGroupAdmin()
|
||||
|
||||
群组设置管理员
|
||||
|
||||
参数
|
||||
|
||||
| 字段名 | 数据类型 | 默认值 | 说明 |
|
||||
| ---------- | -------- | ------ | ------------------------- |
|
||||
| `group_id` | number | - | 群号 |
|
||||
| `user_id` | number | - | 要设置管理员的 QQ 号 |
|
||||
| `enable` | boolean | `true` | true 为设置,false 为取消 |
|
||||
|
||||
响应数据:无
|
||||
|
||||
### setGroupAnonymous()
|
||||
|
||||
群组匿名
|
||||
|
||||
参数
|
||||
|
||||
| 字段名 | 数据类型 | 默认值 | 说明 |
|
||||
| ---------- | -------- | ------ | ---------------- |
|
||||
| `group_id` | number | - | 群号 |
|
||||
| `enable` | boolean | `true` | 是否允许匿名聊天 |
|
||||
|
||||
响应数据:无
|
||||
|
||||
### setGroupCard()
|
||||
|
||||
设置群名片(群备注)
|
||||
|
||||
参数
|
||||
|
||||
| 字段名 | 数据类型 | 默认值 | 说明 |
|
||||
| ---------- | -------- | ------ | ---------------------------------------- |
|
||||
| `group_id` | number | - | 群号 |
|
||||
| `user_id` | number | - | 要设置的 QQ 号 |
|
||||
| `card` | string | 空 | 群名片内容,不填或空字符串表示删除群名片 |
|
||||
|
||||
响应数据:无
|
||||
|
||||
### setGroupName()
|
||||
|
||||
设置群名。
|
||||
|
||||
参数
|
||||
|
||||
| 字段名 | 数据类型 | 说明 |
|
||||
| ------------ | -------------- | ------ |
|
||||
| `group_id` | number (int64) | 群号 |
|
||||
| `group_name` | string | 新群名 |
|
||||
|
||||
响应数据:无
|
||||
|
||||
### setGroupLeave()
|
||||
|
||||
退出群组
|
||||
|
||||
参数
|
||||
|
||||
| 字段名 | 数据类型 | 默认值 | 说明 |
|
||||
| ------------ | -------- | ------- | -------------------------------------------------------- |
|
||||
| `group_id` | number | - | 群号 |
|
||||
| `is_dismiss` | boolean | `false` | 是否解散,如果登录号是群主,则仅在此项为 true 时能够解散 |
|
||||
|
||||
响应数据:无
|
||||
|
||||
### setGroupSpecialTitle()
|
||||
|
||||
设置群组专属头衔
|
||||
|
||||
参数
|
||||
|
||||
| 字段名 | 数据类型 | 默认值 | 说明 |
|
||||
| --------------- | -------- | ------ | ------------------------------------------------------------ |
|
||||
| `group_id` | number | - | 群号 |
|
||||
| `user_id` | number | - | 要设置的 QQ 号 |
|
||||
| `special_title` | string | 空 | 专属头衔,不填或空字符串表示删除专属头衔 |
|
||||
| `duration` | number | `-1` | 专属头衔有效期,单位秒,-1 表示永久,不过此项似乎没有效果,可能是只有某些特殊的时间长度有效,有待测试 |
|
||||
|
||||
响应数据:无
|
||||
|
||||
### setFriendAddRequest()
|
||||
|
||||
处理加好友请求
|
||||
|
||||
参数
|
||||
|
||||
| 字段名 | 数据类型 | 默认值 | 说明 |
|
||||
| --------- | -------- | ------ | ----------------------------------------- |
|
||||
| `flag` | string | - | 加好友请求的 flag(需从上报的数据中获得) |
|
||||
| `approve` | boolean | `true` | 是否同意请求 |
|
||||
| `remark` | string | 空 | 添加后的好友备注(仅在同意时有效) |
|
||||
|
||||
响应数据:无
|
||||
|
||||
### setGroupAddRequest()
|
||||
|
||||
处理加群请求 / 邀请
|
||||
|
||||
参数
|
||||
|
||||
| 字段名 | 数据类型 | 默认值 | 说明 |
|
||||
| ---------- | -------- | ------ | ------------------------------------------------------------ |
|
||||
| `flag` | string | - | 加群请求的 flag(需从上报的数据中获得) |
|
||||
| `sub_type` | string | - | `add` 或 `invite`,请求类型(需要和上报消息中的 `sub_type` 字段相符) |
|
||||
| `approve` | boolean | `true` | 是否同意请求/邀请 |
|
||||
| `reason` | string | 空 | 拒绝理由(仅在拒绝时有效) |
|
||||
|
||||
响应数据无
|
||||
|
||||
### getLoginInfo()
|
||||
|
||||
获取登录号信息
|
||||
|
||||
参数:无
|
||||
|
||||
响应数据
|
||||
|
||||
| 字段名 | 数据类型 | 说明 |
|
||||
| ---------- | -------------- | ------- |
|
||||
| `user_id` | number (int64) | QQ 号 |
|
||||
| `nickname` | string | QQ 昵称 |
|
||||
|
||||
### getStrangerInfo()
|
||||
|
||||
获取陌生人信息
|
||||
|
||||
参数
|
||||
|
||||
| 字段名 | 数据类型 | 默认值 | 说明 |
|
||||
| ---------- | -------- | ------- | ---------------------------------------------------- |
|
||||
| `user_id` | number | - | QQ 号 |
|
||||
| `no_cache` | boolean | `false` | 是否不使用缓存(使用缓存可能更新不及时,但响应更快) |
|
||||
|
||||
响应数据
|
||||
|
||||
| 字段名 | 数据类型 | 说明 |
|
||||
| ---------- | -------------- | ------------------------------------- |
|
||||
| `user_id` | number (int64) | QQ 号 |
|
||||
| `nickname` | string | 昵称 |
|
||||
| `sex` | string | 性别,`male` 或 `female` 或 `unknown` |
|
||||
| `age` | number (int32) | 年龄 |
|
||||
|
||||
### getFriendList()
|
||||
|
||||
获取好友列表
|
||||
|
||||
参数:无
|
||||
|
||||
响应数据
|
||||
|
||||
响应内容为 JSON 数组,每个元素如下:
|
||||
|
||||
| 字段名 | 数据类型 | 说明 |
|
||||
| ---------- | -------------- | ------ |
|
||||
| `user_id` | number (int64) | QQ 号 |
|
||||
| `nickname` | string | 昵称 |
|
||||
| `remark` | string | 备注名 |
|
||||
|
||||
### getGroupInfo()
|
||||
|
||||
获取群信息
|
||||
|
||||
参数
|
||||
|
||||
| 字段名 | 数据类型 | 默认值 | 说明 |
|
||||
| ---------- | -------- | ------- | ---------------------------------------------------- |
|
||||
| `group_id` | number | - | 群号 |
|
||||
| `no_cache` | boolean | `false` | 是否不使用缓存(使用缓存可能更新不及时,但响应更快) |
|
||||
|
||||
响应数据
|
||||
|
||||
| 字段名 | 数据类型 | 说明 |
|
||||
| ------------------ | -------------- | -------------------- |
|
||||
| `group_id` | number (int64) | 群号 |
|
||||
| `group_name` | string | 群名称 |
|
||||
| `member_count` | number (int32) | 成员数 |
|
||||
| `max_member_count` | number (int32) | 最大成员数(群容量) |
|
||||
|
||||
### getGroupList()
|
||||
|
||||
获取群列表
|
||||
|
||||
参数:无
|
||||
|
||||
响应数据
|
||||
|
||||
响应内容为 JSON 数组,每个元素如下:
|
||||
|
||||
| 字段名 | 数据类型 | 说明 |
|
||||
| ------------ | -------------- | ------ |
|
||||
| `group_id` | number (int64) | 群号 |
|
||||
| `group_name` | string | 群名称 |
|
||||
|
||||
### getGroupMemberInfo()
|
||||
|
||||
获取群成员信息
|
||||
|
||||
参数
|
||||
|
||||
| 字段名 | 数据类型 | 默认值 | 说明 |
|
||||
| ---------- | -------- | ------- | ---------------------------------------------------- |
|
||||
| `group_id` | number | - | 群号 |
|
||||
| `user_id` | number | - | QQ 号 |
|
||||
| `no_cache` | boolean | `false` | 是否不使用缓存(使用缓存可能更新不及时,但响应更快) |
|
||||
|
||||
响应数据
|
||||
|
||||
| 字段名 | 数据类型 | 说明 |
|
||||
| ------------------- | -------------- | ------------------------------------- |
|
||||
| `group_id` | number (int64) | 群号 |
|
||||
| `user_id` | number (int64) | QQ 号 |
|
||||
| `nickname` | string | 昵称 |
|
||||
| `card` | string | 群名片/备注 |
|
||||
| `sex` | string | 性别,`male` 或 `female` 或 `unknown` |
|
||||
| `age` | number (int32) | 年龄 |
|
||||
| `area` | string | 地区 |
|
||||
| `join_time` | number (int32) | 加群时间戳 |
|
||||
| `last_sent_time` | number (int32) | 最后发言时间戳 |
|
||||
| `level` | string | 成员等级 |
|
||||
| `role` | string | 角色,`owner` 或 `admin` 或 `member` |
|
||||
| `unfriendly` | boolean | 是否不良记录成员 |
|
||||
| `title` | string | 专属头衔 |
|
||||
| `title_expire_time` | number (int32) | 专属头衔过期时间戳 |
|
||||
| `card_changeable` | boolean | 是否允许修改群名片 |
|
||||
|
||||
### getGroupMemberList()
|
||||
|
||||
获取群成员列表
|
||||
|
||||
参数
|
||||
|
||||
| 字段名 | 数据类型 | 默认值 | 说明 |
|
||||
| ---------- | -------- | ------ | ---- |
|
||||
| `group_id` | number | - | 群号 |
|
||||
|
||||
响应数据
|
||||
|
||||
响应内容为 JSON 数组,每个元素的内容和上面的 `/get_group_member_info` 接口相同,但对于同一个群组的同一个成员,获取列表时和获取单独的成员信息时,某些字段可能有所不同,例如 `area`、`title` 等字段在获取列表时无法获得,具体应以单独的成员信息为准。
|
||||
|
||||
### getGroupHonorInfo()
|
||||
|
||||
获取群荣誉信息。
|
||||
|
||||
参数
|
||||
|
||||
| 字段名 | 数据类型 | 默认值 | 说明 |
|
||||
| ---------- | -------------- | ------ | ------------------------------------------------------------ |
|
||||
| `group_id` | number (int64) | - | 群号 |
|
||||
| `type` | string | - | 要获取的群荣誉类型,可传入 `talkative` `performer` `legend` `strong_newbie` `emotion` 以分别获取单个类型的群荣誉数据,或传入 `all` 获取所有数据 |
|
||||
|
||||
响应数据
|
||||
|
||||
| 字段名 | 数据类型 | 说明 |
|
||||
| -------------------- | -------------- | ---------------------------------------------------------- |
|
||||
| `group_id` | number (int64) | 群号 |
|
||||
| `current_talkative` | object | 当前龙王,仅 `type` 为 `talkative` 或 `all` 时有数据 |
|
||||
| `talkative_list` | array | 历史龙王,仅 `type` 为 `talkative` 或 `all` 时有数据 |
|
||||
| `performer_list` | array | 群聊之火,仅 `type` 为 `performer` 或 `all` 时有数据 |
|
||||
| `legend_list` | array | 群聊炽焰,仅 `type` 为 `legend` 或 `all` 时有数据 |
|
||||
| `strong_newbie_list` | array | 冒尖小春笋,仅 `type` 为 `strong_newbie` 或 `all` 时有数据 |
|
||||
| `emotion_list` | array | 快乐之源,仅 `type` 为 `emotion` 或 `all` 时有数据 |
|
||||
|
||||
其中 `current_talkative` 字段的内容如下:
|
||||
|
||||
| 字段名 | 数据类型 | 说明 |
|
||||
| ----------- | -------------- | -------- |
|
||||
| `user_id` | number (int64) | QQ 号 |
|
||||
| `nickname` | string | 昵称 |
|
||||
| `avatar` | string | 头像 URL |
|
||||
| `day_count` | number (int32) | 持续天数 |
|
||||
|
||||
其它各 `*_list` 的每个元素是一个 JSON 对象,内容如下:
|
||||
|
||||
| 字段名 | 数据类型 | 说明 |
|
||||
| ------------- | -------------- | -------- |
|
||||
| `user_id` | number (int64) | QQ 号 |
|
||||
| `nickname` | string | 昵称 |
|
||||
| `avatar` | string | 头像 URL |
|
||||
| `description` | string | 荣誉描述 |
|
||||
|
||||
### getCookies()
|
||||
|
||||
获取 Cookies。
|
||||
|
||||
!!! warning "注意"
|
||||
|
||||
目前开源的 mirai 为底层的机器人客户端均不支持获取 Cookies 和 CSRF Token,包括 go-cqhttp。
|
||||
|
||||
|
||||
参数
|
||||
|
||||
| 字段名 | 数据类型 | 默认值 | 说明 |
|
||||
| -------- | -------- | ------ | ----------------------- |
|
||||
| `domain` | string | 空 | 需要获取 cookies 的域名 |
|
||||
|
||||
响应数据
|
||||
|
||||
| 字段名 | 数据类型 | 说明 |
|
||||
| --------- | -------- | ------- |
|
||||
| `cookies` | string | Cookies |
|
||||
|
||||
### getCsrfToken()
|
||||
|
||||
获取 CSRF Token
|
||||
|
||||
参数:无
|
||||
|
||||
响应数据
|
||||
|
||||
| 字段名 | 数据类型 | 说明 |
|
||||
| ------- | -------------- | ---------- |
|
||||
| `token` | number (int32) | CSRF Token |
|
||||
|
||||
### getCredentials()
|
||||
|
||||
获取 QQ 相关接口凭证,即上面两个合并。
|
||||
|
||||
参数
|
||||
|
||||
| 字段名 | 数据类型 | 默认值 | 说明 |
|
||||
| -------- | -------- | ------ | ----------------------- |
|
||||
| `domain` | string | 空 | 需要获取 cookies 的域名 |
|
||||
|
||||
响应数据
|
||||
|
||||
| 字段名 | 数据类型 | 说明 |
|
||||
| ------------ | -------------- | ---------- |
|
||||
| `cookies` | string | Cookies |
|
||||
| `csrf_token` | number (int32) | CSRF Token |
|
||||
|
||||
### getRecord()
|
||||
|
||||
获取语音。其实并不是真的获取语音,而是转换语音到指定的格式。
|
||||
|
||||
> **提示**:要使用此接口,通常需要安装 ffmpeg,请参考 OneBot 实现的相关说明。
|
||||
|
||||
参数
|
||||
|
||||
| 字段名 | 数据类型 | 默认值 | 说明 |
|
||||
| ------------ | -------- | ------ | ------------------------------------------------------------ |
|
||||
| `file` | string | - | 收到的语音文件名(CQ 码的 `file` 参数),如 `0B38145AA44505000B38145AA4450500.silk` |
|
||||
| `out_format` | string | - | 要转换到的格式,目前支持 `mp3`、`amr`、`wma`、`m4a`、`spx`、`ogg`、`wav`、`flac` |
|
||||
|
||||
响应数据
|
||||
|
||||
| 字段名 | 数据类型 | 说明 |
|
||||
| ------ | -------- | ------------------------------------------------------------ |
|
||||
| `file` | string | 转换后的语音文件路径,如 `/home/somebody/cqhttp/data/record/0B38145AA44505000B38145AA4450500.mp3` |
|
||||
|
||||
### getImage()
|
||||
|
||||
获取图片。
|
||||
|
||||
参数
|
||||
|
||||
| 字段名 | 数据类型 | 默认值 | 说明 |
|
||||
| ------ | -------- | ------ | ------------------------------------------------------------ |
|
||||
| `file` | string | - | 收到的图片文件名(CQ 码的 `file` 参数),如 `6B4DE3DFD1BD271E3297859D41C530F5.jpg` |
|
||||
|
||||
响应数据
|
||||
|
||||
| 字段名 | 数据类型 | 说明 |
|
||||
| ------ | -------- | ------------------------------------------------------------ |
|
||||
| `file` | string | 下载后的图片文件路径,如 `/home/somebody/cqhttp/data/image/6B4DE3DFD1BD271E3297859D41C530F5.jpg` |
|
||||
|
||||
### canSendImage()
|
||||
|
||||
检查是否可以发送图片。
|
||||
|
||||
参数:无
|
||||
|
||||
响应数据
|
||||
|
||||
| 字段名 | 数据类型 | 说明 |
|
||||
| ------ | -------- | ------ |
|
||||
| `yes` | boolean | 是或否 |
|
||||
|
||||
### canSendRecord()
|
||||
|
||||
检查是否可以发送语音,返回同上。
|
||||
|
||||
### getStatus()
|
||||
|
||||
获取插件运行状态。
|
||||
|
||||
参数:无
|
||||
|
||||
响应数据
|
||||
|
||||
| 字段名 | 数据类型 | 说明 |
|
||||
| -------- | -------- | -------------------------------------------------------- |
|
||||
| `online` | boolean | 当前 QQ 在线,`null` 表示无法查询到在线状态 |
|
||||
| `good` | boolean | 状态符合预期,意味着各模块正常运行、功能正常,且 QQ 在线 |
|
||||
| ...... | - | OneBot 实例自行添加的其他内容 |
|
||||
|
||||
通常情况下建议只使用 `online` 和 `good` 这两个字段来判断运行状态,因为根据 OneBot 实现的不同,其它字段可能完全不同。
|
||||
|
||||
### getVersionInfo()
|
||||
|
||||
获取版本信息
|
||||
|
||||
响应数据
|
||||
|
||||
| 字段名 | 数据类型 | 说明 |
|
||||
| ------------------ | -------- | ----------------------------- |
|
||||
| `app_name` | string | 应用标识,如 `mirai-native` |
|
||||
| `app_version` | string | 应用版本,如 `1.2.3` |
|
||||
| `protocol_version` | string | OneBot 标准版本,如 `v11` |
|
||||
| …… | - | OneBot 实现自行添加的其它内容 |
|
||||
|
||||
### setRestartPlugin()
|
||||
|
||||
重启 OneBot 客户端
|
||||
|
||||
由于重启 OneBot 实现同时需要重启 API 服务,这意味着当前的 API 请求会被中断,因此需要异步地重启,接口返回的 `status` 是 `async`。
|
||||
|
||||
参数
|
||||
|
||||
| 字段名 | 数据类型 | 默认值 | 说明 |
|
||||
| ------- | -------- | ------ | ------------------------------------------------------------ |
|
||||
| `delay` | number | `0` | 要延迟的毫秒数,如果默认情况下无法重启,可以尝试设置延迟为 2000 左右 |
|
||||
|
||||
响应数据:无
|
||||
|
||||
### cleanCache()
|
||||
|
||||
清理 OneBot 客户端的缓存。
|
||||
|
||||
参数:无
|
||||
|
||||
响应数据:无
|
||||
|
||||
### callExtendedAPI() (扩充 API)
|
||||
|
||||
用来调用 OneBot 标准之外扩展出来的自定义 API。
|
||||
|
||||
使用不同 OneBot 客户端时,可能有一些 API 不在上方的 OneBot 标准里,这时可以使用此方法进行额外调用。
|
||||
|
||||
参数
|
||||
|
||||
| 字段名 | 数据类型 | 默认值 |
|
||||
| -------- | -------- | ------ |
|
||||
| `action` | string | 必填 |
|
||||
| `params` | array | `[]` |
|
||||
|
||||
例子
|
||||
|
||||
```php
|
||||
$result = $bot->callExtendedAPI("get_group_root_files", ["group_id" => 123456]);
|
||||
//这里以 go-cqhttp 扩展的一个获取群文件的 API 为例
|
||||
var_dump($result["data"]);
|
||||
// 输出群文件列表
|
||||
```
|
||||
|
||||
25
docs/event/README.md
Normal file
@@ -0,0 +1,25 @@
|
||||
# 入门
|
||||
|
||||
## 事件是什么
|
||||
|
||||
简单来说,事件是一个底层的 Event Loop 收到消息后调用对应的方法的一个模型,比如给机器人发送消息后框架会调用你定义的方法来执行你的业务代码。
|
||||
|
||||
## 属性和注解是什么
|
||||
|
||||
属性(Attribute)是 PHP 8 最大的新变化之一,是 PHP 官方支持的、内置的注解实现,允许我们通过编程方式获取对应的元数据,可以大大方便我们对某一类代码进行处理。
|
||||
|
||||
而注解(Annotation)则是在 PHP 尚未支持属性的时代,用来代替的社区实现方案,通过解析 PHPDoc 注释来实现自己的注解机制。
|
||||
|
||||
炸毛框架同时支持注解和属性,在文档当中,有时会混用两者的字眼,在大多数情况下都可以安全地交换使用,例如 `#[BotEvent]` 和 `@BotEvent` 的行为是完全一致的。
|
||||
|
||||
## 注解和事件的关系
|
||||
|
||||
在炸毛框架中,注解是事件分发的一个重要角色,但注解本身并非事件,更恰当地说,注解代表了事件。
|
||||
|
||||
无论是机器人开发过程中场景的 `#[BotCommand]` 或是 HTTP 服务的路由 `#[RequestMapping]` 都是注解代表事件的例子。
|
||||
|
||||
## 阻断事件分发
|
||||
|
||||
在炸毛框架中,事件由一个统一的事件分发器进行分发,你可以在任意事件中阻断所有后续的分发。
|
||||
|
||||
(待考)
|
||||
86
docs/event/bot.md
Normal file
@@ -0,0 +1,86 @@
|
||||
# 机器人事件
|
||||
|
||||
<aside>
|
||||
🛰️ 此页面下的所有注解命名空间为 `ZM\Annotation\OneBot`
|
||||
|
||||
</aside>
|
||||
|
||||
> 在使用注解绑定事件时,如果不存在 **必需** 参数,可一个参数都不写,效果就是此事件在任何情况下都会调用此方法,例如 `#[BotEvent()]` 会在收到任意机器人事件时调用。
|
||||
>
|
||||
|
||||
## BotAction
|
||||
|
||||
啊?
|
||||
|
||||
| 参数名称 | 允许值 | 用途 | 默认 |
|
||||
| --- | --- | --- | --- |
|
||||
| action | string | 动作名称 | “” |
|
||||
| need_response | string | 动作是否需要响应 | false |
|
||||
| level | int | 事件优先级(越大越先执行) | 20 |
|
||||
|
||||
## BotActionResponse
|
||||
|
||||
啊??
|
||||
|
||||
| 参数名称 | 允许值 | 用途 | 默认 |
|
||||
| --- | --- | --- | --- |
|
||||
| retcode | int | 响应码 | null |
|
||||
| level | int | 事件优先级(越大越先执行) | 20 |
|
||||
|
||||
## BotEvent
|
||||
|
||||
用于处理所有的机器人事件,具体的参数含义可以参见 [https://12.onebot.dev/connect/data-protocol/event/](https://12.onebot.dev/connect/data-protocol/event/)。
|
||||
|
||||
| 参数名称 | 允许值 | 用途 | 默认 |
|
||||
| --- | --- | --- | --- |
|
||||
| type | string | 对应标准中的事件类型 | null |
|
||||
| detail_type | string | 对应标准中的事件详细类型 | null |
|
||||
| sub_type | string | 对应标准中的事件子类型 | null |
|
||||
| level | int | 事件优先级(越大越先执行) | 20 |
|
||||
|
||||
## BotCommand
|
||||
|
||||
对于 `BotEvent` 的封装,用于支持常用的命令式调用(如:”天气 深圳”)。
|
||||
|
||||
| 参数名称 | 允许值 | 用途 | 默认 |
|
||||
| --- | --- | --- | --- |
|
||||
| name | string | 命令名称,应全局唯一 | “” |
|
||||
| match | string | 匹配第一个词的命令式消息,如 天气 北京 中的 天气 | “” |
|
||||
| pattern | string | 根据 * 号通配符进行模式匹配用户消息,如 查询*天气 | “” |
|
||||
| regex | string 合法的正则表达式 | 匹配正则表达式匹配到的用户消息 | “” |
|
||||
| start_with | string | 匹配消息开头相匹配的消息,如 我叫炸毛,这里写 我叫 | “” |
|
||||
| end_with | string | 匹配消息结尾相匹配的消息,以 start_with 类推 | “” |
|
||||
| keyword | string | 匹配消息中有相关关键词的消息 | “” |
|
||||
| alias | string | match 匹配到命令的别名,数组形式 | [] |
|
||||
| detail_type | string | 限定消息事件的详细类型,见 BotEvent | “” |
|
||||
| prefix | string | | |
|
||||
| level | int | 事件优先级(越大越先执行) | 20 |
|
||||
|
||||
> 机器人命令注册的实例可参见【一堆例子链接】
|
||||
>
|
||||
|
||||
## CommandArgument
|
||||
|
||||
与 BotCommand 搭配使用,可以自动识别参数及生成帮助。
|
||||
|
||||
| 参数名称 | 允许值 | 用途 | 默认 |
|
||||
| --- | --- | --- | --- |
|
||||
| name | string | 参数名称 | “” |
|
||||
| description | string | 参数描述 | “” |
|
||||
| required | bool | 参数是否必需 | false |
|
||||
| prompt | string | 当参数缺失时请求用户输入时提示的消息(需要参数设为必需) | “请输入{$name}” |
|
||||
| default | mixed | 当参数非必需且未填入时的默认值 | null |
|
||||
| timeout | int 单位秒 | 请求输入超时时间 | 60 |
|
||||
|
||||
## CommandHelp
|
||||
|
||||
与 BotCommand 搭配使用,可以补充生成的帮助信息。
|
||||
|
||||
| 参数名称 | 允许值 | 用途 | 默认 |
|
||||
| --- | --- | --- | --- |
|
||||
| description | string | 命令描述 | “” |
|
||||
| usage | string | 命令用法 | “” |
|
||||
| example | string | 命令示例 | “” |
|
||||
|
||||
> 关于自动帮助生成的更多信息,请参见 【这里链接】
|
||||
>
|
||||
@@ -1,3 +0,0 @@
|
||||
# 自定义注解
|
||||
|
||||
TODO:师傅,莫催,快肝完了!
|
||||
@@ -1,119 +0,0 @@
|
||||
# 事件分发器(进阶)
|
||||
|
||||
事件分发器是以上所有注解事件执行函数的一个分发器,如果你在上一章已经学会了如何创建自定义注解,那么本章就来说明如何用内置的事件分发器进行分发自定义事件。
|
||||
|
||||
如果你不需要了解或自定义有关事件分发的功能,此处可无需阅读。
|
||||
|
||||
## 属性
|
||||
|
||||
- 类名:`ZM\Event\EventDispatcher`
|
||||
|
||||
## 方法
|
||||
|
||||
### EventDispatcher::interrupt()
|
||||
|
||||
阻断当前正在运行的事件,只能在事件内部被调用的函数中实现。
|
||||
|
||||
### __construct()
|
||||
|
||||
构造方法。
|
||||
|
||||
```php
|
||||
EventDispatcher::__construct(string $class = '')
|
||||
```
|
||||
|
||||
初始化一个事件分发器,可进行一系列设置,对事件分发做限定。
|
||||
|
||||
#### 参数
|
||||
|
||||
`$class`:设置要分发的事件对应的注解类名,支持自定义注解(例如 `CQMessage::class`)
|
||||
|
||||
### setRuleFunction()
|
||||
|
||||
设置函数触发规则判定的函数(就是在执行事件函数前执行的规则判定)
|
||||
|
||||
```php
|
||||
setRuleFunction(callable $rule = null)
|
||||
```
|
||||
|
||||
#### 参数
|
||||
|
||||
`$rule`:支持回调或闭包。闭包的参数为执行对应事件函数所绑定的注解事件对象。
|
||||
|
||||
```php
|
||||
$dispatcher = new EventDispatcher(CustomEvent::class);
|
||||
$dispatcher->setRuleFunction(function($obj) {
|
||||
return $obj->name == "zhamao" ? true : false;
|
||||
});
|
||||
```
|
||||
|
||||
上方的 `$obj` 就是 CustomEvent 类的实例,参数绑定为注解中对应的参数。
|
||||
|
||||
### setResultFunction()
|
||||
|
||||
设置事件函数返回值处理的回调函数。
|
||||
|
||||
```php
|
||||
setReturnFunction(callable $return_func)
|
||||
```
|
||||
|
||||
#### 参数
|
||||
|
||||
`$return_func`:设置事件函数返回值处理的回调函数,回调参数绑定为对应单独事件函数的返回值。
|
||||
|
||||
```php
|
||||
$dispatcher = new EventDispatcher(CustomEvent::class);
|
||||
$dispatcher->setReturnFunction(function($return) {
|
||||
if (is_string($return)) Console::info("函数返回了 ".$return);
|
||||
});
|
||||
```
|
||||
|
||||
### dispatchEvents()
|
||||
|
||||
开始分发事件。
|
||||
|
||||
```php
|
||||
dispatchEvents(...$params)
|
||||
```
|
||||
|
||||
#### 参数
|
||||
|
||||
自定义参数,这里填入的参数将被填入被分发的函数参数中。
|
||||
|
||||
```php
|
||||
$dispatcher->dispatchEvents("foo", "bar");
|
||||
```
|
||||
|
||||
```php
|
||||
<?php
|
||||
class Test {
|
||||
/**
|
||||
* @CustomEvent("zhamao")
|
||||
*/
|
||||
public function test($arg1, $arg2) {
|
||||
echo "$arg1: $arg2"; //将输出 "foo: bar"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 机制
|
||||
|
||||
事件分发器的机制说简单不简单,说复杂也不复杂,它和中间件有着非常大的关系,因为它会自动检测和识别所要执行的函数有没有中间件,并且根据顺序进行执行。
|
||||
|
||||
在炸毛框架内部,一个完整的事件流程和中间件的关系如下图:
|
||||
|
||||

|
||||
|
||||
对于同一事件的优先级和响应顺序,优先级的关系如下图:
|
||||
|
||||

|
||||
|
||||
对于事件内单个事件被调用的单个函数下如果存在多个中间件,中间件模型和事件的关系如下图:
|
||||
|
||||

|
||||
|
||||
## 实战例子
|
||||
|
||||
我们假设 CustomEvent 是我们的自定义注解。还没写完,这部分太复杂了,而且举例子也不好举例,这块应该也不用着急更新。
|
||||
|
||||
TODO:待完成
|
||||
3
docs/event/extend.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# 扩展事件分发器
|
||||
|
||||
TODO
|
||||
@@ -1,72 +0,0 @@
|
||||
# 框架核心注解事件
|
||||
|
||||
框架核心注解事件区别于机器人和路由注解事件,这里框架注解事件都是**直接**或封装调用 Swoole 的回调事件的,所以对一些比较底层或者基础的操作都在这里做,例如收到 HTTP 或 WebSocket 连接后执行的事件函数。
|
||||
|
||||
## OnSwooleEvent()
|
||||
|
||||
绑定 Swoole 所相关的事件,例如 WebSocket 接入、收到 WS 消息、关闭 WS 连接,HTTP 请求到达等。这个是旧的统一的 Swoole 事件分发注解。请尽量使用上面几个新的注解。
|
||||
|
||||
### 属性
|
||||
|
||||
| 类型 | 值 |
|
||||
| ------------ | ------------------------------------------ |
|
||||
| 名称 | `@OnSwooleEvent` |
|
||||
| 触发前提 | 当参数指定的 `type` 对应的事件被触发后激活 |
|
||||
| 命名空间 | `ZM\Annotation\Swoole\OnSwooleEvent` |
|
||||
| 适用位置 | 方法 |
|
||||
| 返回值处理 | 无 |
|
||||
| 注解绑定参数 | |
|
||||
|
||||
### 注解参数
|
||||
|
||||
| 参数名称 | 参数范围 | 用途 | 默认 |
|
||||
| -------- | -------------------------------------------------------- | ----------------------------------------------- | ---------------- |
|
||||
| type | `string`,支持填入 `open`,`request`,`close`,`message` | 限定事件的类型,**必填** | |
|
||||
| rule | `string`,必须是可执行且返回 bool 的 PHP 代码 | 例如判断连接是否为 QQ 机器人(`connectIsQQ()`) | 空,rule 为 true |
|
||||
| level | `int` | 事件优先级(越大越靠前) | 20 |
|
||||
|
||||
### 事件绑定参数
|
||||
|
||||
`$conn`: [ConnectionObject](/advanced/inside-class/) 类型,返回一个当前 WS 连接的连接对象。
|
||||
|
||||
### 示例1(机器人连接框架后输出信息)
|
||||
|
||||
```php
|
||||
<?php
|
||||
namespace Module\Example;
|
||||
use ZM\Annotation\Swoole\OnSwooleEvent;
|
||||
use ZM\ConnectionManager\ConnectionObject;
|
||||
use ZM\Console\Console;
|
||||
class Hello {
|
||||
/**
|
||||
* 在机器人客户端连接框架后向终端输出信息
|
||||
* @OnSwooleEvent("open",rule="connectIsQQ()")
|
||||
* @param $conn
|
||||
*/
|
||||
public function onConnect(ConnectionObject $conn) {
|
||||
Console::info("机器人 " . $conn->getOption("connect_id") . " 已连接!");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
这里的 Console 是终端输出组件,详情见组件一栏对应的文档查询。
|
||||
|
||||
### 示例2(阻断 Chrome 访问框架时多访问一次的问题)
|
||||
|
||||
```php
|
||||
<?php
|
||||
namespace Module\Example;
|
||||
use ZM\Annotation\Swoole\OnSwooleEvent;
|
||||
use ZM\Event\EventDispatcher;
|
||||
class Hello {
|
||||
/**
|
||||
* 阻止 Chrome 自动请求 /favicon.ico 导致的多条请求并发和干扰
|
||||
* @OnSwooleEvent("request",rule="ctx()->getRequest()->server['request_uri'] == '/favicon.ico'",level=200)
|
||||
*/
|
||||
public function onRequest() {
|
||||
EventDispatcher::interrupt();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
其中 EventDispatcher 为事件分发器,interrupt 是通用阻断方法,如果你平常只使用阻断,则只需掌握这一个方法即可,`EventDispatcher::interrupt()` 在所有事件内可用。
|
||||
31
docs/event/framework.md
Normal file
@@ -0,0 +1,31 @@
|
||||
# 框架事件
|
||||
|
||||
<aside>
|
||||
🛰️ 此页面下的所有注解命名空间为 `ZM\Annotation\Framework`
|
||||
|
||||
</aside>
|
||||
|
||||
## BindEvent
|
||||
|
||||
相对底层的事件绑定,支持绑定所有透过框架分发的事件。
|
||||
|
||||
| 参数名称 | 允许值 | 用途 | 默认 |
|
||||
| --- | --- | --- | --- |
|
||||
| event_class | string | 时间名 | 必填 |
|
||||
| level | int | 事件优先级(越大越先执行) | 800 |
|
||||
|
||||
## Init
|
||||
|
||||
在 Worker 进程初始化时触发,用于进行 Worker 初始化。
|
||||
|
||||
| 参数名称 | 允许值 | 用途 | 默认 |
|
||||
| --- | --- | --- | --- |
|
||||
| worker | int 由 0 至 (最大Worker数-1) | 限定执行的 Worker 进程,-1 为在所有 Worker 执行 | 0 |
|
||||
|
||||
## Setup
|
||||
|
||||
在框架初始化时触发,在主进程执行,不可使用协程相关功能。
|
||||
|
||||
可用于改变所有进程的设置,相关更改会随着进程创建应用到所有 Worker 和 Manager 进程。
|
||||
|
||||
*没有参数*
|
||||
25
docs/event/http.md
Normal file
@@ -0,0 +1,25 @@
|
||||
# 路由事件
|
||||
|
||||
<aside>
|
||||
🛰️ 此页面下的所有注解命名空间为 `ZM\Annotation\Http`
|
||||
|
||||
</aside>
|
||||
|
||||
## Controller
|
||||
|
||||
对同一类下的路由进行修饰,只可在类上使用。
|
||||
|
||||
| 参数名称 | 允许值 | 用途 | 默认 |
|
||||
| --- | --- | --- | --- |
|
||||
| prefix | string | 路由前缀,应用到类下的所有路由 | 必填 |
|
||||
|
||||
## Route
|
||||
|
||||
路由事件,当对应的路由收到请求时触发。
|
||||
|
||||
| 参数名称 | 允许值 | 用途 | 默认 |
|
||||
| --- | --- | --- | --- |
|
||||
| route | string | 路由 | 必填 |
|
||||
| name | string | 路由名称 | “” |
|
||||
| request_method | array<string> | 允许的请求方法 | [’GET’, ‘POST’] |
|
||||
| params | array<string, string> | 路由参数 | [] |
|
||||
@@ -1,70 +0,0 @@
|
||||
# 事件和注解
|
||||
|
||||
## 注解事件概念
|
||||
|
||||
我们知道事件,是一个底层的 event loop 收到消息后调用对应的各类方法的一个模型,比如给机器人发送消息后框架要做的就是指定到一个你定义的函数上,处理你的业务逻辑代码。比如在默认模块中,提供了 **你好** 的回复:**你好啊,我是由炸毛框架构建的机器人!**。这项简单回复的任务就是一个事件的触发到响应的全过程。
|
||||
|
||||
**注解**(Annotation)又称标注,Java 最早在 2004 年的 JDK 5 中引入的一种注释机制。目前 PHP 官方版本并未提供内置元注解和注解概念,但我们通过 `ReflectionClass` 反射类解析 PHP 代码注释从而实现了自己的一套注解机制。如果你没有写过 Java,并且不了解注解是什么,你可以理解为对 function 或 class 的一个修饰,因为传统的 PHP 代码逻辑我们都知道,不能简单给原先存在的函数贴标签,就比如,你不能在原本的 PHP 代码中给函数贴上一个可以影响它一生并且改变它行为的标签,而有了注解,就相当于有了给函数贴标签的机会。
|
||||
|
||||
在常见框架如 Spring,Swoft 等代码结构里面,注解更是其核心的存在。
|
||||
|
||||
在炸毛框架中,我们所有事件的绑定均采用这一方式进行调用模块内各个方法。包括 Swoole 自身的框架启动事件、WebSocket 连接握手事件、HTTP 请求事件等等,也包括 CQHTTP 发来的事件,如`message`,`notice`,`request` 等。
|
||||
|
||||
## 如何使用注解
|
||||
|
||||
就像我们日常开发写注释一样,只需在类、方法或成员变量上方按规则添加注释即可,这里以默认自带的 `Hello` 模块类为例子:
|
||||
|
||||
```php
|
||||
<?php
|
||||
namespace Module\Example;
|
||||
use ZM\Annotation\CQ\CQCommand;
|
||||
class Hello {
|
||||
/**
|
||||
* @CQCommand(match="你好")
|
||||
* @return string
|
||||
*/
|
||||
public function hello(){
|
||||
return "你好啊,我是由炸毛框架构建的机器人!";
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
其中 `@CQCommand()` 就是一个基本的注解应用。注意需引入相关注解(Annotation)类,**且必须** 以 `/**` 开始并以 `*/` 结束,否则会导致无法解析!上方 `@return` 为 IDE 自动生成的 PHPDoc,不需要管。
|
||||
|
||||
有什么用?大有妙用!这个例子内注解类的用途是收到 QQ 消息后如果消息第一个词匹配到 `你好` 的话,框架就会自动处理,最终执行调用此 `hello()` 方法。注意 `CQCommand` 和其他任何后面讲到的注解类一样,需先 `use ZM\Annotation\` 下的对应注解类,否则也不能正常使用。
|
||||
|
||||
### 基本语法
|
||||
|
||||
先 use!先 use!先 use!重要的事情说三遍!`use ZM\Annotation\xxxx;`
|
||||
|
||||
**必须** 以 `/**` 开始并以 `*/` 结束。
|
||||
|
||||
```
|
||||
@注解类名(参数名1="参数1的值"[,参数名2="参数2的值"])
|
||||
```
|
||||
|
||||
对于只使用或只有一个参数的注解类,`@注解类名("参数的值")` 可以省略参数名。
|
||||
|
||||
对于没有参数的注解类,`@参数名()` 直接使用即可。
|
||||
|
||||
## 注解和事件的关系
|
||||
|
||||
在炸毛框架里,注解常常被当作事件分发的一个重要角色,但注解本身又不是事件,更恰当的说,是注解代表了事件。
|
||||
|
||||
机器人开发过程中常见的 `@CQCommand`,或者是 HTTP 服务器路由绑定 `@RequestMapping` 都是相当于由对应注解代表了事件,而 `@Middleware`,`@Closed` 等这类注解显然不代表任何事件,只能当作这个函数或类的修饰属性而已。代表了事件的注解,我们称之为**注解事件**,它会在某种事件达成条件后触发注解下方的函数本身。
|
||||
|
||||
值得注意的是,注解事件本身概念是我凭空捏造的,我不好解释所以只能创造这么一个词来代指这一抽象的概念,硬要解释的话,大致就好比一个社区里有一个卖牛奶的,有几家人订阅了每日上门送牛奶的服务,只要你打了“给我配送牛奶”的注解,他就会上门。而它送的不止一种奶,可以给你个性化定制,比如让卖牛奶的给你带包糖带瓶水,而描述这个的注解就只能做一个之前注解的修饰。假设你只写了带包糖的注解,没有写给我配送牛奶的注解,那他永远也不会给你送牛奶和糖过来。
|
||||
|
||||
## 阻断事件
|
||||
|
||||
由于炸毛框架内的注解事件统一由一个通用的事件分发器进行分发,所以你在任何注解事件内都可以用通用的方式阻断当前正在运行的事件。
|
||||
|
||||
首先就是要记得先 use 事件分发器的类:`use ZM\Event\EventDispatcher;`。
|
||||
|
||||
```php
|
||||
EventDispatcher::interrupt();
|
||||
EventDispatcher::interrupt($data); // 也可以带返回值,自定义注解事件时有用。
|
||||
```
|
||||
|
||||
|
||||
|
||||
@@ -1,3 +1,18 @@
|
||||
# 中间件注解
|
||||
# 中间件事件
|
||||
|
||||
TODO:师傅,莫催,快肝完了!
|
||||
<aside>
|
||||
🛰️ 此页面下的所有注解命名空间为 `ZM\Annotation\Middleware`
|
||||
|
||||
</aside>
|
||||
|
||||
## Middleware
|
||||
|
||||
当绑定了此中间件的方法被触发时触发。
|
||||
|
||||
| 参数名称 | 允许值 | 用途 | 默认 |
|
||||
| --- | --- | --- | --- |
|
||||
| name | string | 中间件名称 | 必填 |
|
||||
| params | array<mixed> | 中间件参数 | [] |
|
||||
|
||||
> 关于中间件的具体用法,请参见【再来链接】
|
||||
>
|
||||
|
||||
@@ -1,328 +0,0 @@
|
||||
# 机器人注解事件
|
||||
|
||||
QQ 机器人事件是指 CQHTTP 插件发来的 Event 事件,被框架处理后触发到单个类中方法的事件。
|
||||
|
||||
为了便于开发,这里的注解类对应 CQHTTP 插件返回的 `post_type` 类型,对号入座即可。
|
||||
|
||||
!!! tip "提示"
|
||||
|
||||
在使用注解绑定事件过程中,如果无 **必需** 参数,可一个参数也不写,效果就是此事件任何情况下都会调用此方法。例如:`@CQMessage()`
|
||||
|
||||
事件是用户需要从 OneBot 被动接收的数据,有以下几个大类:
|
||||
|
||||
- [消息事件](#cqmessage),包括私聊消息、群消息等,被 [`@CQCommand`](#cqcommand),`@CQMessage` 注解处理。
|
||||
- [通知事件](#cqnotice),包括群成员变动、好友变动等,被 `@CQNotice` 注解事件处理。
|
||||
- [请求事件](#cqrequest),包括加群请求、加好友请求等,被 `@CQRequest` 注解事件处理。
|
||||
- [元事件](#cqmetaevent),包括 OneBot 生命周期、心跳等,被 `@CQMetaEvent` 注解事件处理。
|
||||
|
||||
## CQMessage()
|
||||
|
||||
QQ 收到消息后触发的事件对应注解。
|
||||
|
||||
### 属性
|
||||
|
||||
| 类型 | 值 |
|
||||
| ---- | ----------- |
|
||||
| 名称 | `@CQMessage` |
|
||||
| 触发前提 | 当 `post_type` 为 `message` 时触发 |
|
||||
| 命名空间 | `ZM\Annotation\CQ\CQMessage` |
|
||||
| 适用位置 | 方法 |
|
||||
| 返回值处理 | 当方法返回字符串时,效果等同于执行 `ctx()->reply("xxx")` |
|
||||
|
||||
### 参数
|
||||
|
||||
| 参数名称 | 参数范围 | 用途 | 默认 |
|
||||
| ------------ | ------------------------------------- | -------------------------------------- | ---- |
|
||||
| message_type | `string`,支持填入 `private`,`group` | 限定消息事件的来源类型,如私聊或群消息 | 空 |
|
||||
| user_id | `int64` 或 `string` | 限定消息发送用户 ID(QQ 号) | 空 |
|
||||
| group_id | `int64` 或 `string` | 限定消息发送来源群 ID(QQ 群号) | 空 |
|
||||
| message | `string` | 限定消息内容文本 | 空 |
|
||||
| level | `int` | 事件优先级(越大越靠前) | 20 |
|
||||
|
||||
### 用法
|
||||
|
||||
下面这个例子的注释用途就是:
|
||||
|
||||
- 在用户 QQ 为 `123456` 的用户私聊给机器人发消息后机器人回复内容。
|
||||
- 用户发送文字为 `hello` 时返回 `你好啊,xxx` 的消息。
|
||||
|
||||
=== "代码"
|
||||
|
||||
```php
|
||||
<?php
|
||||
namespace Module\Example;
|
||||
|
||||
use ZM\Annotation\CQ\CQMessage;
|
||||
|
||||
class Hello {
|
||||
/**
|
||||
* @CQMessage(message_type="private",user_id=123456)
|
||||
*/
|
||||
public function test() {
|
||||
return "你和机器人私聊发送了这些文本:".ctx()->getMessage();
|
||||
}
|
||||
/**
|
||||
* @CQMessage(message="hello")
|
||||
*/
|
||||
public function hello() {
|
||||
return "你好啊,".ctx()->getUserId();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
=== "效果"
|
||||
|
||||
<chat-box>
|
||||
) 假设我是私聊机器人
|
||||
( 你和机器人私聊发送了这些文本:假设我是私聊机器人
|
||||
^ 假设我现在切到群里,在群里发hello
|
||||
) hello
|
||||
( 你好啊,123456
|
||||
</chat-box>
|
||||
|
||||
## CQCommand()
|
||||
|
||||
此注解是对 `@CQMessage` 类别的再封装,是命令解析格式处理消息的利器。例如,你想写一个疫情上报,指令是 `疫情 城市名称`,那么此方式来解析用户消息会更加方便。
|
||||
|
||||
### 属性
|
||||
|
||||
| 类型 | 值 |
|
||||
| ---------- | -------------------------------------------------------- |
|
||||
| 名称 | `@CQCommand` |
|
||||
| 触发前提 | 当根据参数规则匹配到用户命令式消息时触发 |
|
||||
| 命名空间 | `ZM\Annotation\CQ\CQCommand` |
|
||||
| 适用位置 | 方法 |
|
||||
| 返回值处理 | 当方法返回字符串时,效果等同于执行 `ctx()->reply("xxx")` |
|
||||
|
||||
### 参数
|
||||
|
||||
| 参数名称 | 参数范围 | 用途 | 默认 |
|
||||
| ------------ | ------------------------------------- | ------------------------------------------------------ | ---- |
|
||||
| match | `string` | 匹配第一个词的命令式消息,如 `天气 北京` 中的 `天气` | 空 |
|
||||
| pattern | `string` | 根据 * 号通配符进行模式匹配用户消息,如 `查询*天气` | 空 |
|
||||
| regex | `string`,限定正则表达式 | 匹配正则表达式匹配到的用户消息 | 空 |
|
||||
| start_with | `string` | 匹配消息开头相匹配的消息,如 `我叫炸毛`,这里写 `我叫` | 空 |
|
||||
| end_with | `string` | 匹配消息结尾相匹配的消息,以 `start_with` 类推 | 空 |
|
||||
| keyword | `string` | 匹配消息中有相关关键词的消息 | 空 |
|
||||
| alias | `array[string]` | `match` 匹配到命令的别名,数组形式 | `{}` |
|
||||
| message_type | `string`,支持填入 `private`,`group` | 限定消息事件的来源类型,同 `@CQMessage` | 空 |
|
||||
| user_id | `int64` 或 `string` | 限定消息发送用户 ID,同 `@CQMessage` | 空 |
|
||||
| group_id | `int64` 或 `string` | 限定消息发送来源群 ID,同 `@CQMessage` | 空 |
|
||||
| level | `int` | 事件优先级(越大越靠前) | 20 |
|
||||
|
||||
!!! warning "注意"
|
||||
|
||||
在 `@CQCommand` 注解事件中,从 `match` 到 `keyword` 六个参数中,必须且只能定义一个,`alias` 目前只能和 `match` 参数同时使用;
|
||||
|
||||
框架内部对于同一条消息事件,优先处理 `@CQCommand` 注解事件,如果未匹配到任何注解事件,则才会继续执行 `@CQMessage` 注解事件。
|
||||
|
||||
- 参数 `match` 匹配模式是:遇到空格、换行就会切分,比如 `点歌 xxx yyy` 会被分割为 `[点歌,xxx,yyy]`,然后抽取第一个词做为命令去匹配,剩下的为参数。
|
||||
- 参数 `pattern` 匹配模式是:\* 号位置变成参数,比如 `从*到*的随机数`,我们输入 `从1到9的随机数`,成功匹配,参数列表:`[1,9]`。
|
||||
- 参数 `regex` 匹配模式为 PHP 标准的 pcre 正则表达式,比如 `([01][0-9][2][0-3]):[0-5][0-9]` 用来匹配 `22:45`。
|
||||
- 参数 `start_with`, `end_with` 和 `keyword` 都是根据消息内容开头、结尾或者内容包含是否匹配来匹配,这里就不多说了,你懂的。
|
||||
- 参数 `alias` 用的时候一般是这样:`@CQCommand(match="你好",alias={"你好啊","你是谁"})`,用以扩充同义词下命令的适配广度。
|
||||
|
||||
### 用法
|
||||
|
||||
我们以参数 `match` 写一个简单的 demo:
|
||||
|
||||
=== "代码"
|
||||
```php
|
||||
<?php
|
||||
namespace Module\Example;
|
||||
|
||||
use ZM\Annotation\CQ\CQCommand;
|
||||
|
||||
class Hello {
|
||||
/**
|
||||
* @CQCommand(match="疫情",alias={"COVID"})
|
||||
*/
|
||||
public function virus(){
|
||||
$city = ctx()->getNextArg("请输入城市名称");
|
||||
return "城市 ".$city." 的疫情状况如下:"."{这里假装是疫情接口返回的数据}";
|
||||
}
|
||||
/**
|
||||
* 如果选择使用 match 参数的话,可以省略 `match=`
|
||||
* @CQCommand("掷硬币")
|
||||
*/
|
||||
public function randChoice() {
|
||||
return "你看到的是:" . (mt_rand(0,1) ? "正面" : "反面");
|
||||
}
|
||||
/**
|
||||
* @CQCommand(pattern="*把*翻译成*")
|
||||
*/
|
||||
public function translate() {
|
||||
ctx()->getNextArg(); // 为什么需要单独调用一次呢?看下面例子就知道啦
|
||||
$text = ctx()->getNextArg(); // 获取第二个星号匹配的内容
|
||||
$target = ctx()->getNextArg(); // 获取第三个星号匹配的内容
|
||||
// 这里 FakeTranslateAPI 是假设我们对接了一个翻译的 API,开发时请替换为自己的接口。
|
||||
return "翻译结果:" . FakeTranslateAPI::translate($text, $target);
|
||||
}
|
||||
}
|
||||
```
|
||||
=== "效果"
|
||||
|
||||
<chat-box>
|
||||
) 疫情 北京
|
||||
( 城市 北京 的疫情状况如下:blablablabla
|
||||
) COVID 香港
|
||||
( 城市 香港 的疫情状况如下:blablablabla
|
||||
) 掷硬币
|
||||
( 你看到的是:正面
|
||||
) 我想把我爱你翻译成英语
|
||||
( 翻译结果:I love you!
|
||||
</chat-box>
|
||||
|
||||
## CQNotice()
|
||||
|
||||
通知事件。
|
||||
|
||||
### 属性
|
||||
|
||||
| 类型 | 值 |
|
||||
| ---------- | ----------------------------------------------------- |
|
||||
| 名称 | `@CQNotice` |
|
||||
| 触发前提 | 当 `post_type` 为 `notice` 时触发(通知类事件上报时) |
|
||||
| 命名空间 | `ZM\Annotation\CQ\CQNotice` |
|
||||
| 适用位置 | 方法 |
|
||||
| 返回值处理 | 无作用 |
|
||||
|
||||
### 参数
|
||||
|
||||
| 参数名称 | 参数范围 | 用途 | 默认 |
|
||||
| ----------- | ------------------------------------ | ------------------------------------------------------------ | ---- |
|
||||
| notice_type | `string`,支持填入 onebot 标准的内容 | 限定通知事件的类型,见 [OneBot - 通知事件](https://github.com/howmanybots/onebot/blob/master/v11/specs/event/notice.md) | 空 |
|
||||
| user_id | `int64` 或 `string` | 限定通知事件用户 ID(QQ 号),同上见 [OneBot - 通知事件](https://github.com/howmanybots/onebot/blob/master/v11/specs/event/notice.md) | 空 |
|
||||
| group_id | `int64` 或 `string` | 限定通知事件群 ID(QQ 群号),同上见 [OneBot - 通知事件](https://github.com/howmanybots/onebot/blob/master/v11/specs/event/notice.md) | 空 |
|
||||
| operator_id | `int64` 或 `string` | 限定操作者 QQ 号,同上见 [OneBot - 通知事件](https://github.com/howmanybots/onebot/blob/master/v11/specs/event/notice.md) | 空 |
|
||||
| level | `int` | 事件优先级(越大越靠前) | 20 |
|
||||
|
||||
### 用法
|
||||
|
||||
TODO:先放着,有时间再更。
|
||||
|
||||
## CQRequest()
|
||||
|
||||
请求事件。
|
||||
|
||||
### 属性
|
||||
|
||||
| 类型 | 值 |
|
||||
| ---------- | ------------------------------------------------------ |
|
||||
| 名称 | `@CQRequest` |
|
||||
| 触发前提 | 当 `post_type` 为 `request` 时触发(通知类事件上报时) |
|
||||
| 命名空间 | `ZM\Annotation\CQ\CQRequest` |
|
||||
| 适用位置 | 方法 |
|
||||
| 返回值处理 | 无作用 |
|
||||
|
||||
### 参数
|
||||
|
||||
| 参数名称 | 参数范围 | 用途 | 默认 |
|
||||
| ------------ | ------------------------------------ | ------------------------------------------------------------ | ---- |
|
||||
| request_type | `string`,支持填入 onebot 标准的内容 | 限定请求事件的类型,见 [OneBot - 请求事件](https://github.com/howmanybots/onebot/blob/master/v11/specs/event/request.md) | 空 |
|
||||
| user_id | `int64` 或 `string` | 限定请求事件当事人用户 ID(QQ 号),见 [OneBot - 请求事件](https://github.com/howmanybots/onebot/blob/master/v11/specs/event/request.md) | 空 |
|
||||
| sub_type | `string` | 限定请求事件来源群 ID(QQ 群号),见 [OneBot - 请求事件](https://github.com/howmanybots/onebot/blob/master/v11/specs/event/request.md) | 空 |
|
||||
| comment | `string` | 限定验证消息内容,见 [OneBot - 请求事件](https://github.com/howmanybots/onebot/blob/master/v11/specs/event/request.md) | 空 |
|
||||
| level | `int` | 事件优先级(越大越靠前) | 20 |
|
||||
|
||||
### 用法
|
||||
|
||||
TODO:先放着,有时间再更。
|
||||
|
||||
## CQMetaEvent()
|
||||
|
||||
元事件,元事件不属于用户交互的一部分,消息、通知、请求三大类事件是与聊天软件直接相关的、机器人真实接收到的事件,除了这些,OneBot 自己还会产生一类事件,这里称之为「元事件」,例如生命周期事件、心跳事件等,这类事件与兼容 OneBot 的客户端和炸毛框架本身的运行状态有关,而与聊天软件无关。元事件的上报方式和普通事件完全一样。
|
||||
|
||||
### 属性
|
||||
|
||||
| 类型 | 值 |
|
||||
| ---------- | ----------------------------------------------------- |
|
||||
| 名称 | `@CQMetaEvent` |
|
||||
| 触发前提 | 当 `post_type` 为 `meta_event` 时触发(元事件上报时) |
|
||||
| 命名空间 | `ZM\Annotation\CQ\CQMetaEvent` |
|
||||
| 适用位置 | 方法 |
|
||||
| 返回值处理 | 无作用 |
|
||||
|
||||
### 参数
|
||||
|
||||
| 参数名称 | 参数范围 | 用途 | 默认 |
|
||||
| --------------- | ------------------ | ------------------------------------------------------------ | ---- |
|
||||
| meta_event_type | `string`,**必需** | 限定元事件的类型,见 [OneBot - 元事件](https://github.com/howmanybots/onebot/blob/master/v11/specs/event/meta.md) | |
|
||||
| level | `int` | 事件优先级(越大越靠前) | 20 |
|
||||
|
||||
### 用法
|
||||
|
||||
TODO:先放着,有时间再更。
|
||||
|
||||
## CQBefore()
|
||||
|
||||
所有机器人事件的前置注解事件,一般用作消息过滤、全局日志、全局替换等。
|
||||
|
||||
### 属性
|
||||
|
||||
| 类型 | 值 |
|
||||
| ---------- | ------------------------------------------------------------ |
|
||||
| 名称 | `@CQBefore` |
|
||||
| 触发前提 | 当 `post_type` 等于参数 `cq_event` 时触发 |
|
||||
| 命名空间 | `ZM\Annotation\CQ\CQBefore` |
|
||||
| 适用位置 | 方法 |
|
||||
| 返回值处理 | 仅可返回 `bool`,如果为 `false`,则阻断 `cq_event` 类的所有事件防止被执行 |
|
||||
|
||||
### 参数
|
||||
|
||||
| 参数名称 | 参数范围 | 用途 | 默认 |
|
||||
| -------- | ------------------------------------------------------------ | ------------------------ | ---- |
|
||||
| cq_event | `string`,**必需**,支持 `message`,`notice`,`request`,`meta_event` | 限定机器人时间的类型 | |
|
||||
| level | `int` | 事件优先级(越大越靠前) | 20 |
|
||||
|
||||
### 用法
|
||||
|
||||
=== "代码"
|
||||
|
||||
```php
|
||||
<?php
|
||||
namespace Module\Example;
|
||||
|
||||
use ZM\Annotation\CQ\CQBefore;
|
||||
use ZM\Annotation\CQ\CQMessage;
|
||||
class Test {
|
||||
/**
|
||||
* @CQBefore("message")
|
||||
*/
|
||||
public function filter(){
|
||||
// 可用于敏感词,如政治相关的词语不响应其他模块
|
||||
if(mb_strpos(ctx()->getMessage(), "谷歌") !== false) return false;
|
||||
else return true;
|
||||
}
|
||||
/**
|
||||
* @CQCommand("百科")
|
||||
*/
|
||||
public function wiki() {
|
||||
$content = ctx()->getNextArg("请说你要查百科的内容");
|
||||
// 这里假设你对接了一个查百科的接口
|
||||
return "已搜到匹配 $content 的如下结果:".FakeAPI::searchWiki($content);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
=== "效果"
|
||||
|
||||
<chat-box>
|
||||
) 百科 北京
|
||||
( 已搜到匹配 北京 的如下结果:blablabla
|
||||
) 百科 谷歌被封
|
||||
^ 机器人没有任何回复
|
||||
|
||||
!!! warning "注意"
|
||||
|
||||
在设置了 `level` 参数后,如果设置了多个 `@CQBefore` 监听事件函数,更高 `level` 的事件函数返回了 `false`,则低 `level` 的绑定函数不会执行,所有 `@CQMessage` 绑定的事件也不会执行。
|
||||
|
||||
你也可以使用 `@CQBefore` 做一些消息的转发和过滤。比如你想去除用户发来的文字中的 emoji、图片等 CQ 码,只保留文本。
|
||||
|
||||
## CQAfter()
|
||||
|
||||
同上。只是在以上所有事件都调用后才会调用的。
|
||||
|
||||
## CQAPIResponse()
|
||||
|
||||
TODO:还没写完,先放着,有时间再更。
|
||||
@@ -1,231 +0,0 @@
|
||||
# 路由注解事件
|
||||
|
||||
炸毛框架提供了一个简易但是高效易用的 HTTP 路由注解,你可以使用路由功能来开发任何 Web 应用微服务、API 接口、中间件等。
|
||||
|
||||
!!! quote "开发提示"
|
||||
|
||||
本章节涉及的路由和控制器概念可能和其他传统框架有一些出入,而且炸毛框架非绝对根据 PSR 标准进行开发,目的是使用上一些常见的东西尽可能地灵活和不罗嗦。
|
||||
|
||||
## 控制器和路由
|
||||
|
||||
Controller 和 Route 为路由注解事件的核心注解事件,其中 Controller 的注解事件为 `@Controller`,Route 的注解事件为 `@RequestMapping`。
|
||||
|
||||
### Controller()
|
||||
|
||||
#### 属性
|
||||
|
||||
| 类型 | 值 |
|
||||
| ---------- | ------------------------------- |
|
||||
| 名称 | `@Controller` |
|
||||
| 触发前提 | 当路由 url 匹配到时进入触发 |
|
||||
| 命名空间 | `ZM\Annotation\Http\Controller` |
|
||||
| 适用位置 | 类 |
|
||||
| 返回值处理 | 对类注解修饰,无返回值 |
|
||||
|
||||
#### 参数
|
||||
|
||||
| 参数名称 | 参数范围 | 用途 | 默认 |
|
||||
| -------- | -------------- | ------------ | ---- |
|
||||
| prefix | `string`,必需 | 控制器的 url | 空 |
|
||||
|
||||
### RequestMapping()
|
||||
|
||||
#### 属性
|
||||
|
||||
| 类型 | 值 |
|
||||
| ---------- | ----------------------------------------------------------- |
|
||||
| 名称 | `@RequestMapping` |
|
||||
| 触发前提 | 当路由 url 匹配到时进入触发 |
|
||||
| 命名空间 | `ZM\Annotation\Http\RequestMapping` |
|
||||
| 适用位置 | 方法 |
|
||||
| 返回值处理 | 返回类型是 `string` 时,自动调用 HTTP 响应并返回 200 状态码 |
|
||||
|
||||
#### 参数
|
||||
|
||||
| 参数名称 | 参数范围 | 用途 | 默认 |
|
||||
| -------------- | ----------------------------------------------------------- | -------------------------- | ------------------------------------------ |
|
||||
| route | `string`,必需 | 控制器的 url | 空 |
|
||||
| name | `string` | 路由的名称 | 空 |
|
||||
| request_method | `array`,限定 `RequestMethod::GET` 等常量 | 限制激活路由的 HTTP 方法 | `[RequestMethod::GET,RequestMethod::POST]` |
|
||||
| params | `array`,当路由中含有如 `{id}` 类似的动态路由时,会动态改变 | 动态参数的路由参数值的绑定 | `[]` |
|
||||
|
||||
#### 函数调用参数
|
||||
|
||||
- `$param`:如果路由中存在变量(动态路由),则会把动态路由所匹配的参数放入 `$param` 数组中。
|
||||
|
||||
```php
|
||||
/**
|
||||
* @RequestMapping(route="/test/{ass}")
|
||||
*/
|
||||
public function testName($param) {
|
||||
return "Your name is ".($param["ass"] ?? "unknown");
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
### 路由示例
|
||||
|
||||
=== "代码"
|
||||
```php
|
||||
<?php
|
||||
namespace Module\Example;
|
||||
|
||||
use ZM\Annotation\Http\Controller;
|
||||
use ZM\Annotation\Http\RequestMapping;
|
||||
/**
|
||||
* @Controller("/api")
|
||||
*/
|
||||
class Hello {
|
||||
/**
|
||||
* @RequestMapping("/index")
|
||||
*/
|
||||
public function index(){
|
||||
ctx()->getResponse()->end("This is API index page"); // 使用上下文获取响应对象
|
||||
}
|
||||
/**
|
||||
* @RequestMapping("/ping")
|
||||
*/
|
||||
public function ping(){
|
||||
return "pong"; // 直接返回字符串
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
=== "效果"
|
||||
|
||||
!!! example "效果描述"
|
||||
当访问浏览器的 `http://localhost:20001/api/index` 时,浏览器会返回 `This is API index page`,当访问 `/api/ping` 的 url 时,浏览器会返回 `pong`。
|
||||
|
||||
```
|
||||
/ -> 无任何路由
|
||||
/api/index -> Hello->index
|
||||
/api/ping -> Hello->ping
|
||||
```
|
||||
|
||||
!!! tip "提示"
|
||||
|
||||
当 `@Controller` 为 `/` 的时候,效果和不写是一样的,`@RequestMapping` 为 `/` 或 `/index/inside` 等多级路由也是可以的。
|
||||
|
||||
### 绑定参数
|
||||
|
||||
在 `@RequestMapping` 中,不仅可以写静态的路由地址,也可以写绑定的参数。例如:`@RequestMapping(route="/index/{name}")`,则访问 `/index/xxx` 的时候,你在函数方法内可以这样获取此参数:
|
||||
|
||||
```php
|
||||
/**
|
||||
* @RequestMapping("/index/{name}")
|
||||
*/
|
||||
public function index($arg) {
|
||||
return "Your param 'name' is ".$arg["name"];
|
||||
}
|
||||
```
|
||||
|
||||
## 获取请求参数 GET / POST
|
||||
|
||||
炸毛框架支持获取外部 HTTP 请求进来的 GET 和 POST 请求,通过获取 HTTP 请求对象 [Request](/advanced/inside-class/) 即可。对象具体属性和方法点这个链接进去就行。
|
||||
|
||||
### 示例
|
||||
|
||||
=== "获取 GET"
|
||||
|
||||
```php
|
||||
/**
|
||||
* @RequestMapping("/testUrl")
|
||||
*/
|
||||
public function testUrl() {
|
||||
$get = ctx()->getRequest()->get;
|
||||
if(isset($get["name"])) return "hello, ".$get["name"];
|
||||
else return "Unknown name!!";
|
||||
}
|
||||
```
|
||||
|
||||
=== "获取 POST(x-www-form-urlencoded)"
|
||||
|
||||
```php
|
||||
/**
|
||||
* @RequestMapping("/testUrl")
|
||||
*/
|
||||
public function testUrl() {
|
||||
$post = ctx()->getRequest()->post;
|
||||
if(isset($post["name"])) return "hello, ".$post["name"];
|
||||
else return "Unknown name!!";
|
||||
}
|
||||
```
|
||||
|
||||
=== "获取 JSON POST"
|
||||
|
||||
```php
|
||||
/**
|
||||
* @RequestMapping("/testUrl")
|
||||
*/
|
||||
public function testUrl() {
|
||||
$post = ctx()->getRequest()->rawContent();
|
||||
$json = json_decode($post, true);
|
||||
if ($json === null) return "Invalid json data!";
|
||||
if(isset($json["name"])) return "hello, ".$json["name"];
|
||||
else return "Unknown name!!";
|
||||
}
|
||||
```
|
||||
|
||||
## 设置路由请求方式
|
||||
|
||||
如果想要设置允许请求控制器的 HTTP 请求方式,可以使用方法在控制器中的 `@RequestMapping` 注解配置 `method` 参数,可以是 `GET`,`POST`,`PUT`, `PATCH`,`DELETE`,`OPTIONS`,`HEAD` 中的一个或多个。
|
||||
|
||||
- 限定 HTTP 方法:`@RequestMapping(method="GET")`,`@RequestMapping(method={"GET","POST"})`
|
||||
|
||||
## 静态文件服务器
|
||||
|
||||
框架支持了静态文件的访问。如需使用,则需要先到配置文件中配置相应的 `static_file_server` 参数中 `status` 为 `true`。
|
||||
|
||||
框架分为两种静态文件服务器,一种是全局的静态文件服务器,比如框架部署在 `http://127.0.0.1:20001/` 上通过 HTTP 访问,如果没有访问到 `@RequestMapping` 注解事件注册的路由地址,则会通过 url 自动查找静态文件服务器设置的根路径下面的文件,如果都不存在则会返回 404。
|
||||
|
||||
### 配置全局静态文件服务器
|
||||
|
||||
我们假设在你写的框架应用的根目录下,有如下文件和内容:
|
||||
|
||||
```
|
||||
resources/html/hello.html (下面是内容)
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
</head>
|
||||
<body>
|
||||
框架文档内容太多了,写不完!!!
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
然后在 `global.php` 配置文件中静态文件服务器参数为:
|
||||
|
||||
```php
|
||||
/** 静态文件访问 */
|
||||
$config['static_file_server'] = [
|
||||
'status' => true,
|
||||
'document_root' => realpath(__DIR__ . "/../") . '/resources/html',
|
||||
'document_index' => [
|
||||
'index.html'
|
||||
]
|
||||
];
|
||||
```
|
||||
|
||||
最终,我们通过 `vendor/bin/start server` 等方式,启动框架后,浏览器访问 `http://127.0.0.1:20001/hello.html` 即可获取内容。
|
||||
|
||||
### 配置局部静态文件服务器
|
||||
|
||||
所涉及的类的命名空间:`use ZM\Http\StaticFileHandler;`
|
||||
|
||||
局部静态文件服务器一般用于,比如机器人要发送图片,或者给其他 HTTP 服务提供文件下载的接口时可用。我们假设写了一个图片收集的一个静态文件夹区域,将其中一个子路由当作图片静态目录:
|
||||
|
||||
```php
|
||||
/**
|
||||
* @RequestMapping("/images/{filename}")
|
||||
* @param $param
|
||||
* @return StaticFileHandler
|
||||
*/
|
||||
public function staticImage($param) {
|
||||
Console::info("[下载图片] " . $param["filename"]);
|
||||
return new StaticFileHandler($param["filename"], "/path/to/your/image_dir/");
|
||||
}
|
||||
```
|
||||
|
||||
这样当用户访问 `http://框架地址/images/aaa.jpg` 就可以快速地调用此路由下的局部文件服务器功能了。
|
||||
26
docs/guide/README.md
Normal file
@@ -0,0 +1,26 @@
|
||||
# 介绍
|
||||
|
||||
> 编写文档需要较大精力,你也可以参与到本文档的建设中来,比如找错字,增加或更正内容,每页文档可直接点击右上方铅笔图标直接跳转至 GitHub 进行编辑,编辑后自动 Fork 并生成 Pull Request,以此来贡献此文档!
|
||||
|
||||
炸毛框架主要面向聊天机器人(OneBot 标准)和 API 服务的开发。
|
||||
|
||||
框架使用 PHP 编写,无需安装任何额外扩展即可运行在任意主流平台,支持包括 Swoole 和 Workerman 在内的多种驱动,同时支持所有在 OneBot 标准内的通信方式,并支持使用注解(Annotation)和属性(Attribute)注册绑定各种事件,同时引入了依赖注入容器让开发更为便捷。
|
||||
|
||||
框架内置了对于 WebSocket 和 HTTP 的服务端和客户端支持,并针对聊天机器人消息处理进行优化扩展,提供常用会话机制和内部调用机制,让代码更为灵活。
|
||||
|
||||
## 环境要求
|
||||
|
||||
虽然我们已经大力简化了运行框架的要求,但仍然存在少量的必要项:
|
||||
|
||||
- PHP 8.0 或以上版本(使用命令 `php -v` 检查)
|
||||
- Tokenizer 扩展(使用命令 `php -m | grep tokenizer` 检查)
|
||||
- Composer 工具(使用命令 `composer` 检查)
|
||||
|
||||
## 框架特色
|
||||
|
||||
- 支持MySQL数据库(连接池),自带查询缓存提高多查询时的效率
|
||||
- WebSocket 服务器、HTTP 服务器兼容运行,一个框架多个用处
|
||||
- 支持命令、自然语言处理等多种插件形式
|
||||
- 支持多个机器人账号负载均衡
|
||||
- 完善的插件系统,可以随意加载和编写独立的插件
|
||||
- 灵活的注释和注解注册事件方式,支持 PHP 原生注解,提示更为友好
|
||||
@@ -1,142 +0,0 @@
|
||||
# 基本配置
|
||||
|
||||
到目前为止,炸毛框架的配置文件还没有任何变更,是默认的行为。在本章内容中,将列举出炸毛框架的配置文件的规则和使用。
|
||||
|
||||
!!! error "警告"
|
||||
|
||||
因为炸毛框架的全局配置中含有数据库名称和密码以及 access_token 等敏感字段,在使用版本控制软件过程中请不要将将敏感信息写入配置文件并提交至开源仓库!
|
||||
|
||||
## 全局配置文件 global.php
|
||||
|
||||
框架的全局配置文件在 `config/global.php` 文件中。下面是配置文件的各个选项,请根据自己的需要自行配置。
|
||||
|
||||
| 配置名称 | 说明 | 默认值 |
|
||||
| :--------------------------- | ------------------------------------------------ | ---------------------------- |
|
||||
| `host` | 框架监听的地址 | 0.0.0.0 |
|
||||
| `port` | 框架监听的端口 | 20001 |
|
||||
| `http_reverse_link` | 框架开到公网或外部的 HTTP 反代链接 | 见配置文件 |
|
||||
| `zm_data` | 框架的配置文件、日志文件等文件目录 | `./` 下的 `zm_data/` |
|
||||
| `debug_mode` | 框架是否启动 debug 模式 | false |
|
||||
| `crash_dir` | 存放崩溃和运行日志的目录 | `zm_data` 下的 `crash/` |
|
||||
| `swoole` | 对应 Swoole server 中 set 的参数,参考Swoole文档 | 见子表 `swoole` |
|
||||
| `light_cache` | 轻量内置 key-value 缓存 | 见字表 `light_cache` |
|
||||
| `sql_config` | MySQL 数据库连接信息 | 见子表 `sql_config` |
|
||||
| `redis_config` | Redis 连接信息 | 见子表 `redis_config` |
|
||||
| `access_token` | OneBot 客户端连接约定的token,留空则无 | 空 |
|
||||
| `http_header` | HTTP 请求自定义返回的header | 见配置文件 |
|
||||
| `http_default_code_page` | HTTP服务器在指定状态码下回复的默认页面 | 见配置文件 |
|
||||
| `init_atomics` | 框架启动时初始化的原子计数器列表 | 见配置文件 |
|
||||
| `info_level` | 终端日志显示等级(0-4) | 2 |
|
||||
| `context_class` | 上下文所定义的类,待上下文完善后见对应文档 | `\ZM\Context\Context::class` |
|
||||
| `static_file_server` | 静态文件服务器配置项 | 见子表 `static_file_server` |
|
||||
| `server_event_handler_class` | 注册 Swoole Server 事件注解的类列表 | 见配置文件 |
|
||||
| `command_register_class` | 注册自定义命令行选项指令的类 | 见配置文件 |
|
||||
| `modules` | 服务器启用的外部第三方和内部插件 | `['onebot' => true]` |
|
||||
|
||||
### 子表 **swoole**
|
||||
|
||||
| 配置名称 | 说明 | 默认值 |
|
||||
| --------------- | ------------------------------------------------------------ | ----------------------------------- |
|
||||
| `log_file` | Swoole 的日志文件 | `crash_dir` 下的 `swoole_error.log` |
|
||||
| `worker_num` | Worker 工作进程数 | 运行框架的主机 CPU 核心数 |
|
||||
| `dispatch_mode` | 数据包分发策略,见 [文档](https://wiki.swoole.com/#/server/setting?id=dispatch_mode) | 2 |
|
||||
| `max_coroutine` | 最大协程并发数 | 300000 |
|
||||
|
||||
### 子表 **light_cache**
|
||||
|
||||
| 配置名称 | 说明 | 默认值 |
|
||||
| -------------------------- | ----------------------------------------------- | ---------------------------- |
|
||||
| `size` | 最多可以缓存的 k-v 条目数(必须是 2 的 n 次方) | 1024 |
|
||||
| `max_strlen` | 作为 value 字符串的最大长度 | 16384 |
|
||||
| `hash_conflict_proportion` | Hash冲突率(越大越好,但是需要的内存更多) | 0.6 |
|
||||
| `persistence_path` | 持久化的键值对的存储路径 | `zm_data` 下的 `_cache.json` |
|
||||
| `auto_save_interval` | 持久化的键值对自动保存时间间隔(秒) | 900 |
|
||||
|
||||
### 子表 **sql_config**
|
||||
|
||||
| 配置名称 | 说明 | 默认值 |
|
||||
| ------------------------ | ------------------------------ | ------------------------------------------------------------ |
|
||||
| `sql_host` | 数据库地址(留空则不使用数据库) | 空 |
|
||||
| `sql_port` | 数据库端口 | 3306 |
|
||||
| `sql_username` | 连接数据库的用户名 | |
|
||||
| `sql_database` | 要连接的数据库名 | |
|
||||
| `sql_password` | 数据库连接密码 | |
|
||||
| `sql_options` | PDO 数据库的 options 参数 | `[PDO::ATTR_STRINGIFY_FETCHES => false,PDO::ATTR_EMULATE_PREPARES => false]` |
|
||||
| `sql_default_fetch_mode` | PDO 的 fetch 模式 | `PDO::FETCH_ASSOC` |
|
||||
|
||||
### 子表 **redis_config**
|
||||
|
||||
| 配置名称 | 说明 | 默认值 |
|
||||
| ---------- | ------------------------------------------ | ------ |
|
||||
| `host` | Redis 服务器地址,留空则启动时不创建连接池 | 空 |
|
||||
| `port` | Redis 服务器端口 | 6379 |
|
||||
| `timeout` | Redis 超时时间 | 1 |
|
||||
| `db_index` | Redis 要连接的数据库 index | 0 |
|
||||
| `auth` | 认证字符串 | 空 |
|
||||
|
||||
### 子表 static_file_server
|
||||
|
||||
| 配置名称 | 说明 | 默认值 |
|
||||
| ---------------- | ---------------------- | ------------------------------ |
|
||||
| `status` | 是否开启静态文件服务器 | false |
|
||||
| `document_root` | 静态文件的根目录 | `{WORKING_DIR}/resources/html` |
|
||||
| `document_index` | 默认索引的文件名列表 | `["index.html"]` |
|
||||
|
||||
## 多环境下的配置文件
|
||||
|
||||
炸毛框架的配置文件模块支持不同环境下的配置文件,主要结构为 `global.{环境}.php`。在一般情况下,炸毛框架默认从教程引导方式根据指令 `vendor/bin/start server` 启动的框架是不带环境控制的。这章将讲述如何根据不同的环境(production / development / staging)来编写配置文件。
|
||||
|
||||
### 使用环境参数
|
||||
|
||||
在启动框架时,额外增加参数 `--env` 可以指定当前的环境,从而使用不同的配置文件。现在框架支持以下几种环境: `production`,`staging`,`development`。
|
||||
|
||||
```bash
|
||||
vendor/bin/start server --env=development
|
||||
```
|
||||
|
||||
### 不同环境配置文件
|
||||
|
||||
由于框架默认只带有 `global.php` 文件,所以假设你现在需要区分开发环境和生产环境的配置,将 `global.php` 文件复制或改名为 `global.development.php` 或 `global.production.php` 即可。
|
||||
|
||||
### 优先级
|
||||
|
||||
如果指定了 `--env` 环境参数:`global.{对应环境}.php` > `global.php`,如果两个配置文件都找不到则报错。
|
||||
|
||||
如果未指定 `--env` 环境参数:`global.php` > `global.development.php` > `global.staging.php` > `global.production.php`。
|
||||
|
||||
## 其他自定义配置文件
|
||||
|
||||
炸毛框架的全局配置文件为 `global.php`,为了让不同的开发者更好的二次开发或者集成更多功能,炸毛框架的配置文件模块也支持自己编写的其他 `*.php` 或 `*.json` 格式的配置文件。例如炸毛框架默认附带了 `file_header.json` 这个配置文件(用来返回各类文件扩展名对应的 `Content-Type` 头参数的表)。
|
||||
|
||||
使用也非常简单,我们先以 `.json` 格式为例,我们创建一个 `example_a.json` 文件在 `config/` 目录(和 `global.php` 一个文件夹下),并编写自己的任意配置内容:
|
||||
|
||||
```json
|
||||
{
|
||||
"key1": "value1"
|
||||
}
|
||||
```
|
||||
|
||||
在框架中,启动后就会默认加载,使用只需要用以下方式即可:
|
||||
|
||||
```php
|
||||
use ZM\Config\ZMConfig; # 先 use 再使用!
|
||||
$r = ZMConfig::get("example_a", "key1"); # $r == "value1"
|
||||
```
|
||||
|
||||
如果需要用到变量或其他动态的内容,可以使用 `.php` 格式的配置文件。这里还是以 `example_a.php` 来举例:
|
||||
|
||||
```php
|
||||
<?php
|
||||
$config['key1'] = "value1";
|
||||
$config['starttime'] = time();
|
||||
return $config;
|
||||
```
|
||||
|
||||
使用方式同上:
|
||||
|
||||
```php
|
||||
$r = ZMConfig::get("example_a", "key1"); # $r == "value1"
|
||||
$time = ZMConfig::get("example_a", "starttime"); # $time == 服务器启动时间
|
||||
```
|
||||
|
||||
同时,自定义配置文件也支持环境变量,例如:`example_a.development.json` 或 `example_a.production.php` 均可。
|
||||
47
docs/guide/configuration.md
Normal file
@@ -0,0 +1,47 @@
|
||||
# 配置
|
||||
|
||||
炸毛框架的所有配置文件都存储在 `config` 目录中。每个选项都带有文档,所以你可以查阅这些文件并熟悉可用的配置项。
|
||||
|
||||
一般来说,我们建议你优先查看 `config/global.php` 文件及其文档,它包含了运行框架所需要的绝大部分配置,例如通信方式和时区等。
|
||||
|
||||
## 配置文件格式
|
||||
|
||||
除了 `php` 文件外,我们还支持以下格式:
|
||||
|
||||
- YAML
|
||||
- JSON
|
||||
- TOML
|
||||
|
||||
## 环境配置
|
||||
|
||||
根据运行的环境采用不同的配置值是很有必要的,例如你可能希望在本地和生产环境中使用不同的数据库配置。
|
||||
|
||||
为了方便这一功能,我们提供了基于环境的配置加载措施。你可以在启动框架时使用 `--env` 选项指定当前环境,例如 `./zhamao server --env=production` 。
|
||||
|
||||
如果未明确指定当前环境,则默认环境为 `development`。
|
||||
|
||||
如果要使某一配置文件只在特定环境下记载,你可以为给文件添加后缀,例如 `example.production.php` 只会在当前环境为 `production` 时加载。
|
||||
|
||||
你可以同时设置基础配置文件和环境特定配置文件,例如 `global.php` 和 `global.production.php` ,后者将会覆盖前者中的配置项。
|
||||
|
||||
### 配置安全
|
||||
|
||||
我们**非常不建议**你将数据库信息、访问密钥的敏感字段提交给版本控制,因为这可能会导致相关信息的泄露,同时也不利于其他开发人员修改自己的本地配置。
|
||||
|
||||
相反,我们建议你借助 DotEnv 等库,将相关配置信息写入 `.env` 文件中,并按需读取。
|
||||
|
||||
## 访问配置项
|
||||
|
||||
你可以在任何地方使用全局 `config` 函数获取配置项值。支持使用点语法获取,配置项名称由文件名开头,并允许指定默认值。
|
||||
|
||||
```php
|
||||
# 获取时区,未设置则返回 Asia/Shanghai
|
||||
$value = config('global.runtime.timezone', 'Asia/Shanghai');
|
||||
```
|
||||
|
||||
你也可以使用 `config` 函数设置配置项,传递数组即可:
|
||||
|
||||
```php
|
||||
# 将时区修改为 UTC
|
||||
config(['global.runtime.timezone' => 'UTC']);
|
||||
```
|
||||
112
docs/guide/get_started.md
Normal file
@@ -0,0 +1,112 @@
|
||||
# 快速上手
|
||||
|
||||
在这里,我们将会以一个基础的复读机为例,帮助你快速熟悉框架的开发流程。
|
||||
|
||||
在开始之前,你需要先准备一个可用的 OneBot 机器人实现端(客户端)。
|
||||
|
||||
其中一些可用的选项为:
|
||||
|
||||
- Walle-Q
|
||||
- 更多…
|
||||
|
||||
## 机器人实现端
|
||||
|
||||
> 机器人实现端与炸毛框架相互独立。关于实现端本身的问题请向对应的开发者反馈。如果你确定相关问题由炸毛框架引起(例如缺少适配或代码问题等)请向我们报告。
|
||||
|
||||
框架支持多种通信方式,这里将以反向 WebSocket 为例,即框架充当 WS 服务端,实现端作为 WS 客户端连接到框架。
|
||||
|
||||
这里以 Walle-Q 实现端为例,在实际使用中,你可以自由选用不同的实现端。
|
||||
|
||||
你可以前往 Walle-Q 的 [发布页面](https://github.com/onebot-walle/walle-q/releases) 下载最新的发行版本,并运行以进行初始化。
|
||||
|
||||
在登录成功后,请关闭 Walle-Q 以修改配置文件。
|
||||
|
||||
首次运行后,将会在当前目录下生成 `walle-q.toml` 文件。在大多数场景下,这是你唯一需要接触的文件。
|
||||
|
||||
在于框架对接的情况中,我们只需要关注 `onebot.websocket_rev` 配置。
|
||||
|
||||
```toml
|
||||
[onebot]
|
||||
http = []
|
||||
http_webhook = []
|
||||
websocket = []
|
||||
|
||||
[[onebot.websocket_rev]]
|
||||
url = "ws://127.0.0.1:20001" # 这里是框架的监听地址
|
||||
reconnect_interval = 4
|
||||
```
|
||||
|
||||
修改完成并保存后,重新启动 Walle-Q 并登录即可。如果出现连接失败也请勿惊慌,因为框架此时尚未启动,失败是正常现象。
|
||||
|
||||
有些情况可能无法正常扫码登录,可使用 QQ+密码 的方式登录,在最上方插入配置:
|
||||
|
||||
```toml
|
||||
[qq.123456]
|
||||
password = "MyPassword"
|
||||
```
|
||||
|
||||
> 请将 123456 替换为你的机器人 QQ 号码,MyPassword 替换为机器人 QQ 密码。
|
||||
|
||||
## 编写第一个功能
|
||||
|
||||
在框架中,几乎所有事件的绑定都是通过注解进行的,详情可以参阅 注解的使用。
|
||||
|
||||
让我们新建第一个插件,插件的功能很简单,就是复读。我们假设这个复读插件的名字是 `repeater`
|
||||
|
||||
```bash
|
||||
./zhamao plugin:make
|
||||
# 然后根据提示,创建,比如名字输入 repeater
|
||||
# 选择类型的时候,输入 file
|
||||
```
|
||||
|
||||
我们就可以在目录 `plugins/repeater/` 下得到两个文件,其中 `main.php` 代码可能如下:
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
$plugin = new ZMPlugin(__DIR__);
|
||||
|
||||
/*
|
||||
* 发送 "测试repeater",回复 "这是repeater插件的第一个命令!"
|
||||
*/
|
||||
$cmd1 = BotCommand::make('repeater', match: '测试repeater')->on(fn () => '这是repeater插件的第一个命令!');
|
||||
|
||||
$plugin->addBotCommand($cmd1);
|
||||
|
||||
return $plugin;
|
||||
```
|
||||
|
||||
然后根据复读的原理(简单重复一遍用户发的消息),将上方 `$cmd1` 替换为下面的指令:
|
||||
|
||||
```php
|
||||
$cmd1 = BotCommand::make('repeater', match: '复读')->on(function(OneBotEvent $event, BotContext $ctx) {
|
||||
$ctx->reply($event->getMessage());
|
||||
});
|
||||
```
|
||||
|
||||
此后,保存文件。
|
||||
|
||||
> 借助容器的依赖注入功能,我们可以直接指定相应的类,相关实例会在调用时自动传入。上方的 OneBotEvent 和 BotContext 可以自由选择位置。
|
||||
|
||||
## 启动框架
|
||||
|
||||
在保存了上述的代码后,你就可以通过 `./zhamao server` 启动框架了。
|
||||
|
||||
启动后,Walle-Q 的日志应当会显示连接成功的信息。
|
||||
|
||||
此时,你可以通过任意账号向机器人发送 `复读 给我复读` 消息,机器人会回复 `复读 给我复读`。
|
||||
|
||||
至此,你的第一个功能,复读机,也就开发完成了。
|
||||
|
||||
<chat-box :my-chats="[
|
||||
{type:0,content:'复读 给我复读'},
|
||||
{type:1,content:'复读 给我复读'},
|
||||
]"></chat-box>
|
||||
|
||||
## 使用机器人 API 和更多事件
|
||||
|
||||
如果你希望机器人进行其他复杂的动作(操作),请参见 机器人 API。
|
||||
|
||||
关于更多可以注册绑定的事件,请参见 注解事件。
|
||||
@@ -1,134 +1,88 @@
|
||||
# 安装
|
||||
|
||||
> 这篇为炸毛框架以及环境的部署教程。
|
||||
我们希望尽可能轻松地使用炸毛,不论是本地开发或是部署都提供多种选择,尽量覆盖所有需求。
|
||||
|
||||
框架部署分为环境部署和框架部署。框架部署非常简单,只需要通用的指令,下方主要说环境部署。
|
||||
- 一键脚本
|
||||
- Docker
|
||||
- Composer
|
||||
|
||||
## Docker 部署 PHP 环境
|
||||
如果你不想干扰主机的环境,可以使用 Docker 进行拉取框架适用的 PHP7 with Swoole Extension Docker Container。本框架安装教程中使用的 DockerHub 及 Dockerfile 构建文件所构建的容器均为独立的容器,和框架无关,此 Docker 也可以用作运行**其他基于 php-cli 模式的项目**。
|
||||
## 一键脚本
|
||||
|
||||
方法一、直接拉取远程容器(推荐)
|
||||
```bash
|
||||
docker pull zmbot/swoole
|
||||
炸毛框架提供了一键脚本来设置运行环境并拉取脚手架,帮助你快速进行开发。
|
||||
|
||||
如果检测到本机未安装 PHP 或不符合运行要求,脚本将会自动拉取提前编译好的静态 PHP 运行时。
|
||||
|
||||
```shell
|
||||
# 将静态 PHP 和框架安装在当前目录(适用于 Linux、macOS)
|
||||
bash <(curl -fsSL https://zhamao.xin/v3.sh)
|
||||
|
||||
# 安装完成后启动
|
||||
./zhamao server
|
||||
```
|
||||
|
||||
方法二、从 Dockerfile 构建容器
|
||||
```bash
|
||||
git clone https://github.com/zhamao-robot/zhamao-swoole-docker.git
|
||||
cd zhamao-swoole-docker/
|
||||
docker build -t zm .
|
||||
> 关于静态运行时的更多用法,请参见 这里是链接
|
||||
|
||||
## Docker
|
||||
|
||||
待完善
|
||||
|
||||
## Composer
|
||||
|
||||
如果你已经有了必要的 PHP 环境和 Composer 工具,你可以直接在任意目录下初始化框架。
|
||||
|
||||
> 由于目前 3.0 仍处于预发布阶段,请使用 `composer require zhamao/framework:^3` 来安装
|
||||
|
||||
```shell
|
||||
# 在当前目录初始化框架
|
||||
composer require zhamao/framework
|
||||
./vendor/bin/zhamao init
|
||||
|
||||
## 安装完成后启动
|
||||
./zhamao server
|
||||
|
||||
## 生成新插件脚手架,用于开发
|
||||
./zhamao plugin:make
|
||||
```
|
||||
|
||||
!!! note "从 Dockerfile 构建容器的提示"
|
||||
## Windows 安装方法
|
||||
|
||||
使用 Dockerfile 构建后,需要将下方所有的 `zmbot/swoole` 全部更换成 `zm`,或者你上方指令中的 `-t` 参数后方的名称,具体可以详情查阅 Docker 的文档。
|
||||
由于 Windows 系统下的 PHP 环境配置较为复杂,我们推荐使用 Docker 或一键脚本来进行安装。
|
||||
|
||||
## 主机部署 PHP 环境
|
||||
如果你打算在 Windows 使用原生的 Win 环境 PHP,你需要先安装 PHP 和 Composer,然后在任意目录下执行上方 composer 的安装方法即可。
|
||||
|
||||
### Debian 系列(Ubuntu、Kali )
|
||||
### 包管理安装
|
||||
|
||||
需要的系统内软件包为:`php php-dev php-mbstring gcc make openssl php-mbstring php-json php-curl php-mysql wget composer`
|
||||
Windows 也可以使用包管理安装 PHP、Composer,例如你可以使用 Scoop 包管理进行安装:
|
||||
|
||||
下面是一个一键安装的命令行(最小安装,需 root 权限):
|
||||
|
||||
```bash
|
||||
apt-get update && apt-get install -y software-properties-common && add-apt-repository ppa:ondrej/php && apt-get update && apt-get install php php-dev php-mbstring gcc make openssl php-mbstring php-json php-curl php-mysql -y && apt-get install wget composer -y && wget https://github.com/swoole/swoole-src/archive/v4.5.7.tar.gz && tar -zxvf v4.5.7.tar.gz && cd swoole-src-4.5.7/ && phpize && ./configure --enable-openssl --enable-mysqlnd && make -j2 && make install && (echo "extension=swoole.so" >> $(php -i | grep "Loaded Configuration File" | awk '{print $5}'))
|
||||
```powershell
|
||||
scoop install php
|
||||
scoop install composer
|
||||
```
|
||||
|
||||
### macOS (with Homebrew)
|
||||
采用这种包管理安装后,可直接使用 `php`、`composer` 命令在任意位置,无需配置环境变量。
|
||||
|
||||
macOS 系统下的部署相对简单很多,只需要使用 Homebrew 安装以下包和执行安装命令即可
|
||||
如果你使用包管理或已经安装了 PHP 到系统内,接下来就直接使用 Composer 来安装框架即可!
|
||||
|
||||
!!! note "给 macOS 开发者的提示"
|
||||
|
||||
因为苹果新的 Apple Sillicon 对 Homebrew 的支持目前仅限于 Rosetta2 转译版,
|
||||
所以在使用 M1-based Mac 时出现问题暂时无解。
|
||||
使用以下指令可能会遇到报错等问题,如有疑问可直接使用 Docker 或咨询我(炸毛框架开发者)。
|
||||
|
||||
```bash
|
||||
brew install php composer
|
||||
pecl install swoole
|
||||
```powershell
|
||||
composer create-project zhamao/framework-starter zhamao-v3
|
||||
cd zhamao-v3
|
||||
./zhamao plugin:make
|
||||
./zhamao server
|
||||
```
|
||||
|
||||
### 其他 Linux 发行版
|
||||
### 纯手动安装
|
||||
|
||||
其他 Linux 发行版,如 CentOS,Fedora,Arch 等暂时还没有经过严格的测试需要哪些依赖,大体和 Ubuntu、Debian 系需要的依赖包差不多,可根据安装过程中报错提示依次安装,或者直接使用 Docker 环境。
|
||||
如果你不想使用包管理的方式安装 PHP,且让 PHP 仅框架独立使用,你可以依次采用以下的方式来安装 PHP、Composer 和框架:
|
||||
|
||||
## 安装框架
|
||||
1. 从 GitHub 下载框架的脚手架,地址:<https://github.com/zhamao-robot/zhamao-framework-starter/archive/refs/heads/master.zip>
|
||||
2. 解压框架脚手架,重命名文件夹名称为你自己喜欢的名称,例如 `zhamao-v3`。
|
||||
3. 从 PHP 官网下载 PHP,选择 `Non Thread Safe` 版本,PHP 版本选择 8.0 ~ 8.2 均可(推荐 8.1),下载完成后解压到框架目录下的 `runtime\php` 目录,例如 `D:\zhamao-v3\runtime\php\`。
|
||||
4. 从 [Composer 官网](https://getcomposer.org/download/) 或 [阿里云镜像](https://mirrors.aliyun.com/composer/composer.phar) 下载 Composer,下载到 `runtime\` 目录。
|
||||
5. 在你的脚手架目录下执行 `.\runtime\php\php.exe .\runtime\composer.phar install` 安装框架依赖。
|
||||
6. 执行框架初始化命令:`./zhamao init`。
|
||||
7. 接下来你就可以使用和上方所有框架操作指令相同的内容了,例如 `./zhamao plugin:make`、`./zhamao server` 等。
|
||||
8. 如果你需要使用 Composer,你可以使用 `.\runtime\php\php.exe .\runtime\composer.phar` 来代替 `composer` 命令。
|
||||
|
||||
恭喜你,前方通过 Docker 或主机安装环境后可以开始构建框架的开发脚手架了!
|
||||
## 更多的环境部署和开发方式
|
||||
|
||||
如果你是通过**主机安装 PHP 部署的环境**,下方是通过脚手架来构建项目的命令行。
|
||||
|
||||
```bash
|
||||
git clone https://github.com/zhamao-robot/zhamao-framework-starter.git
|
||||
cd zhamao-framework-starter/
|
||||
composer update
|
||||
```
|
||||
|
||||
如果是通过 **Docker 部署的环境**,则需要在先克隆脚手架后在文件夹内使用 Docker 命令下的 `composer update`。
|
||||
|
||||
```bash
|
||||
git clone https://github.com/zhamao-robot/zhamao-framework-starter.git
|
||||
cd zhamao-framework-starter/
|
||||
docker run -it --rm -v $(pwd):/app/ -p 20001:20001 zmbot/swoole composer update
|
||||
```
|
||||
|
||||
或者在 Docker 环境下,你可以直接使用如下方法拉取和快速启动一个最标准的框架。
|
||||
|
||||
```bash
|
||||
git clone https://github.com/zhamao-robot/zhamao-framework-starter.git
|
||||
cd zhamao-framework-starter
|
||||
./run-docker.sh # 在正式版炸毛框架 v2 发布后可用,测试版暂不放出
|
||||
```
|
||||
|
||||
|
||||
## 启动框架
|
||||
本地环境启动方式:
|
||||
```bash
|
||||
cd zhamao-framework-starter
|
||||
vendor/bin/start server
|
||||
```
|
||||
|
||||
使用 Docker 启动:
|
||||
```bash
|
||||
cd zhamao-framework-starter
|
||||
docker run -it --rm -v $(pwd):/app/ -p 20001:20001 zmbot/swoole vendor/bin/start server
|
||||
```
|
||||
|
||||
启动后你会看到和下方类似的初始化内容,表明启动成功了
|
||||
|
||||
```verilog
|
||||
$ vendor/bin/start server
|
||||
host: 0.0.0.0 | port: 20001
|
||||
log_level: 2 | version: 2.0.0
|
||||
config: global.php | worker_num: 4
|
||||
working_dir: /Users/jerry/project/git-project/zhamao-framework
|
||||
______
|
||||
|__ / |__ __ _ _ __ ___ __ _ ___
|
||||
/ /| '_ \ / _` | '_ ` _ \ / _` |/ _ \
|
||||
/ /_| | | | (_| | | | | | | (_| | (_) |
|
||||
/____|_| |_|\__,_|_| |_| |_|\__,_|\___/
|
||||
|
||||
[14:27:31] [I] [#0] Worker #0 启动中
|
||||
[14:27:31] [I] [#2] Worker #2 启动中
|
||||
[14:27:31] [I] [#1] Worker #1 启动中
|
||||
[14:27:31] [I] [#3] Worker #3 启动中
|
||||
[14:27:31] [S] [#3] Worker #3 已启动
|
||||
[14:27:31] [S] [#0] Worker #0 已启动
|
||||
[14:27:31] [S] [#2] Worker #2 已启动
|
||||
[14:27:31] [S] [#1] Worker #1 已启动
|
||||
```
|
||||
|
||||
单纯运行 炸毛框架 后,如果不部署或安装启动任何机器人客户端的话,仅仅相当于启动了一个 监听 20001 端口的WebSoket + HTTP 服务器。你可以通过浏览器访问:http://127.0.0.1:20001 ,或者你部署到了服务器后需要输入服务器地址。
|
||||
|
||||
!!! note "安装和部署总结"
|
||||
|
||||
根据上方描述,此文档中剩余提到的所有 Bash 命令,如果使用 Docker 部署环境,则需要加上 Docker 环境的指令:`docker run -it --rm -v $(pwd):/app/ -p 20001:20001 zmbot/swoole`,如执行其他 Linux 指令(以查看 PHP 版本为例):`docker run -it --rm -v $(pwd):/app/ -p 20001:20001 zmbot/swoole php -v`。
|
||||
|
||||
## 使用 IDE 等工具开发代码
|
||||
|
||||
我们使用文本编辑器进行炸毛框架开发,在使用集成开发环境 **IDEA** 或 **PhpStorm** 时,推荐通过插件市场搜索并安装 **PHP Annotations** 插件以提供注解命名空间自动补全、注解属性代码提醒、注解类跳转等,非常有助于提升开发效率的功能。
|
||||
|
||||
## 进阶环境部署和开发
|
||||
炸毛框架还支持更多种启动方式,如源码模式、守护进程模式,具体后续有关环境和部署的进阶教程,请查看 [进阶开发](/advanced/) 部分!
|
||||
除了上述方式之外,框架还支持源码模式、守护进程等运行方式,详情请参阅 [进阶开发]。
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
# OneBot 实例
|
||||
|
||||
## 什么是 OneBot
|
||||
|
||||
OneBot 是一个聊天机器人应用接口标准,详情戳[这里](https://github.com/howmanybots/onebot)。
|
||||
|
||||
## OneBot 实现选择
|
||||
|
||||
如果你使用炸毛框架作为聊天机器人的开发框架,请先选择一种兼容 OneBot 标准的机器人接口。理论上,基于 OneBot 标准开发的**任何** SDK、框架和机器人应用,都可以无缝地在下面的不同实现中切换。当然,在一小部分细节上各实现可能有一些不同。
|
||||
|
||||
| 项目地址 | 平台 | 核心作者 | 备注 |
|
||||
| ------------------------------------------------------------ | --------------------------------------------- | -------------- | ------------------------------------------------------------ |
|
||||
| [richardchien/coolq-http-api](https://github.com/richardchien/coolq-http-api) | CKYU | richardchien | 可在 Mirai 平台使用 [mirai-native](https://github.com/iTXTech/mirai-native) 加载 |
|
||||
| [Mrs4s/go-cqhttp](https://github.com/Mrs4s/go-cqhttp) | [MiraiGo](https://github.com/Mrs4s/MiraiGo) | Mrs4s | 炸毛框架推荐使用此项目机器人应用 |
|
||||
| [yyuueexxiinngg/cqhttp-mirai](https://github.com/yyuueexxiinngg/cqhttp-mirai) | [Mirai](https://github.com/mamoe/mirai) | yyuueexxiinngg | |
|
||||
| [takayama-lily/onebot](https://github.com/takayama-lily/onebot) | [OICQ](https://github.com/takayama-lily/oicq) | takayama | |
|
||||
| [ProtobufBot](https://github.com/ProtobufBot) | [Mirai](https://github.com/mamoe/mirai) | lz1998 | 事件和 API 数据内容和 OneBot 一致,通信方式不兼容 |
|
||||
|
||||
!!! warning "注意"
|
||||
|
||||
因为目前炸毛框架 2.0 只支持 WebSocket 方式的 OneBot 实现,所以目前上述项目的连接方式均只可选支持反向 WebSocket 通信的。后期会兼容 HTTP 和正向 WebSocket 通信方式。
|
||||
|
||||
如果你还没有自己的 QQ,或者是其他原因导致的暂时无法使用上述 OneBot 实例,可以使用炸毛项目中的 OneBot 协议聊天模拟器。但目前还处在开发中,暂不可用。
|
||||
@@ -1,4 +0,0 @@
|
||||
# 快速上手 - HTTP 服务器篇
|
||||
|
||||
HTTP 服务器篇暂时先放一放,大家应该主要都是奔着机器人开发来的吧~
|
||||
|
||||
@@ -1,228 +0,0 @@
|
||||
# 快速上手 - 机器人篇
|
||||
|
||||
|
||||
|
||||
## 简介
|
||||
|
||||
看到这里,你已经完成了前面的环境部署,到了最关键的第一步了!
|
||||
|
||||
一切都安装成功后,你就已经做好了进行简单配置以运行一个最小的 **机器人问答模块** 的准备。
|
||||
|
||||
炸毛框架和机器人客户端是什么关系呢?炸毛框架就好比我们传统的一系列例如 Spring 框架、ThinkPHP 框架等,是服务端,而机器人客户端是一个 HTTP / WebSocket 客户端,时刻准备着连接到炸毛框架的。
|
||||
|
||||
## 机器人客户端
|
||||
|
||||
开发之前请注意,**机器人客户端和框架相互独立!**故有关**机器人客户端**出现的问题请到对应机器人客户端开发者或 GitHub 项目中咨询和讨论,炸毛框架为对接机器人客户端的一个快速开发的框架。
|
||||
|
||||
机器人客户端是炸毛框架以外的程序或软件,目前炸毛框架支持的机器人客户端通信标准为 OneBot 标准(原 CQHTTP),只要你的机器人客户端是 OneBot 标准的,就可以和炸毛框架进行无缝对接。
|
||||
|
||||
OneBot 机器人部分的选择详情见 [OneBot 实例](/guide/OneBot实例/)。
|
||||
|
||||
这里以炸毛框架开发过程中使用的 [go-cqhttp](https://github.com/Mrs4s/go-cqhttp) 来举例进行第一个机器人的配置工作。
|
||||
|
||||
简要步骤描述为:
|
||||
|
||||
1. 下载 go-cqhttp 对应平台的 [release 文件](https://github.com/Mrs4s/go-cqhttp/releases)
|
||||
2. 双击 exe 文件或者使用 `./go-cqhttp` 启动
|
||||
3. 生成默认配置文件并修改默认配置
|
||||
|
||||
!!! warning "注意"
|
||||
|
||||
由于 go-cqhttp 项目还处于开发期,而且配置文件格式也发生了多次变化,但大体内容没有变(比如编写此文档时发布的版本中配置文件格式变成了 `hjson` 取代了原来的 `json`。
|
||||
|
||||
=== "config.json(旧格式)"
|
||||
|
||||
``` json hl_lines="2 3 30 31"
|
||||
{
|
||||
"uin": 你的QQ号,
|
||||
"password": "你的密码",
|
||||
"encrypt_password": false,
|
||||
"password_encrypted": "",
|
||||
"enable_db": true,
|
||||
"access_token": "",
|
||||
"relogin": {
|
||||
"enabled": true,
|
||||
"relogin_delay": 3,
|
||||
"max_relogin_times": 0
|
||||
},
|
||||
"ignore_invalid_cqcode": false,
|
||||
"force_fragmented": true,
|
||||
"heartbeat_interval": 0,
|
||||
"http_config": {
|
||||
"enabled": false,
|
||||
"host": "0.0.0.0",
|
||||
"port": 5700,
|
||||
"timeout": 0,
|
||||
"post_urls": {}
|
||||
},
|
||||
"ws_config": {
|
||||
"enabled": false,
|
||||
"host": "0.0.0.0",
|
||||
"port": 6700
|
||||
},
|
||||
"ws_reverse_servers": [
|
||||
{
|
||||
"enabled": true,
|
||||
"reverse_url": "ws://127.0.0.1:20001/",
|
||||
"reverse_api_url": "",
|
||||
"reverse_event_url": "",
|
||||
"reverse_reconnect_interval": 3000
|
||||
}
|
||||
],
|
||||
"post_message_format": "string",
|
||||
"debug": false,
|
||||
"log_level": ""
|
||||
}
|
||||
```
|
||||
|
||||
=== "config.hjson(新格式)"
|
||||
|
||||
``` json hl_lines="3 5 81 84"
|
||||
{
|
||||
// QQ号
|
||||
uin: 你的机器人QQ
|
||||
// QQ密码
|
||||
password: "你的QQ密码"
|
||||
// 是否启用密码加密
|
||||
encrypt_password: false
|
||||
// 加密后的密码, 如未启用密码加密将为空, 请勿随意修改.
|
||||
password_encrypted: ""
|
||||
// 是否启用内置数据库
|
||||
// 启用将会增加10-20MB的内存占用和一定的磁盘空间
|
||||
// 关闭将无法使用 撤回 回复 get_msg 等上下文相关功能
|
||||
enable_db: true
|
||||
// 访问密钥, 强烈推荐在公网的服务器设置
|
||||
access_token: ""
|
||||
// 重连设置
|
||||
relogin: {
|
||||
// 是否启用自动重连
|
||||
// 如不启用掉线后将不会自动重连
|
||||
enabled: true
|
||||
// 重连延迟, 单位秒
|
||||
relogin_delay: 3
|
||||
// 最大重连次数, 0为无限制
|
||||
max_relogin_times: 0
|
||||
}
|
||||
// API限速设置
|
||||
// 该设置为全局生效
|
||||
// 原 cqhttp 虽然启用了 rate_limit 后缀, 但是基本没插件适配
|
||||
// 目前该限速设置为令牌桶算法, 请参考:
|
||||
//https://baike.baidu.com/item/%E4%BB%A4%E7%89%8C%E6%A1%B6%E7%AE%97%E6%B3%95/6597000?fr=aladdin
|
||||
_rate_limit: {
|
||||
// 是否启用限速
|
||||
enabled: false
|
||||
// 令牌回复频率, 单位秒
|
||||
frequency: 1
|
||||
// 令牌桶大小
|
||||
bucket_size: 1
|
||||
}
|
||||
// 是否忽略无效的CQ码
|
||||
// 如果为假将原样发送
|
||||
ignore_invalid_cqcode: false
|
||||
// 是否强制分片发送消息
|
||||
// 分片发送将会带来更快的速度
|
||||
// 但是兼容性会有些问题
|
||||
force_fragmented: false
|
||||
// 心跳频率, 单位秒
|
||||
// -1 为关闭心跳
|
||||
heartbeat_interval: 0
|
||||
// HTTP设置
|
||||
http_config: {
|
||||
// 是否启用正向HTTP服务器
|
||||
enabled: true
|
||||
// 服务端监听地址
|
||||
host: 0.0.0.0
|
||||
// 服务端监听端口
|
||||
port: 5700
|
||||
// 反向HTTP超时时间, 单位秒
|
||||
// 最小值为5,小于5将会忽略本项设置
|
||||
timeout: 0
|
||||
// 反向HTTP POST地址列表
|
||||
// 格式:
|
||||
// {
|
||||
// 地址: secret
|
||||
// }
|
||||
post_urls: {}
|
||||
}
|
||||
// 正向WS设置
|
||||
ws_config: {
|
||||
// 是否启用正向WS服务器
|
||||
enabled: true
|
||||
// 正向WS服务器监听地址
|
||||
host: 0.0.0.0
|
||||
// 正向WS服务器监听端口
|
||||
port: 6700
|
||||
}
|
||||
// 反向WS设置
|
||||
ws_reverse_servers: [
|
||||
// 可以添加多个反向WS推送
|
||||
{
|
||||
// 是否启用该推送
|
||||
enabled: true
|
||||
// 反向WS Universal 地址
|
||||
// 注意 设置了此项地址后下面两项将会被忽略
|
||||
reverse_url: ws://127.0.0.1:20001/
|
||||
// 反向WS API 地址
|
||||
reverse_api_url: ""
|
||||
// 反向WS Event 地址
|
||||
reverse_event_url: ""
|
||||
// 重连间隔 单位毫秒
|
||||
reverse_reconnect_interval: 3000
|
||||
}
|
||||
]
|
||||
// 上报数据类型
|
||||
// 可选: string array
|
||||
post_message_format: string
|
||||
// 是否使用服务器下发的新地址进行重连
|
||||
// 注意, 此设置可能导致在海外服务器上连接情况更差
|
||||
use_sso_address: false
|
||||
// 是否启用 DEBUG
|
||||
debug: false
|
||||
// 日志等级
|
||||
// WebUi 设置
|
||||
web_ui: {
|
||||
// 是否启用 WebUi
|
||||
enabled: true
|
||||
// 监听地址
|
||||
host: 127.0.0.1
|
||||
// 监听端口
|
||||
web_ui_port: 9999
|
||||
// 是否接收来自web的输入
|
||||
web_input: false
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
其中 ws://127.0.0.1:20001/ 中的 127.0.0.1 和 20001 应分别对应炸毛框架配置的 HOST 和 PORT
|
||||
|
||||
## 第一次对话
|
||||
|
||||
一旦新的配置文件正确生效之后,所在的控制台(如果正在运行的话)应该会输出类似下面的内容:
|
||||
|
||||
```verilog
|
||||
[15:26:34] [I] [#2] 机器人 你的QQ号 已连接!
|
||||
```
|
||||
|
||||
表明机器人已成功连接到炸毛框架了!
|
||||
|
||||
这时,如果你是根据安装教程走下来并且未编写任何模块,炸毛自带一个示例模块,里面含有命令:`你好`,`随机数`。如果你对机器人回复:`你好`,它会回复你 `你好啊,我是由炸毛框架构建的机器人!`。这一历史性的对话标志着你已经成功地运行了炸毛框架,开始了编写更强大的 QQ 机器人的创意之旅!
|
||||
|
||||
## 编写一个命令
|
||||
|
||||
让我们转到框架的模块源代码部分,目录是 `src/Module/Example`,文件是 `Hello.php`。我们插入一段这样的代码:
|
||||
|
||||
```php
|
||||
/**
|
||||
* @CQCommand("echo")
|
||||
*/
|
||||
public function repeat() {
|
||||
$repeat = ctx()->getFullArg("请输入你要回复的内容");
|
||||
ctx()->reply($repeat);
|
||||
//return $repeat; // 这样的效果等同于 ctx()->reply()
|
||||
}
|
||||
```
|
||||
|
||||
这样,一个简易的复读机就做好了!回到 QQ 机器人聊天,向机器人发送 `echo 你好啊`,它会回复你 `你好啊`。
|
||||
|
||||
> 如果你只回复 `echo` 的话,它会先和你进入一个会话状态,并问你 `请输入你要回复的内容`,这时你再次说一些内容例如 `哦豁`,会回复你 `哦豁`。效果和直接输入 `echo 哦豁` 是一致的,这是炸毛框架内的一个封装好的命令参数对话询问功能。有关参数询问功能,请看后面的进阶模块。
|
||||
|
||||
@@ -1,66 +0,0 @@
|
||||
# 注册事件响应(机器人篇)
|
||||
|
||||
现在模块已经创建完毕,我们可以开始编写实际代码了。本段以机器人会话为例子来讲述事件注册和响应,有关 HTTP 服务器等注册事件响应请看后面事件和注解章节。
|
||||
|
||||
## 机器人聊天事件处理
|
||||
|
||||
首先知道,QQ 等聊天机器人的消息我们的处理逻辑为如下简单的模式:
|
||||
|
||||
- QQ 用户消息 -> 机器人客户端 -> 连接客户端的框架(炸毛框架)
|
||||
- 框架处理逻辑后返回给用户的消息 -> 机器人客户端 -> QQ用户
|
||||
|
||||
第一步,我们以框架这边的角度考虑,我们称之为“事件”,我们编写代码所做的就是要响应这一事件。
|
||||
|
||||
首先我们以一句简单的功能——查天气,我们要从零实现一个查天气的功能进行示范如何快捷有效地开发一个功能。
|
||||
|
||||
### 确定问法
|
||||
|
||||
我们首先要确定的用户问法是一般由我们自己定义,但最好贴合用户的自然语言来进行定义。比如我们这里提到的天气功能,用户一般就会询问“北京天气”,“北京天气怎么样”,“天气 北京”。
|
||||
|
||||
### 注册消息事件
|
||||
|
||||
我们以最简单的命令方式“天气 北京”进行处理。问法为参数化的,通过空格来分开,这也是炸毛框架默认支持最基本的聊天事件之一。我们通过上一部分的方式新建一个单文件模块 `Weather.php` 在 `src/Module` 目录下,并编写:
|
||||
|
||||
```php
|
||||
<?php
|
||||
namespace Module;
|
||||
use ZM\Annotation\CQ\CQCommand;
|
||||
class Weather {
|
||||
/**
|
||||
* @CQCommand("天气")
|
||||
* @return string
|
||||
*/
|
||||
public function searchWeather() {
|
||||
$city = ctx()->getNextArg("请告诉我你要查询的城市"); // 发送 “天气 北京”时,变量为“北京”
|
||||
// 这里假设是天气API接口的对接,返回了天气的数据
|
||||
$weather = "2020年12月22日,晴,-2~9℃ blablabla";
|
||||
return "$city 天气情况:".$weather;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
!!! note "提示"
|
||||
为了简单起见,我们在这里的例子中没有接入真实的天气数据,但要接入也非常简单,你可以使用中国天气网、和风天气等网站提供的 API,本教程的进阶一栏后期会详细编写如何对接一个天气 API 接口。
|
||||
|
||||
在上方代码编写完毕后,运行框架(或运行过程中终端输入 `reload`),然后使用机器人客户端连接到炸毛框架,即可实现我们的第一个功能。我们在代码中编写了一个**注解事件**:`@CQCommand`,此注解事件是用于接收用户的普通消息并切分成各类命令规则的一个注解事件绑定。代码中的注解事件省去了注解中的键名,也可以写作 `@CQCommand(match="天气")`。
|
||||
|
||||
这里注解事件的概念请看 [事件和注解](/event/) 一栏描述的概念即可。到这里,我们就完成一个可以处理命令 `天气 xxx` 的方法了!
|
||||
|
||||
### 处理消息事件
|
||||
|
||||
第一行 `ctx()` 是炸毛框架内的上下文获取方式,每条用户聊天信息发过来,被炸毛框架收到,都会创建一次上下文,同时这次聊天的全部信息,比如用户的 ID(QQ 号码),发消息的时间,如果是群消息的话所在的群号等等,都被存到了上下文中。`ctx()` 获取的是一个上下文对象,内部有许多可操作上下文的方法,其中代码的 `getNextArg()`,作用是根据空格分隔获取命令中的下一个参数。
|
||||
|
||||
在之前我们知道:`天气 北京` 是我们发送的消息,我们要获取到用户发送的参数 `北京`。`getNextArg()` 是框架封装好的一个快速获取下一个参数的方法,我们这里直接使用它来获取。
|
||||
|
||||
对于 `getNextArg()` 中的文本,可为空,不为空的时候如果用户只发送天气两个字,机器人还是会响应,但是它会询问你这句话,然后你回复机器人“北京”,这里 `$city` 变量就接受到并赋值为“北京”了,代码会继续执行,和直接一次性发送机器人“天气 北京”是一个效果,此为框架封装的消息会话机制,以贴近自然会话的方式来编写代码逻辑。
|
||||
|
||||
最后,函数直接返回了一个字符串,作为事件的响应,炸毛框架会自动处理并调用机器人客户端的接口,最后返回给用户消息。这里也可以使用上下文的 `ctx()->reply("xxx")` 方法替代,不返回字符串。注意两者只能选择一种方式,取决于开发者的开发习惯。
|
||||
|
||||
<chat-box>
|
||||
) 天气 北京
|
||||
( 北京 天气情况:2020年12月22日,晴,-2~9℃ blablabla
|
||||
) 天气
|
||||
( 请告诉我你要查询的城市
|
||||
) 北京
|
||||
( 北京 天气情况:2020年12月22日,晴,-2~9℃ blablabla
|
||||
</chat-box>
|
||||
36
docs/guide/structure.md
Normal file
@@ -0,0 +1,36 @@
|
||||
# 目录结构
|
||||
|
||||
## 用户目录
|
||||
|
||||
### config 目录
|
||||
|
||||
`config` 目录包含框架、应用的所有配置文件。最好把这些文件都浏览一遍,并熟悉所有可用的选项。
|
||||
|
||||
```
|
||||
config/
|
||||
├── global.php # 全局配置文件
|
||||
├── container.php # 容器配置文件
|
||||
└── motd.txt # 框架启动时展示的文字信息
|
||||
```
|
||||
|
||||
### vendor 目录
|
||||
|
||||
`vendor` 目录包含你通过 Composer 安装的所有依赖,此目录为自动生成,无需操作。
|
||||
|
||||
### plugins 目录
|
||||
|
||||
`plugins` 目录包含你编写或加载到源代码模式的插件,里面的插件都会被框架自动扫描并解析,你可以在其中利用注解来注册事件绑定并进行相应处理。
|
||||
|
||||
比如你通过 `./zhamao plugin:make` 新建了一个名字叫 `test-app` 的插件,并且设置为单文件模式(`file`),那么这个插件内包含的文件及结构为:
|
||||
|
||||
```
|
||||
plugins/
|
||||
└── test-app/
|
||||
├── main.php # 你的插件源代码文件
|
||||
└── zmplugin.json # 插件元信息(如名称、版本等)
|
||||
```
|
||||
|
||||
### zm_data 目录
|
||||
|
||||
`zm_data` 目录存放了框架运行时持久化保存的数据,例如 KV 数据库、驱动日志等内容。
|
||||
|
||||
@@ -1,68 +0,0 @@
|
||||
# 编写模块
|
||||
|
||||
到现在为止,我们还在使用框架的默认模块 `Example/Hello.php`,在开始编写自己的模块应用之前,我们先说明一些编写代码的约定。
|
||||
|
||||
## 加载模块
|
||||
|
||||
框架默认使用脚手架构建好后,目录结构大致为下面这样:
|
||||
|
||||
```bash
|
||||
zhamao-framework-starter/
|
||||
├── config/ # 项目的配置文件文件夹,如 global.php
|
||||
├── src/ # 项目的主要源码目录
|
||||
│ ├── Module/ # 用户编写的模块目录
|
||||
│ │ └── Example/ # 模块文件夹名称
|
||||
│ │ └── Hello.php # 模块内的类
|
||||
│ └── Custom/ # 用户自定义的全局方法、全局注解类等存放的目录
|
||||
├── vendor/ # Composer 依赖加载目录
|
||||
└── composer.json # Composer 配置文件
|
||||
```
|
||||
|
||||
其中我们脚手架包含的默认模块 `Example` 下的 `Hello` 类,就是用户写模块的位置。你也可以根据实际情况,自行添加更多的模块文件夹甚至单文件模块。
|
||||
|
||||
需要注意的是,所有文件夹名称和 `.php` 文件必须遵循 [psr-4 规范](https://learnku.com/docs/psr/psr-4-autoloader/1608),简单来说,`src/` 目录下的文件夹,子文件夹要写成命名空间,比如默认框架中 `Example/` 下的 `.php` 文件的命名空间为 `namespace Module\Example;`,且一个 `.php` 文件推荐只包含一个 `class`、`trait` 或 `interface`。
|
||||
|
||||
```php
|
||||
<?php
|
||||
namespace Module\<your-module-dir>;
|
||||
class ModuleA {}
|
||||
```
|
||||
|
||||
!!! fail "警告"
|
||||
如果没有遵守上方的类和文件命名规则的话(文件名、文件夹名和命名空间的统一性),在加载框架时就会报错,无法找到对应的类。因为框架的注解解析依赖于 Composer 中 psr-4 规则的自动加载。
|
||||
|
||||
## 创建模块
|
||||
### 标准形式
|
||||
我们这里以 `Entertain` 娱乐模块的创建为例,新建一个内有 `Dice.php` 掷骰子功能的模块,目录结构如下,在 `Module/` 下新建文件夹 `Entertain/`,再在此子目录下新建 `Dice.php` 文件。
|
||||
```bash
|
||||
zhamao-framework-starter/
|
||||
└── src/
|
||||
└── Module/
|
||||
└── Entertain/
|
||||
└── Dice.php
|
||||
```
|
||||
新建的 PHP 文件按照如下方式编写:
|
||||
```php
|
||||
<?php
|
||||
namespace Module\Entertain;
|
||||
class Dice {
|
||||
}
|
||||
```
|
||||
|
||||
这个时候它已经可以被称为一个模块了,尽管它还什么都没做。
|
||||
|
||||
### 单文件形式
|
||||
|
||||
如果你只开发很简单的一些功能,如一个 PHP 文件就可以实现的,可以少去创建模块文件夹的一步,直接将 `.php` 文件新建到 `Module/` 文件夹下,这时此文件的命名空间需要更正为 `namespace Module;` 即可,而文件夹结构也更加简单:
|
||||
|
||||
```bash
|
||||
zhamao-framework-starter/
|
||||
└── src/
|
||||
└── Module/
|
||||
└── Dice.php
|
||||
```
|
||||
|
||||
### Composer 外部引入形式
|
||||
|
||||
(暂未支持,敬请期待)
|
||||
|
||||
130
docs/index.md
@@ -1,130 +0,0 @@
|
||||
# 介绍
|
||||
|
||||
> 本文档为炸毛框架 v2 版本,如需查看 v1 版本,[点我](https://docs-v1.zhamao.xin/)。
|
||||
|
||||
> 如果是从 v1.x 版本升级到 v2.x,[点我看升级指南](/advanced/to-v2/)。
|
||||
|
||||
炸毛框架使用 PHP 编写,采用 Swoole 扩展为基础,主要面向 API 服务,聊天机器人(CQHTTP 对接),包含 websocket、http 等监听和请求库,用户代码采用模块化处理,使用注解可以方便地编写各类功能。
|
||||
|
||||
框架主要用途为 HTTP 服务器,机器人搭建框架。尤其对于 QQ 机器人消息处理较为方便和全面,提供了众多会话机制和内部调用机制,可以以各种方式设计你自己的模块。
|
||||
|
||||
在 HTTP 和 WebSocket 服务器上,PHP 的扩展 Swoole 提供了高性能的支持,使其效率可媲美 nginx 静态网页处理的效率。
|
||||
|
||||
此外,QQ 机器人方面此框架基于 OneBot 标准的反向 WebSocket 连接,比传统 HTTP 通信更快,未来也会兼容微信公众号开发者模式。
|
||||
|
||||
```php
|
||||
/**
|
||||
* @CQCommand("你好")
|
||||
*/
|
||||
public function hello() {
|
||||
ctx()->reply("你好,我是炸毛!");
|
||||
}
|
||||
/**
|
||||
* @RequestMapping("/index")
|
||||
*/
|
||||
public function index() {
|
||||
return "<h1>hello!</h1>";
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
## 开始前
|
||||
|
||||
首先,你需要了解你需要知道哪些事情才能开始着手使用框架:
|
||||
|
||||
1. Linux 命令行基础
|
||||
2. php 7.2+ 开发环境
|
||||
3. HTTP 协议(可选)
|
||||
4. OneBot 机器人聊天接口标准(可选)
|
||||
|
||||
需要值得注意的是,本教程中所涉及的内容均为尽可能翻译为白话的方式进行描述,但对于框架的组件或事件等需要单独拆分说明文档的部分则需要足够详细,所以本教程提供一个快速上手的教程,并且会将最典型的安装方式写到快速教程篇。
|
||||
|
||||
!!! bug "文档提示"
|
||||
|
||||
此文档采用 MkDocs 驱动,但因为本文档的搜索组件原生不支持中文搜索,所以搜索体验会大打折扣,敬请谅解!搜不到不是没这个东西哦!
|
||||
|
||||
|
||||
## 框架特色
|
||||
- 支持MySQL数据库(连接池),自带查询缓存提高多查询时的效率
|
||||
- Websocket 服务器、HTTP 服务器兼容运行,一个框架多个用处
|
||||
- 支持命令、自然语言处理等多种插件形式
|
||||
- 支持多个机器人账号负载均衡
|
||||
- 协程 + TaskWorker 进程重度任务处理机制,保证高效,单个请求响应时间为 0.1 ms 左右
|
||||
- 模块分离和自由组合,可根据自身需求自己建立模块内的目录结构和代码结构
|
||||
- 灵活的注释注解注册事件方式,弥补 PHP 语言缺少注解的遗憾
|
||||
|
||||
## 文档主题
|
||||
|
||||
### 主题
|
||||
<div class="tx-switch">
|
||||
<button data-md-color-scheme="default"><code>默认模式</code></button>
|
||||
<button data-md-color-scheme="slate"><code>暗黑模式</code></button>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
var buttons = document.querySelectorAll("button[data-md-color-scheme]");
|
||||
buttons.forEach(function(button) {
|
||||
button.addEventListener("click", function() {
|
||||
var attr = this.getAttribute("data-md-color-scheme");
|
||||
setCookie("_theme", attr);
|
||||
document.body.setAttribute("data-md-color-scheme", attr);
|
||||
var name = document.querySelector("#__code_0 code span:nth-child(7)");
|
||||
name.textContent = attr;
|
||||
})
|
||||
})
|
||||
</script>
|
||||
|
||||
### 主色调
|
||||
<div class="tx-switch">
|
||||
<button data-md-color-primary="red"><code>red</code></button>
|
||||
<button data-md-color-primary="pink"><code>pink</code></button>
|
||||
<button data-md-color-primary="purple"><code>purple</code></button>
|
||||
<button data-md-color-primary="deep-purple"><code>deep purple</code></button>
|
||||
<button data-md-color-primary="indigo"><code>indigo</code></button>
|
||||
<button data-md-color-primary="blue"><code>blue</code></button>
|
||||
<button data-md-color-primary="light-blue"><code>light blue</code></button>
|
||||
<button data-md-color-primary="cyan"><code>cyan</code></button>
|
||||
<button data-md-color-primary="teal"><code>teal</code></button>
|
||||
<button data-md-color-primary="green"><code>green</code></button>
|
||||
<button data-md-color-primary="light-green"><code>light green</code></button>
|
||||
<button data-md-color-primary="lime"><code>lime</code></button>
|
||||
<button data-md-color-primary="yellow"><code>yellow</code></button>
|
||||
<button data-md-color-primary="amber"><code>amber</code></button>
|
||||
<button data-md-color-primary="orange"><code>orange</code></button>
|
||||
<button data-md-color-primary="deep-orange"><code>deep orange</code></button>
|
||||
<button data-md-color-primary="brown"><code>brown</code></button>
|
||||
<button data-md-color-primary="grey"><code>grey</code></button>
|
||||
<button data-md-color-primary="blue-grey"><code>blue grey</code></button>
|
||||
<button data-md-color-primary="black"><code>black</code></button>
|
||||
<button data-md-color-primary="white"><code>white</code></button>
|
||||
</div>
|
||||
|
||||
### 辅色调
|
||||
<div class="tx-switch"> <button data-md-color-accent="red"><code>red</code></button> <button data-md-color-accent="pink"><code>pink</code></button> <button data-md-color-accent="purple"><code>purple</code></button> <button data-md-color-accent="deep-purple"><code>deep purple</code></button> <button data-md-color-accent="indigo"><code>indigo</code></button> <button data-md-color-accent="blue"><code>blue</code></button> <button data-md-color-accent="light-blue"><code>light blue</code></button> <button data-md-color-accent="cyan"><code>cyan</code></button> <button data-md-color-accent="teal"><code>teal</code></button> <button data-md-color-accent="green"><code>green</code></button> <button data-md-color-accent="light-green"><code>light green</code></button> <button data-md-color-accent="lime"><code>lime</code></button> <button data-md-color-accent="yellow"><code>yellow</code></button> <button data-md-color-accent="amber"><code>amber</code></button> <button data-md-color-accent="orange"><code>orange</code></button> <button data-md-color-accent="deep-orange"><code>deep orange</code></button> </div>
|
||||
|
||||
<script>
|
||||
var buttons = document.querySelectorAll("button[data-md-color-primary]")
|
||||
buttons.forEach(function(button) {
|
||||
button.addEventListener("click", function() {
|
||||
var attr = this.getAttribute("data-md-color-primary")
|
||||
setCookie("_primary_color", attr)
|
||||
document.body.setAttribute("data-md-color-primary", attr)
|
||||
var name = document.querySelector("#__code_2 code span:nth-child(7)")
|
||||
name.textContent = attr.replace("-", " ")
|
||||
})
|
||||
})
|
||||
</script>
|
||||
|
||||
<script>
|
||||
var buttons2 = document.querySelectorAll("button[data-md-color-accent]")
|
||||
buttons2.forEach(function(button) {
|
||||
button.addEventListener("click", function() {
|
||||
var attr = this.getAttribute("data-md-color-accent")
|
||||
setCookie("_accent_color", attr)
|
||||
document.body.setAttribute("data-md-color-accent", attr)
|
||||
var name = document.querySelector("#__code_3 code span:nth-child(7)")
|
||||
name.textContent = attr.replace("-", " ")
|
||||
})
|
||||
})
|
||||
</script>
|
||||
@@ -1,92 +0,0 @@
|
||||
hljs.initHighlighting()
|
||||
|
||||
var _hmt = _hmt || [];
|
||||
(function () {
|
||||
var hm = document.createElement("script");
|
||||
hm.src = "https://hm.baidu.com/hm.js?f0f276cefa10aa31a20ae3815a50b795";
|
||||
var s = document.getElementsByTagName("script")[0];
|
||||
s.parentNode.insertBefore(hm, s);
|
||||
})();
|
||||
|
||||
function appendChatModule(id, chatDialogs) {
|
||||
let insertDiv = document.getElementById(id);
|
||||
let ss = '';
|
||||
ss += '<div class="doc-chat-container">';
|
||||
for(let i of chatDialogs) {
|
||||
if (i.role === 0) {
|
||||
ss += '<div class="doc-chat-row doc-chat-row-robot">\n' +
|
||||
' <img class="doc-chat-avatar" src="https://docs-v1.zhamao.xin/logo.png" alt=""/>\n' +
|
||||
' <div class="doc-chat-box doc-chat-box-robot">' + i.msg + '</div>\n' +
|
||||
' </div>';
|
||||
} else {
|
||||
ss += '<div class="doc-chat-row">\n' +
|
||||
' <div class="doc-chat-box">' + i.msg + '</div>\n' +
|
||||
' <img class="doc-chat-avatar" src="http://api.btstu.cn/sjtx/api.php" alt=""/>\n' +
|
||||
' </div>';
|
||||
}
|
||||
}
|
||||
insertDiv.innerHTML = ss + '</div>';
|
||||
}
|
||||
|
||||
function getCookie(name) {
|
||||
var arr, reg = new RegExp("(^| )" + name + "=([^;]*)(;|$)");
|
||||
|
||||
if (arr = document.cookie.match(reg))
|
||||
|
||||
return unescape(arr[2]);
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
function setCookie(name, value) {
|
||||
var Days = 30;
|
||||
var exp = new Date();
|
||||
exp.setTime(exp.getTime() + Days * 24 * 60 * 60 * 1000);
|
||||
document.cookie = name + "=" + escape(value) + ";expires=" + exp.toGMTString();
|
||||
}
|
||||
|
||||
s_theme=getCookie("_theme");
|
||||
if(s_theme === undefined) s_theme = "default";
|
||||
document.body.setAttribute("data-md-color-scheme", s_theme)
|
||||
var name = document.querySelector("#__code_0 code span:nth-child(7)")
|
||||
name.textContent = s_theme
|
||||
|
||||
s_primary=getCookie("_primary_color");
|
||||
document.body.setAttribute("data-md-color-primary", s_primary);
|
||||
var name2 = document.querySelector("#__code_2 code span:nth-child(7)");
|
||||
if(s_primary !== null && name2 !== null) name2.textContent = s_primary.replace("-", " ");
|
||||
|
||||
s_accent=getCookie("_accent_color");
|
||||
document.body.setAttribute("data-md-color-accent", s_accent);
|
||||
var name3 = document.querySelector("#__code_3 code span:nth-child(7)");
|
||||
if(s_accent !== null && name3 !== null) name3.textContent = s_accent.replace("-", " ");
|
||||
|
||||
setTimeout(() => {
|
||||
let ls = document.querySelectorAll("chat-box");
|
||||
for(let i of ls) {
|
||||
let final = '<div class="doc-chat-container">';
|
||||
let dialogs = i.innerHTML.split("\n");
|
||||
for(let j of dialogs) {
|
||||
if(j === '') continue;
|
||||
if(j.substr(0, 2) === ') ') {
|
||||
final += '<div class="doc-chat-row">\n' +
|
||||
' <div class="doc-chat-box">' + j.substr(2) + '</div>\n' +
|
||||
' <img class="doc-chat-avatar" src="http://api.btstu.cn/sjtx/api.php" alt=""/>\n' +
|
||||
' </div>';
|
||||
} else if (j.substr(0, 2) === '( ') {
|
||||
final += '<div class="doc-chat-row doc-chat-row-robot">\n' +
|
||||
' <img class="doc-chat-avatar" src="https://docs-v1.zhamao.xin/logo.png" alt=""/>\n' +
|
||||
' <div class="doc-chat-box doc-chat-box-robot">' + j.substr(2) + '</div>\n' +
|
||||
' </div>';
|
||||
} else if (j.substr(0, 2) === '^ ') {
|
||||
final += '<div class="doc-chat-row doc-chat-banner">' + j.substr(2) + '</div>';
|
||||
} else if (j.substr(0, 2) === '[ ') {
|
||||
final += '<div class="doc-chat-row doc-chat-row-robot">\n' +
|
||||
' <img class="doc-chat-avatar" src="https://docs-v1.zhamao.xin/logo.png" alt=""/>\n' +
|
||||
' <div class="doc-chat-box doc-chat-box-robot"><img src="' + j.substr(2) + '" alt=""/></div>\n' +
|
||||
' </div>';
|
||||
}
|
||||
}
|
||||
i.innerHTML = final;
|
||||
}
|
||||
}, 500);
|
||||
@@ -1,220 +0,0 @@
|
||||
# 更新日志(v1 版本)
|
||||
|
||||
## v1.6.5
|
||||
|
||||
> 更新时间:2020.12.9
|
||||
|
||||
- 修复:版本号显示
|
||||
- 优化:依赖问题,减少对 PHP 扩展的依赖,转变为可选
|
||||
|
||||
## v1.6.4
|
||||
|
||||
> 更新时间:2020.12.9
|
||||
|
||||
- 修复:composer require模式下自动加载的问题
|
||||
- 优化:减少不是必需的依赖问题
|
||||
|
||||
## v1.6.3
|
||||
|
||||
> 更新时间:2020.11.15
|
||||
|
||||
- 修复:Response 对象使用 redirect 造成的递归报错
|
||||
- 修复:`document_index` 配置项在 `/` 路径下无法使用的 bug
|
||||
|
||||
## v1.6.2
|
||||
|
||||
> 更新时间:2020.7.27
|
||||
|
||||
- 修复:不写配置 `server_event_handler_class` 项无法启动的 bug
|
||||
|
||||
## v1.6.1
|
||||
|
||||
> 更新时间:2020.7.26
|
||||
|
||||
- 新增:`ZMRequest::downloadFile($url, $dst)` 方法,可直接将文件下载到本地
|
||||
|
||||
## v1.6
|
||||
|
||||
> 更新时间:2020.7.14
|
||||
|
||||
- 新增:现在可以对类修饰自定义的注解了
|
||||
- 修复:数据库操作 where 对象时产生的歧义
|
||||
- 新增:支持自定义任何 Swoole Server 事件的注解绑定,详见文档
|
||||
- 修复:多个中间件注解对类只生效最后一个的 bug
|
||||
|
||||
❗ 下面是框架升级需要手动进行的变更:
|
||||
|
||||
- 新版本由于引进了自定义 Swoole Server 事件的机制,对 global.php 全局配置文件有了变动,需要添加以下内容才能正常启动(旧版本升级新版本用户,新用户无需操作):
|
||||
|
||||
```php
|
||||
/** 注册 Swoole Server 事件注解的类列表 */
|
||||
$config['server_event_handler_class'] = [
|
||||
\Framework\ServerEventHandler::class, //默认不可删除,否则会不能使用框架
|
||||
];
|
||||
```
|
||||
|
||||
## v1.5.8
|
||||
|
||||
> 更新时间:2020.6.26
|
||||
|
||||
- 新增:`@CQCommand` 注解的 fullMatch 参数(全量正则表达式匹配)
|
||||
|
||||
## v1.5.7
|
||||
|
||||
> 更新时间:2020.6.20
|
||||
|
||||
- 新增:ZM_BREAKPOINT 的短名称:BP
|
||||
- 优化:终端连接器自动重连
|
||||
- 修复:语法错误时防止循环报错
|
||||
|
||||
## v1.5.6
|
||||
|
||||
> 更新时间:2020.6.15
|
||||
|
||||
- 新增:`@CQCommand` 注解支持 `message_type`,`user_id`,`group_id`,`discuss_id` 限定条件
|
||||
- 新增:PDO 数据库支持自定义 fetch_mode,可在 `global.php` 中的 `sql_config["sql_default_fetch_mode"]` 字段设置,也可以调用时 `DB::rawQuery("语句", [], PDO::FETCH_ASSOC);` 第三个参数可选
|
||||
- 🔴 废弃:`ModBase` 基类,基类继承机制将在 1.6 版本起完全删除
|
||||
|
||||
## v1.5.5
|
||||
|
||||
> 更新时间:2020.6.13
|
||||
|
||||
- 修复:`@SwooleEventAt("close")` 下不能使用 `ctx()->getConnection()` 获取链接对象的 bug
|
||||
- 新增:init 命令,可在 `composer require zhamao/framework` 后使用 `vendor/bin/start init` 初始化项目目录结构和配置文件
|
||||
- 更新:默认模块新增机器人断开连接的回调事件
|
||||
|
||||
## v1.5.4
|
||||
|
||||
> 更新时间:2020.6.13
|
||||
|
||||
- 新增:`@CQCommand` 下支持 alias 参数
|
||||
- 更新:将 autoload 变为 composer autoload(需要重新 composer update)
|
||||
|
||||
## v1.5.3
|
||||
|
||||
> 更新时间:2020.6.10
|
||||
|
||||
- 修复:在 Linux 系统下 Terminal 无法正常使用的 bug
|
||||
|
||||
## v1.5.2
|
||||
|
||||
> 更新时间:2020.6.8
|
||||
|
||||
- 新增:`ZM_VERSION` 常量,对应为当前框架版本
|
||||
- 修复:部分链接不带 `/` 会导致 ZMRequest 模块报错的 bug
|
||||
|
||||
## v1.5.1
|
||||
|
||||
> 更新时间:2020.6.5
|
||||
|
||||
- 新增:ZMRequest::request() 自定义构建 HTTP 请求方法
|
||||
- 修复:一个不会导致崩溃的 warning 提示
|
||||
|
||||
## v1.5
|
||||
|
||||
> 更新时间:2020.6.5
|
||||
|
||||
- 重要变更:支持从 composer 使用框架
|
||||
- 新增:数据库 Select 选择器支持 `count()` 方法
|
||||
- 修复:ZMRequest 中 https 和端口的指定顺序问题
|
||||
- 新增:ZMWebSocket 创建 WS 链接的轻量级客户端
|
||||
- 修复:数据库异常的捕获更改为 PDOException
|
||||
|
||||
## v1.4
|
||||
|
||||
> 更新时间:2020.5.23
|
||||
|
||||
- 新增:自定义 motd
|
||||
- 新增:debug_mode 下断点调试功能
|
||||
- 新增:`@OnSave` 注解,储存自动保存的变量时事件激活
|
||||
- 新增:Swoole 版本检测
|
||||
- 新增:全局函数,以 `zm_` 开头的,详情见文档
|
||||
- 新增:`@LoadBuffer` 注解,只加载内存不自动保存的变量
|
||||
- 新增:局部静态文件服务
|
||||
- 新增:mysqlnd 扩展状态检测
|
||||
- 更新:将终端输入更换为多进程
|
||||
- 更新:将数据库连接池变更为 Swoole 官方的连接池,需要 Swoole 版本 >= 4.4.13
|
||||
- 更新:提升注解绑定的事件函数的执行效率
|
||||
- 修复:上下文 `getConnection()` 的 fd 无法获取的 bug
|
||||
- 修复:MySQL 长链接 gone away 自动重连的问题
|
||||
- 修复:MySQL 查询构造器无 WHERE 语句时会造成的 bug
|
||||
- 修复:调整各项资源初始化前后顺序
|
||||
|
||||
不可逆修改:你需要重新执行一次 `composer update` 或重新拉取一次 Docker Image,因为 composer 依赖发生了变化。
|
||||
|
||||
## v1.3.1
|
||||
|
||||
> 更新时间:2020.5.10
|
||||
|
||||
- 修复:DataProvider 下 setJsonData 新建文件夹的问题
|
||||
- 优化:默认 / 页面显示 `Hello Zhamao!` 文字
|
||||
- 优化:Exception 和 Fatal error 报错机制的改进
|
||||
- 修复:计时器没有上下文环境,发不了 API 的 bug
|
||||
|
||||
❗ 下面是框架升级需要手动进行的变更:
|
||||
|
||||
- 更改 MySQL 客户端为原生 PDO mysqlnd,如果之前使用 Docker 启动,则需使用新的 Dockerfile 构建。如果安装在本机,需安装 php-mysql 扩展。本次更新不影响框架内的 API,不需要更改任何代码。
|
||||
|
||||
## v1.3.0
|
||||
|
||||
> 更新时间:2020.5.8
|
||||
|
||||
- 新增:上下文,具体更新都写到了文档里了!
|
||||
- 修复:ZMRobot 的 `setPrefix()` 的严重错误
|
||||
- 优化:优化部分代码
|
||||
- 改动:现在你可以和任意事件的注解使用任意中间件啦,而且还支持多中间件
|
||||
- 新增:CQHTTP + 酷Q + 炸毛框架 的 Dockerfile
|
||||
- 新增注解:`@CQAPISend`,`@CQAPIResponse`,是 API 调用后触发的事件,具体见文档说明
|
||||
|
||||
## v1.2.1
|
||||
|
||||
> 更新时间:2020.5.2
|
||||
|
||||
- 新增:phar 启动模式构建脚本,你可以直接拉取 phar 运行框架了!
|
||||
- 优化:优化部分代码
|
||||
|
||||
## v1.2
|
||||
|
||||
> 更新时间:2020.4.29
|
||||
|
||||
- 新增:systemd 生成脚本、一键 daemonize 守护进程方式常驻后台
|
||||
- 新增:示例模块的注释
|
||||
- 重构:Console 模块,现在有准确的控制台输出分级功能了
|
||||
- 新增:`@OnTick` 注解,用于绑定定时器(毫秒级)
|
||||
- 新增:`ZMRobot` 类,比调用 `CQAPI` 类发送 API 更方便,同时兼容最新版本的 `CQHTTP` 插件
|
||||
- 优化:使用键盘中断 `Ctrl+C`,不会丢失未保存的缓存数据了
|
||||
- 优化:完善上下文对象的方法
|
||||
- 新增:终端命令:`logtest`,测试输出的 log 类型
|
||||
|
||||
:exclamation:下面是框架模块开发中需要注意的或有不兼容的修改内容:
|
||||
|
||||
- 修改:`global.php` 中原来的 `info_level` 默认数值需要改为 `2`,保证终端输出和原来一致
|
||||
|
||||
## v1.1.2
|
||||
|
||||
> 更新时间:2020.4.26
|
||||
|
||||
- 新增:静态文件服务器
|
||||
- 修复:`/` 路径的 Mapping 无法正常绑定的 bug
|
||||
|
||||
## v1.1.1
|
||||
|
||||
> 更新时间:2020.4.26
|
||||
|
||||
- 新增:中间件对类的修饰
|
||||
- 新增:上下文对象对 IDE 的支持
|
||||
- 修复:数据库插入查询的愚蠢错误
|
||||
- 修复:数据库查询的 `value()` 不支持指定参数的 bug
|
||||
|
||||
## v1.1.0
|
||||
|
||||
> 更新时间:2020.3.29
|
||||
|
||||
- 新增:中间件 `@Middleware` 功能
|
||||
- 修复:Websocket 链接关闭后未自动删除连接对象的bug
|
||||
|
||||
## v1.0.0
|
||||
|
||||
> 更新时间:2020.3.19
|
||||
|
||||
正式版发布。
|
||||
@@ -1,26 +0,0 @@
|
||||
# 更新日志(v2 版本)
|
||||
|
||||
## v2.0.3
|
||||
|
||||
> 更新事件:2020.12.31
|
||||
|
||||
- 修复:CQBefore 注解事件在 level 低于 200 时无法调用的 bug
|
||||
- 修复:CQMetaEvent 注解事件调用时报错的 bug
|
||||
|
||||
## v2.0.2
|
||||
|
||||
> 更新时间:2020.12.31
|
||||
|
||||
- 更新:将 CQ 码调用类更新到与最新 OneBot 标准相兼容的状态
|
||||
|
||||
## v2.0.1
|
||||
|
||||
> 更新时间:2020.12.23
|
||||
|
||||
- 修复:开屏报错文件夹不存在
|
||||
|
||||
## v2.0
|
||||
|
||||
> 更新时间:2020.12.23
|
||||
|
||||
已发布正式版。
|
||||
34
ext/go-cqhttp-down.sh
Normal file
@@ -0,0 +1,34 @@
|
||||
#!/bin/bash
|
||||
|
||||
echo "正在检查最新版本的go-cqhttp..."
|
||||
|
||||
if [ "$(uname -m)" = "x86_64" ]; then
|
||||
arch_type="amd64"
|
||||
elif [ "$(uname -m)" = "i386" ]; then
|
||||
arch_type="386"
|
||||
elif [ "$(uname -m)" = "aarch64" ]; then
|
||||
arch_type="arm64"
|
||||
else
|
||||
echo "Not supported architecture: $(uname -m)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
aas=$(uname -s | tr 'A-Z' 'a-z')
|
||||
|
||||
ver=$(wget -qO- -t1 -T2 "https://fgit-api.zhamao.me/repos/Mrs4s/go-cqhttp/releases" | grep "tag_name" | head -n 1 | awk -F ":" '{print $2}' | sed 's/\"//g;s/,//g;s/ //g')
|
||||
|
||||
if [ "$ver" != "" ]; then
|
||||
echo "最新版本:"$ver
|
||||
echo -n "是否下载到本地?[y/N] "
|
||||
read option
|
||||
if [ "$option" = "y" ]; then
|
||||
wget https://fgit.zhamao.me/Mrs4s/go-cqhttp/releases/download/$ver/go-cqhttp-$ver-$aas-$arch_type.tar.gz -O temp.tar.gz
|
||||
if [ $? != 0 ]; then
|
||||
wget https://fgit.zhamao.me/Mrs4s/go-cqhttp/releases/download/$ver/go-cqhttp_"$aas""_""$arch_type"".tar.gz" -O temp.tar.gz
|
||||
fi
|
||||
tar -zxvf temp.tar.gz go-cqhttp
|
||||
rm temp.tar.gz
|
||||
echo "下载完成,启动命令:./go-cqhttp"
|
||||
echo "首次启动后先编辑config文件!"
|
||||
fi
|
||||
fi
|
||||
264
ext/go.sh
Executable file
@@ -0,0 +1,264 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# 支持的环境变量:
|
||||
# ZM_DOWN_PHP_VERSION php版本,默认为8.0
|
||||
# ZM_NO_LOCAL_PHP 如果填入任意内容,则不检查本地PHP,直接安装内建PHP(仅限Linux)
|
||||
# ZM_TEMP_DIR 脚本下载内建PHP和Composer的临时目录,默认为/tmp/.zm-runtime
|
||||
# ZM_CUSTOM_DIR 自定义新建目录名称,默认为zhamao-app
|
||||
# ZM_COMPOSER_PACKAGIST 是否使用Composer的国外源,默认使用国内阿里云,如果设置并填写了内容,则自动使用Composer的国外源
|
||||
|
||||
ZM_PWD=$(pwd)
|
||||
_cyan="\033[0;36m"
|
||||
_reset="\033[0m"
|
||||
|
||||
# 彩头
|
||||
function nhead() {
|
||||
if [ "$1" = "red" ]; then
|
||||
echo -ne "\033[0;31m[!]"
|
||||
elif [ "$1" = "yellow" ]; then
|
||||
echo -ne "\033[0;33m[?]"
|
||||
else
|
||||
echo -ne "\033[0;32m[*]"
|
||||
fi
|
||||
echo -e "\033[0m"
|
||||
}
|
||||
|
||||
# 下载文件 $1 到目录 $2,自动选择使用 curl 或者 wget
|
||||
function download_file() {
|
||||
downloader="wget"
|
||||
type wget >/dev/null 2>&1 || { downloader="curl"; }
|
||||
if [ "$downloader" = "wget" ]; then
|
||||
_down_prefix="O"
|
||||
else
|
||||
_down_prefix="o"
|
||||
fi
|
||||
_down_symbol=0
|
||||
if [ ! -f "$2" ]; then
|
||||
echo -ne "$(nhead) 正在下载 $1 ... "
|
||||
$downloader "$1" -$_down_prefix "$2" >/dev/null 2>&1 && echo "完成!" && _down_symbol=1
|
||||
else
|
||||
echo "已存在!" && _down_symbol=1
|
||||
fi
|
||||
if [ $_down_symbol == 0 ]; then
|
||||
echo "$(nhead red) 下载失败!请检查网络连接!"
|
||||
rm -rf "$2"
|
||||
return 1
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
# 安装下载内建PHP
|
||||
function install_native_php() {
|
||||
ZM_PHP_VERSION="8.1"
|
||||
if [ "$ZM_DOWN_PHP_VERSION" != "" ]; then
|
||||
ZM_PHP_VERSION="$ZM_DOWN_PHP_VERSION"
|
||||
fi
|
||||
echo "$(nhead) 使用的内建 PHP 版本: $ZM_PHP_VERSION"
|
||||
|
||||
rm -rf "$ZM_TEMP_DIR"
|
||||
mkdir "$ZM_TEMP_DIR" >/dev/null 2>&1
|
||||
if [ ! -f "$ZM_TEMP_DIR/php" ]; then
|
||||
download_file "https://dl.zhamao.xin/php-bin/down.php?php_ver=$ZM_PHP_VERSION&arch=$(uname -m)" "$ZM_TEMP_DIR/php.tgz" || return 1
|
||||
tar -xf "$ZM_TEMP_DIR/php.tgz" -C "$ZM_TEMP_DIR/" && rm -rf "$ZM_TEMP_DIR/php.tgz"
|
||||
fi
|
||||
echo "$(nhead) 安装内建 PHP 完成!"
|
||||
php_executable="$ZM_TEMP_DIR/php"
|
||||
return 0
|
||||
}
|
||||
|
||||
# 安装下载Composer
|
||||
function install_native_composer() {
|
||||
if [ ! -f "$ZM_TEMP_DIR/composer.phar" ]; then
|
||||
# 下载 composer.phar
|
||||
download_file "https://mirrors.aliyun.com/composer/composer.phar" "$ZM_TEMP_DIR/composer.phar" || return 1
|
||||
if [ "$php_executable" = "$ZM_TEMP_DIR/php" ]; then
|
||||
# shellcheck disable=SC2016
|
||||
txt='#!/usr/bin/env sh
|
||||
if [ -f "$(dirname $0)/php" ]; then
|
||||
"$(dirname $0)/php" "$(dirname $0)/composer.phar" $*
|
||||
else
|
||||
php "$(dirname $0)/composer.phar" $*
|
||||
fi'
|
||||
echo "$txt" >"$ZM_TEMP_DIR/composer"
|
||||
chmod +x "$ZM_TEMP_DIR/composer"
|
||||
else
|
||||
mv "$ZM_TEMP_DIR/composer.phar" "$ZM_TEMP_DIR/composer" && chmod +x "$ZM_TEMP_DIR/composer"
|
||||
fi
|
||||
fi
|
||||
echo "$(nhead) 安装内建 Composer 完成!"
|
||||
composer_executable="$ZM_TEMP_DIR/composer"
|
||||
return 0
|
||||
}
|
||||
|
||||
# 检查Composer可用性
|
||||
function composer_check() {
|
||||
# 顺带检查一下
|
||||
echo "$(nhead) 正在检查 Git、unzip、7z 能否正常使用 ... "
|
||||
type git >/dev/null 2>&1 || {
|
||||
echo "$(nhead red) 检测到系统不存在 git 命令,可能无法正常使用 Composer 下载 GitHub 等仓库项目!"
|
||||
}
|
||||
zip_check_symbol=0
|
||||
if type unzip >/dev/null 2>&1; then
|
||||
zip_check_symbol=1
|
||||
fi
|
||||
if type 7z >/dev/null 2>&1; then
|
||||
zip_check_symbol=1
|
||||
fi
|
||||
if [ $zip_check_symbol -eq 0 ]; then
|
||||
if [ "$($php_executable -m | grep zip)" = "" ]; then
|
||||
echo "$(nhead red) 检测到系统不存在 unzip 或 7z 命令,PHP 不存在 zip 扩展,可能无法正常使用 Composer 下载压缩包项目!"
|
||||
fi
|
||||
fi
|
||||
|
||||
# 测试 Composer 和 PHP 是否能正常使用
|
||||
if [ "$("$composer_executable" -n about | grep Manager)" = "" ]; then
|
||||
echo "$(nhead red) Download PHP binary and composer failed!"
|
||||
return 1
|
||||
fi
|
||||
echo "$(nhead) 环境检查完成!"
|
||||
return 0
|
||||
}
|
||||
|
||||
# 环境检查
|
||||
function darwin_env_check() {
|
||||
echo -ne "$(nhead) 检查是否存在 PHP ... "
|
||||
if type php >/dev/null 2>&1; then
|
||||
php_executable=$(which php)
|
||||
echo "位置:$php_executable"
|
||||
if [ "$($php_executable -m | grep swoole)" = "" ]; then
|
||||
echo "$(nhead red) PHP 不存在 swoole 扩展,可能无法正常使用 Swoole 框架!" && return 1
|
||||
fi
|
||||
else
|
||||
echo "不存在"
|
||||
if type brew >/dev/null 2>&1; then
|
||||
echo -n "$(nhead yellow) 是否使用 Homebrew 安装 PHP?[y/N] "
|
||||
read -r y
|
||||
if [ "$y" = "" ]; then y="N"; fi
|
||||
if [ "$y" = "y" ]; then
|
||||
brew install php || echo "$(nhead red) 安装 PHP 失败!" && return 1
|
||||
else
|
||||
echo "$(nhead red) 跳过安装 PHP!" && return 1
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
echo -ne "$(nhead) 检查是否存在 Composer ... "
|
||||
if type composer >/dev/null 2>&1; then
|
||||
composer_executable=$(which composer)
|
||||
echo "位置:$composer_executable"
|
||||
else
|
||||
echo "不存在,正在下载 Composer ..."
|
||||
install_native_composer || return 1
|
||||
fi
|
||||
|
||||
composer_check || return 1
|
||||
}
|
||||
|
||||
# 询问是否安装 native php
|
||||
function prompt_install_native_php() {
|
||||
echo -ne "$(nhead yellow) 检测到系统的 PHP 不存在 swoole 扩展,是否下载安装独立的内建 PHP 和 Composer?[Y/n] "
|
||||
read -r y
|
||||
case $y in
|
||||
Y|y|"") return 0 ;;
|
||||
*) echo "$(nhead red) 跳过安装内建 PHP!" && return 1 ;;
|
||||
esac
|
||||
}
|
||||
|
||||
# 环境检查
|
||||
function linux_env_check() {
|
||||
if [ "$ZM_NO_LOCAL_PHP" != "" ]; then # 如果指定了不使用本地 php,则不检查,直接下载
|
||||
install_native_php && install_native_composer && composer_check && return 0 || return 1
|
||||
else
|
||||
echo -ne "$(nhead) 检查是否存在 PHP ... "
|
||||
if type php >/dev/null 2>&1; then
|
||||
php_executable=$(which php)
|
||||
echo "位置:$php_executable"
|
||||
if [ "$($php_executable -m | grep swoole)" = "" ]; then
|
||||
echo "$(nhead red) PHP 不存在 swoole 扩展,可能无法正常使用 Swoole 框架!" && \
|
||||
prompt_install_native_php && \
|
||||
install_native_php && \
|
||||
install_native_composer && composer_check && return 0 || return 1
|
||||
fi
|
||||
else
|
||||
echo "不存在,将下载内建 PHP"
|
||||
install_native_php || return 1
|
||||
fi
|
||||
fi
|
||||
|
||||
echo -ne "$(nhead) 检查是否存在 Composer ... "
|
||||
if type composer >/dev/null 2>&1; then
|
||||
composer_executable=$(which composer)
|
||||
echo "位置:$composer_executable"
|
||||
else
|
||||
echo "不存在,将下载内建 Composer"
|
||||
install_native_composer || return 1
|
||||
fi
|
||||
|
||||
composer_check || return 1
|
||||
}
|
||||
|
||||
function if_use_aliyun() {
|
||||
if [ "$ZM_COMPOSER_PACKAGIST" = "" ]; then
|
||||
$composer_executable -n config repos.packagist composer https://mirrors.aliyun.com/composer
|
||||
fi
|
||||
}
|
||||
|
||||
function if_restore_native_runtime() {
|
||||
ZM_RUNTIME_DIR="$ZM_PWD/$ZM_CUSTOM_DIR/runtime/"
|
||||
if [ "$php_executable" = "$ZM_TEMP_DIR/php" ]; then
|
||||
echo "$(nhead) 移动内建 PHP 到框架目录 $ZM_RUNTIME_DIR ..." && \
|
||||
mkdir -p "$ZM_RUNTIME_DIR" && \
|
||||
mv "$ZM_TEMP_DIR/php" "$ZM_RUNTIME_DIR" || {
|
||||
echo "$(nhead red) 移动内建 PHP 到框架目录失败!" && return 1
|
||||
}
|
||||
php_executable="$ZM_RUNTIME_DIR/php"
|
||||
fi
|
||||
if [ "$composer_executable" = "$ZM_TEMP_DIR/composer" ]; then
|
||||
echo "$(nhead) 移动内建 Composer 到框架目录 $ZM_RUNTIME_DIR ..." && \
|
||||
mkdir -p "$ZM_CUSTOM_DIR/runtime" && \
|
||||
mv "$ZM_TEMP_DIR/composer" "$ZM_RUNTIME_DIR" && \
|
||||
mv "$ZM_TEMP_DIR/composer.phar" "$ZM_RUNTIME_DIR" || {
|
||||
echo "$(nhead red) 移动内建 Composer 到框架目录失败!" && return 1
|
||||
}
|
||||
composer_executable="$ZM_RUNTIME_DIR/composer"
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
function install_framework() {
|
||||
echo "$(nhead) 开始安装框架到目录 $ZM_CUSTOM_DIR ..."
|
||||
mkdir -p "$ZM_PWD/$ZM_CUSTOM_DIR" && \
|
||||
cd "$ZM_PWD/$ZM_CUSTOM_DIR" && \
|
||||
$composer_executable init --name="zhamao/framework-starter" -n -q && \
|
||||
if_use_aliyun && \
|
||||
echo "$(nhead) 从 Composer 拉取框架 ..." && \
|
||||
$composer_executable require -n -q zhamao/framework:^2.7 && \
|
||||
$composer_executable require -n -q --dev swoole/ide-helper:^4.5 && \
|
||||
if_restore_native_runtime && \
|
||||
echo "$(nhead) 初始化框架脚手架文件 ..." && \
|
||||
vendor/bin/start init >/dev/null 2>&1 && \
|
||||
$composer_executable dump-autoload -n -q && \
|
||||
echo "$(nhead) 安装框架完成,已安装到目录 $ZM_CUSTOM_DIR" && \
|
||||
echo -e "$(nhead) 启动框架命令:""$_cyan""cd $ZM_CUSTOM_DIR && ./zhamao server""$_reset" || {
|
||||
echo "$(nhead red) 安装框架失败!" && return 1
|
||||
}
|
||||
}
|
||||
|
||||
# 环境变量设置
|
||||
test "$ZM_TEMP_DIR" = "" && ZM_TEMP_DIR="/tmp/.zm-runtime"
|
||||
test "$ZM_CUSTOM_DIR" = "" && ZM_CUSTOM_DIR="zhamao-app"
|
||||
|
||||
if [ -d "$ZM_PWD/$ZM_CUSTOM_DIR" ]; then
|
||||
echo "$(nhead red) 检测到目录 $ZM_CUSTOM_DIR/ 已安装过框架,请更换文件夹名称或删除旧文件夹再试!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 检查系统环境,目前只支持 Linux
|
||||
case $(uname -s) in
|
||||
Linux) linux_env_check || exit 1 ;;
|
||||
Darwin) darwin_env_check || exit 1 ;;
|
||||
*) echo "$(nhead red) Only support Linux and macOS!" && exit 1 ;;
|
||||
esac
|
||||
|
||||
# 安装框架
|
||||
install_framework
|
||||
278
ext/v3.sh
Executable file
@@ -0,0 +1,278 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# 支持的环境变量:
|
||||
# ZM_DOWN_PHP_VERSION php版本,默认为8.0
|
||||
# ZM_NO_LOCAL_PHP 如果填入任意内容,则不检查本地PHP,直接安装内建PHP(仅限Linux)
|
||||
# ZM_TEMP_DIR 脚本下载内建PHP和Composer的临时目录,默认为/tmp/.zm-runtime
|
||||
# ZM_CUSTOM_DIR 自定义新建目录名称,默认为zhamao-v3
|
||||
# ZM_COMPOSER_PACKAGIST 是否使用Composer的国外源,默认使用国内阿里云,如果设置并填写了内容,则自动使用Composer的国外源
|
||||
|
||||
ZM_PWD=$(pwd)
|
||||
_cyan="\033[0;36m"
|
||||
_reset="\033[0m"
|
||||
|
||||
# 彩头
|
||||
function nhead() {
|
||||
if [ "$1" = "red" ]; then
|
||||
echo -ne "\033[0;31m[!]"
|
||||
elif [ "$1" = "yellow" ]; then
|
||||
echo -ne "\033[0;33m[?]"
|
||||
else
|
||||
echo -ne "\033[0;32m[*]"
|
||||
fi
|
||||
echo -e "\033[0m"
|
||||
}
|
||||
|
||||
# 下载文件 $1 到目录 $2,自动选择使用 curl 或者 wget
|
||||
function download_file() {
|
||||
downloader="wget"
|
||||
type wget >/dev/null 2>&1 || { downloader="curl"; }
|
||||
if [ "$downloader" = "wget" ]; then
|
||||
_down_prefix="O"
|
||||
else
|
||||
_down_prefix="o"
|
||||
fi
|
||||
_down_symbol=0
|
||||
if [ ! -f "$2" ]; then
|
||||
echo -ne "$(nhead) 正在下载 $1 ... "
|
||||
$downloader "$1" -$_down_prefix "$2" >/dev/null 2>&1 && echo "完成!" && _down_symbol=1
|
||||
else
|
||||
echo "已存在!" && _down_symbol=1
|
||||
fi
|
||||
if [ $_down_symbol == 0 ]; then
|
||||
echo "$(nhead red) 下载失败!请检查网络连接!"
|
||||
rm -rf "$2"
|
||||
return 1
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
# 安装下载内建PHP
|
||||
function install_native_php() {
|
||||
ZM_PHP_VERSION="8.1"
|
||||
if [ "$ZM_DOWN_PHP_VERSION" != "" ]; then
|
||||
ZM_PHP_VERSION="$ZM_DOWN_PHP_VERSION"
|
||||
fi
|
||||
echo "$(nhead) 使用的内建 PHP 版本: $ZM_PHP_VERSION"
|
||||
|
||||
rm -rf "$ZM_TEMP_DIR"
|
||||
mkdir "$ZM_TEMP_DIR" >/dev/null 2>&1
|
||||
if [ ! -f "$ZM_TEMP_DIR/php" ]; then
|
||||
download_file "https://dl.zhamao.xin/php-bin/down.php?php_ver=$ZM_PHP_VERSION&arch=$(uname -m)" "$ZM_TEMP_DIR/php.tgz" || return 1
|
||||
tar -xf "$ZM_TEMP_DIR/php.tgz" -C "$ZM_TEMP_DIR/" && rm -rf "$ZM_TEMP_DIR/php.tgz"
|
||||
fi
|
||||
echo "$(nhead) 安装内建 PHP 完成!"
|
||||
php_executable="$ZM_TEMP_DIR/php"
|
||||
return 0
|
||||
}
|
||||
|
||||
# 安装下载Composer
|
||||
function install_native_composer() {
|
||||
if [ ! -f "$ZM_TEMP_DIR/composer.phar" ]; then
|
||||
# 下载 composer.phar
|
||||
download_file "https://mirrors.aliyun.com/composer/composer.phar" "$ZM_TEMP_DIR/composer.phar" || return 1
|
||||
if [ "$php_executable" = "$ZM_TEMP_DIR/php" ]; then
|
||||
# shellcheck disable=SC2016
|
||||
txt='#!/usr/bin/env sh
|
||||
if [ -f "$(dirname $0)/php" ]; then
|
||||
"$(dirname $0)/php" "$(dirname $0)/composer.phar" $*
|
||||
else
|
||||
php "$(dirname $0)/composer.phar" $*
|
||||
fi'
|
||||
echo "$txt" >"$ZM_TEMP_DIR/composer"
|
||||
chmod +x "$ZM_TEMP_DIR/composer"
|
||||
else
|
||||
mv "$ZM_TEMP_DIR/composer.phar" "$ZM_TEMP_DIR/composer" && chmod +x "$ZM_TEMP_DIR/composer"
|
||||
fi
|
||||
fi
|
||||
echo "$(nhead) 安装内建 Composer 完成!"
|
||||
composer_executable="$ZM_TEMP_DIR/composer"
|
||||
return 0
|
||||
}
|
||||
|
||||
# 检查Composer可用性
|
||||
function composer_check() {
|
||||
# 顺带检查一下
|
||||
echo "$(nhead) 正在检查 Git、unzip、7z 能否正常使用 ... "
|
||||
type git >/dev/null 2>&1 || {
|
||||
echo "$(nhead red) 检测到系统不存在 git 命令,可能无法正常使用 Composer 下载 GitHub 等仓库项目!"
|
||||
}
|
||||
zip_check_symbol=0
|
||||
if type unzip >/dev/null 2>&1; then
|
||||
zip_check_symbol=1
|
||||
fi
|
||||
if type 7z >/dev/null 2>&1; then
|
||||
zip_check_symbol=1
|
||||
fi
|
||||
if [ $zip_check_symbol -eq 0 ]; then
|
||||
if [ "$($php_executable -m | grep zip)" = "" ]; then
|
||||
echo "$(nhead red) 检测到系统不存在 unzip 或 7z 命令,PHP 不存在 zip 扩展,可能无法正常使用 Composer 下载压缩包项目!"
|
||||
fi
|
||||
fi
|
||||
|
||||
# 测试 Composer 和 PHP 是否能正常使用
|
||||
if [ "$("$composer_executable" -n about | grep Manager)" = "" ]; then
|
||||
echo "$(nhead red) Download PHP binary and composer failed!"
|
||||
return 1
|
||||
fi
|
||||
echo "$(nhead) 环境检查完成!"
|
||||
return 0
|
||||
}
|
||||
|
||||
# 环境检查
|
||||
function darwin_env_check() {
|
||||
echo -ne "$(nhead) 检查是否存在 PHP ... "
|
||||
if type php >/dev/null 2>&1; then
|
||||
php_executable=$(which php)
|
||||
echo "位置:$php_executable"
|
||||
if [ "$($php_executable -m | grep swoole)" = "" ]; then
|
||||
echo "$(nhead red) PHP 不存在 swoole 扩展,可能无法正常使用 Swoole 框架!" && return 1
|
||||
fi
|
||||
else
|
||||
echo "不存在"
|
||||
if type brew >/dev/null 2>&1; then
|
||||
echo -n "$(nhead yellow) 是否使用 Homebrew 安装 PHP?[y/N] "
|
||||
read -r y
|
||||
if [ "$y" = "" ]; then y="N"; fi
|
||||
if [ "$y" = "y" ]; then
|
||||
brew install php || echo "$(nhead red) 安装 PHP 失败!" && return 1
|
||||
else
|
||||
echo "$(nhead red) 跳过安装 PHP!" && return 1
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
echo -ne "$(nhead) 检查是否存在 Composer ... "
|
||||
if type composer >/dev/null 2>&1; then
|
||||
composer_executable=$(which composer)
|
||||
echo "位置:$composer_executable"
|
||||
else
|
||||
echo "不存在,正在下载 Composer ..."
|
||||
install_native_composer || return 1
|
||||
fi
|
||||
|
||||
composer_check || return 1
|
||||
}
|
||||
|
||||
# 询问是否安装 native php
|
||||
function prompt_install_native_php() {
|
||||
echo -ne "$(nhead yellow) 检测到系统的 PHP 不符合要求,是否下载安装独立的内建 PHP 和 Composer?[Y/n] "
|
||||
read -r y
|
||||
case $y in
|
||||
Y|y|"") return 0 ;;
|
||||
*) echo "$(nhead red) 跳过安装内建 PHP!" && return 1 ;;
|
||||
esac
|
||||
}
|
||||
|
||||
# 环境检查
|
||||
function linux_env_check() {
|
||||
if [ "$ZM_NO_LOCAL_PHP" != "" ]; then # 如果指定了不使用本地 php,则不检查,直接下载
|
||||
install_native_php && install_native_composer && composer_check && return 0 || return 1
|
||||
else
|
||||
echo -ne "$(nhead) 检查是否存在 PHP ... "
|
||||
if type php >/dev/null 2>&1; then
|
||||
php_executable=$(which php)
|
||||
echo "位置:$php_executable"
|
||||
ver_id=$($php_executable -r "echo PHP_VERSION_ID;")
|
||||
if [ "$ver_id" -lt 80000 ]; then
|
||||
echo "$(nhead red) PHP 版本过低,框架需要 PHP >= 8.0.0!" && \
|
||||
prompt_install_native_php && \
|
||||
install_native_php && \
|
||||
install_native_composer && composer_check && return 0 || return 1
|
||||
fi
|
||||
if [ "$($php_executable -m | grep tokenizer)" = "" ]; then
|
||||
echo "$(nhead red) PHP 不存在 tokenizer 扩展,可能无法正常使用框架!" && \
|
||||
prompt_install_native_php && \
|
||||
install_native_php && \
|
||||
install_native_composer && composer_check && return 0 || return 1
|
||||
fi
|
||||
else
|
||||
echo "不存在,将下载内建 PHP"
|
||||
install_native_php || return 1
|
||||
fi
|
||||
fi
|
||||
|
||||
echo -ne "$(nhead) 检查是否存在 Composer ... "
|
||||
if type composer >/dev/null 2>&1; then
|
||||
composer_executable=$(which composer)
|
||||
echo "位置:$composer_executable"
|
||||
else
|
||||
echo "不存在,将下载内建 Composer"
|
||||
install_native_composer || return 1
|
||||
fi
|
||||
|
||||
composer_check || return 1
|
||||
}
|
||||
|
||||
function if_use_aliyun() {
|
||||
if [ "$ZM_COMPOSER_PACKAGIST" = "" ]; then
|
||||
$composer_executable -n config repos.packagist composer https://mirrors.aliyun.com/composer
|
||||
fi
|
||||
}
|
||||
|
||||
function if_restore_native_runtime() {
|
||||
ZM_RUNTIME_DIR="$ZM_PWD/$ZM_CUSTOM_DIR/runtime/"
|
||||
if [ "$php_executable" = "$ZM_TEMP_DIR/php" ]; then
|
||||
echo "$(nhead) 移动内建 PHP 到框架目录 $ZM_RUNTIME_DIR ..." && \
|
||||
mkdir -p "$ZM_RUNTIME_DIR" && \
|
||||
mv "$ZM_TEMP_DIR/php" "$ZM_RUNTIME_DIR" || {
|
||||
echo "$(nhead red) 移动内建 PHP 到框架目录失败!" && return 1
|
||||
}
|
||||
php_executable="$ZM_RUNTIME_DIR/php"
|
||||
fi
|
||||
if [ "$composer_executable" = "$ZM_TEMP_DIR/composer" ]; then
|
||||
echo "$(nhead) 移动内建 Composer 到框架目录 $ZM_RUNTIME_DIR ..." && \
|
||||
mkdir -p "$ZM_CUSTOM_DIR/runtime" && \
|
||||
mv "$ZM_TEMP_DIR/composer" "$ZM_RUNTIME_DIR" && \
|
||||
mv "$ZM_TEMP_DIR/composer.phar" "$ZM_RUNTIME_DIR" || {
|
||||
echo "$(nhead red) 移动内建 Composer 到框架目录失败!" && return 1
|
||||
}
|
||||
composer_executable="$ZM_RUNTIME_DIR/composer"
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
function install_framework() {
|
||||
echo "$(nhead) 开始安装框架到目录 $ZM_CUSTOM_DIR ..."
|
||||
export COMPOSER_ALLOW_SUPERUSER=1
|
||||
mkdir -p "$ZM_PWD/$ZM_CUSTOM_DIR" && \
|
||||
cd "$ZM_PWD/$ZM_CUSTOM_DIR" && \
|
||||
$composer_executable init --name="zhamao/zhamao-v3-app" -n -q && \
|
||||
if_use_aliyun && \
|
||||
echo "$(nhead) 从 Composer 拉取框架 ..." && \
|
||||
echo '{"minimum-stability":"dev"}' > composer.json && composer require -n zhamao/framework:^3 && \
|
||||
$composer_executable require -n --dev swoole/ide-helper:^4.5 && \
|
||||
if_restore_native_runtime && \
|
||||
echo "$(nhead) 初始化框架脚手架文件 ..." && \
|
||||
vendor/bin/zhamao init && \
|
||||
$composer_executable dump-autoload -n && \
|
||||
show_success_msg || {
|
||||
echo "$(nhead red) 安装框架失败!" && cd $ZM_PWD && rm -rf "$ZM_CUSTOM_DIR" && return 1
|
||||
}
|
||||
}
|
||||
|
||||
function show_success_msg() {
|
||||
echo -e "$(nhead) 框架安装成功,已安装到目录 $ZM_CUSTOM_DIR" && \
|
||||
echo -e "$(nhead) 进入应用目录:""$_cyan""cd $ZM_CUSTOM_DIR""$_reset" && \
|
||||
echo -e "$(nhead) 启动框架命令:""$_cyan""./zhamao server""$_reset" && \
|
||||
echo -e "$(nhead) 生成插件脚手架目录的命令:""$_cyan""./zhamao plugin:make""$_reset"
|
||||
}
|
||||
|
||||
# 环境变量设置
|
||||
test "$ZM_TEMP_DIR" = "" && ZM_TEMP_DIR="/tmp/.zm-runtime"
|
||||
test "$ZM_CUSTOM_DIR" = "" && ZM_CUSTOM_DIR="zhamao-v3"
|
||||
|
||||
if [ -d "$ZM_PWD/$ZM_CUSTOM_DIR" ]; then
|
||||
echo "$(nhead red) 检测到目录 $ZM_CUSTOM_DIR/ 已安装过框架,请更换文件夹名称或删除旧文件夹再试!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 检查系统环境,目前只支持 Linux
|
||||
case $(uname -s) in
|
||||
Linux) linux_env_check || exit 1 ;;
|
||||
Darwin) darwin_env_check || exit 1 ;;
|
||||
*) echo "$(nhead red) Only support Linux and macOS!" && exit 1 ;;
|
||||
esac
|
||||
|
||||
# 安装框架
|
||||
install_framework
|
||||
69
install-runtime.sh
Executable file
@@ -0,0 +1,69 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
function download_file() {
|
||||
downloader="wget"
|
||||
type wget >/dev/null 2>&1 || { downloader="curl"; }
|
||||
if [ "$downloader" = "wget" ]; then
|
||||
_down_prefix="O"
|
||||
else
|
||||
_down_prefix="o"
|
||||
fi
|
||||
_down_symbol=0
|
||||
if [ ! -f "$2" ]; then
|
||||
echo $1
|
||||
$downloader "$1" -$_down_prefix "$2" >/dev/null 2>&1 && \
|
||||
echo "完成!" && _down_symbol=1
|
||||
else
|
||||
echo "已存在!" && _down_symbol=1
|
||||
fi
|
||||
if [ $_down_symbol == 0 ]; then
|
||||
echo "失败!请检查网络连接!"
|
||||
rm -rf "$2"
|
||||
return 1
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
function test_composer_and_php() {
|
||||
succ=$("$(pwd)/runtime/composer" -n about | grep Manage)
|
||||
if [ "$succ" = "" ]; then
|
||||
echo "Download PHP binary and composer failed!"
|
||||
return 1
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
if [ "$(uname -s)" != "Linux" ]; then
|
||||
echo "Only support Linux!!!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
ZM_PHP_VERSION="7.4"
|
||||
if [ "$ZM_DOWN_PHP_VERSION" != "" ]; then
|
||||
ZM_PHP_VERSION="$ZM_DOWN_PHP_VERSION"
|
||||
echo "Using custom PHP version: $ZM_PHP_VERSION"
|
||||
fi
|
||||
|
||||
mkdir "$(pwd)/runtime" >/dev/null 2>&1
|
||||
if [ ! -f "$(pwd)/runtime/php" ]; then
|
||||
download_file "https://dl.zhamao.me/php-bin/down.php?php_ver=$ZM_PHP_VERSION&arch=$(uname -m)" "$(pwd)/runtime/php.tar.gz"
|
||||
if [ $? -ne 0 ]; then
|
||||
exit 1
|
||||
fi
|
||||
tar -xf "$(pwd)/runtime/php.tar.gz" -C "$(pwd)/runtime/"
|
||||
fi
|
||||
if [ ! -f "$(pwd)/runtime/composer" ]; then
|
||||
download_file "https://mirrors.aliyun.com/composer/composer.phar" "$(pwd)/runtime/composer.phar"
|
||||
if [ $? -ne 0 ]; then
|
||||
exit 1
|
||||
fi
|
||||
echo '$(dirname $0)/php $(dirname $0)/composer.phar $@' > $(pwd)/runtime/composer
|
||||
chmod +x $(pwd)/runtime/composer
|
||||
test_composer_and_php
|
||||
fi
|
||||
if [ $? -ne 0 ]; then
|
||||
exit 1
|
||||
fi
|
||||
echo "成功下载!" && \
|
||||
echo -e "PHP使用:\truntime/php -v" && \
|
||||
echo -e "Composer使用:\truntime/composer"
|
||||
20
instant-plugin-demo.php
Normal file
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
$plugin = new \ZM\ZMApplication(__DIR__);
|
||||
|
||||
/*
|
||||
* 发送 "测试 123",回复 "你好,123"
|
||||
*/
|
||||
$cmd1 = BotCommand::make('test', '测试')->withArgument('arg1')->on(fn () => '你好,{{arg1}}');
|
||||
|
||||
/*
|
||||
* 浏览器访问 http://ip:port/index233,返回内容
|
||||
*/
|
||||
$route1 = Route::make('/index233')->on(fn () => '<h1>Hello world</h1>');
|
||||
|
||||
$plugin->addBotCommand($cmd1);
|
||||
$plugin->addHttpRoute($route1);
|
||||
|
||||
$plugin->run();
|
||||
83
mkdocs.yml
@@ -1,83 +0,0 @@
|
||||
site_name: 炸毛框架 v2
|
||||
|
||||
repo_name: '炸毛框架'
|
||||
repo_url: 'https://github.com/zhamao-robot/zhamao-framework'
|
||||
edit_uri: 'blob/master/docs/'
|
||||
|
||||
theme:
|
||||
name: material
|
||||
logo: assets/logos.png
|
||||
favicon: assets/favicon.png
|
||||
language: zh
|
||||
features:
|
||||
- navigation.tabs
|
||||
extra_javascript:
|
||||
- https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.1.1/highlight.min.js
|
||||
- javascripts/config.js
|
||||
extra_css:
|
||||
- assets/css/extra.css
|
||||
- https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.1.1/styles/default.min.css
|
||||
markdown_extensions:
|
||||
- admonition
|
||||
- pymdownx.tabbed
|
||||
- pymdownx.superfences
|
||||
- pymdownx.inlinehilite
|
||||
- pymdownx.snippets
|
||||
- abbr
|
||||
- pymdownx.highlight:
|
||||
linenums: true
|
||||
linenums_style: pymdownx.inline
|
||||
extra:
|
||||
version:
|
||||
method: mike
|
||||
|
||||
copyright: 'Copyright © 2019 - 2020 CrazyBot Team <span class="tx-switch">
|
||||
<button data-md-color-scheme="default"><code>默认模式</code></button>
|
||||
<button data-md-color-scheme="slate"><code>暗黑模式</code></button>
|
||||
</span>
|
||||
|
||||
<script>
|
||||
var buttons = document.querySelectorAll("button[data-md-color-scheme]");
|
||||
buttons.forEach(function(button) {
|
||||
button.addEventListener("click", function() {
|
||||
var attr = this.getAttribute("data-md-color-scheme");
|
||||
setCookie("_theme", attr);
|
||||
document.body.setAttribute("data-md-color-scheme", attr);
|
||||
var name = document.querySelector("#__code_0 code span:nth-child(7)");
|
||||
name.textContent = attr;
|
||||
})
|
||||
})
|
||||
</script><br><a href="http://beian.miit.gov.cn">蒙ICP备18000198号-1</a>'
|
||||
|
||||
nav:
|
||||
- 指南:
|
||||
- 介绍: index.md
|
||||
- 安装框架: guide/installation.md
|
||||
- 快速上手(机器人篇): guide/quickstart-robot.md
|
||||
- 快速上手(HTTP篇): guide/quickstart-http.md
|
||||
- 选择聊天机器人实例: guide/onebot-choose.md
|
||||
- 基本配置: guide/basic-config.md
|
||||
- 编写模块: guide/write-module.md
|
||||
- 注册事件响应: guide/register-event.md
|
||||
- 事件和注解:
|
||||
- 事件和注解: event/index.md
|
||||
- 机器人注解事件: event/robot-annotations.md
|
||||
- HTTP 路由注解事件: event/route-annotations.md
|
||||
- 框架核心注解事件: event/framework-annotations.md
|
||||
- 中间件注解: event/middleware.md
|
||||
- 自定义注解: event/custom-annotations.md
|
||||
- 事件分发器: event/event-dispatcher.md
|
||||
- 框架组件:
|
||||
- 框架组件: component/index.md
|
||||
- 机器人 API: component/robot-api.md
|
||||
- CQ 码(多媒体消息): component/cqcode.md
|
||||
- 上下文: component/context.md
|
||||
- 进阶开发:
|
||||
- 进阶开发: advanced/index.md
|
||||
- 从 v1 升级: advanced/to-v2.md
|
||||
- 内部类文件手册: advanced/inside-class.md
|
||||
- FAQ: FAQ.md
|
||||
- 更新日志:
|
||||
- 更新日志(v2): update/v2.md
|
||||
- 更新日志(v1): update/v1.md
|
||||
- <u>炸毛框架 v1</u>: https://docs-v1.zhamao.xin/
|
||||
39
mybot.php
Normal file
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use OneBot\Driver\Event\Http\HttpRequestEvent;
|
||||
|
||||
require 'vendor/autoload.php';
|
||||
|
||||
// 创建框架 App
|
||||
$app = new ZM\ZMApplication();
|
||||
// 传入自定义配置文件
|
||||
$app->patchConfig([
|
||||
'driver' => 'workerman',
|
||||
]);
|
||||
// 改变启动所需的参数
|
||||
$app->patchArgs([
|
||||
'--private-mode',
|
||||
]);
|
||||
// 如果有 Composer 依赖的插件,使用 enablePlugins 进行开启
|
||||
$app->enablePlugins([
|
||||
'a',
|
||||
'b',
|
||||
'c',
|
||||
'd',
|
||||
]);
|
||||
// BotCommand 事件构造
|
||||
$cmd = BotCommand::make('test')->on(function () {
|
||||
ctx()->reply('test ok');
|
||||
});
|
||||
$event = BotEvent::make(type: 'message')->on(function () {
|
||||
});
|
||||
$app->addBotEvent($event);
|
||||
$app->addBotCommand($cmd);
|
||||
|
||||
$app->addEvent(HttpRequestEvent::getName(), function (HttpRequestEvent $event) {
|
||||
$event->withResponse(\OneBot\Http\HttpFactory::getInstance()->createResponse(503));
|
||||
});
|
||||
|
||||
$app->run();
|
||||
10
package.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"vuepress-theme-antdocs": "^1.4.5",
|
||||
"vuepress": "^1.9.7"
|
||||
},
|
||||
"scripts": {
|
||||
"docs:dev": "vuepress dev docs",
|
||||
"docs:build": "vuepress build docs"
|
||||
}
|
||||
}
|
||||
22
phpstan.neon
Normal file
@@ -0,0 +1,22 @@
|
||||
parameters:
|
||||
reportUnmatchedIgnoredErrors: false
|
||||
level: 4
|
||||
paths:
|
||||
- ./src/
|
||||
- ./tests/
|
||||
excludePaths:
|
||||
- ./src/ZM/Exception/Solution/SolutionRepository.php
|
||||
ignoreErrors:
|
||||
- '#Constant .* not found#'
|
||||
- '#PHPDoc tag @throws with type Psr\\Container\\ContainerExceptionInterface is not subtype of Throwable#'
|
||||
- '#Unsafe usage of new static#'
|
||||
- '#Call to method initTableList\(\) of deprecated class ZM\\DB\\DB#'
|
||||
- '#class Fiber#'
|
||||
- # Ignore Prophesize deprecation bug: https://github.com/phpstan/phpstan-deprecation-rules/issues/76
|
||||
message: '#^Call to deprecated method prophesize\(\) of class Tests\\TestCase#'
|
||||
path: tests
|
||||
dynamicConstantNames:
|
||||
- SWOOLE_VERSION
|
||||
- ZM_TEST_LOG_DEBUG
|
||||
- _PHAR_STUB_ID
|
||||
- LOAD_MODE
|
||||
33
phpunit.xml.dist
Normal file
@@ -0,0 +1,33 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<phpunit bootstrap="./tests/bootstrap.php"
|
||||
backupGlobals="false"
|
||||
backupStaticAttributes="false"
|
||||
colors="true"
|
||||
convertDeprecationsToExceptions="true"
|
||||
convertErrorsToExceptions="true"
|
||||
convertNoticesToExceptions="true"
|
||||
convertWarningsToExceptions="true"
|
||||
processIsolation="false"
|
||||
stopOnError="false"
|
||||
stopOnFailure="false"
|
||||
testdox="true"
|
||||
verbose="true"
|
||||
>
|
||||
<testsuites>
|
||||
<testsuite name="Zhamao Framework Test Suite">
|
||||
<directory suffix="Test.php">./tests</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
<coverage>
|
||||
<include>
|
||||
<directory suffix=".php">./src/ZM</directory>
|
||||
</include>
|
||||
<report>
|
||||
<html outputDirectory="./build/html-coverage"/>
|
||||
<clover outputFile="./build/coverage.xml"/>
|
||||
</report>
|
||||
</coverage>
|
||||
<php>
|
||||
<env name="APP_ENV" value="testing"/>
|
||||
</php>
|
||||
</phpunit>
|
||||
|
Before Width: | Height: | Size: 385 KiB |
|
Before Width: | Height: | Size: 115 KiB |