Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d85e2498fd | ||
|
|
507382b6d2 | ||
|
|
f343cf8549 | ||
|
|
832d5baf35 | ||
|
|
0253f32574 | ||
|
|
6173180272 | ||
|
|
2d21931fb1 | ||
|
|
bdaa7187fd | ||
|
|
e7ad6e06ec | ||
|
|
7c14483852 | ||
|
|
8b4b1f934e | ||
|
|
713eb3f169 | ||
|
|
48297c80d1 | ||
|
|
315ffdac92 | ||
|
|
7ddc17e6e5 | ||
|
|
e3b8073c82 | ||
|
|
80517e416c | ||
|
|
4b71aade9d | ||
|
|
1b52698c54 | ||
|
|
b8f372d83f | ||
|
|
7bc5f55fb2 | ||
|
|
7eeafd03b3 | ||
|
|
f88480d271 | ||
|
|
9b8cce85bd | ||
|
|
aa364b57e8 | ||
|
|
ea973d0de4 | ||
|
|
1a1b64cbb2 | ||
|
|
09c7e11dbc | ||
|
|
3450a56756 | ||
|
|
f37f4466ae | ||
|
|
451834b219 | ||
|
|
91924353e2 | ||
|
|
67cf94fc95 | ||
|
|
36a343c09f | ||
|
|
4a37aebbf1 | ||
|
|
d5fa9f8d72 | ||
|
|
08c244334b | ||
|
|
23dd1c2726 | ||
|
|
23a309d394 | ||
|
|
3c1bb4e145 | ||
|
|
87b320a3e3 | ||
|
|
e4fb9615c7 | ||
|
|
efad4de0af | ||
|
|
04f62700a2 | ||
|
|
08415f7931 | ||
|
|
8bc86af3c8 | ||
|
|
fa2c5e42dc | ||
|
|
e3d3952b81 | ||
|
|
a9171addde | ||
|
|
440ac087ad | ||
|
|
9dd5eb4eeb | ||
|
|
26c2c9abc9 | ||
|
|
663b07c07e | ||
|
|
5add6a0e73 | ||
|
|
5c4912feb4 | ||
|
|
75e115f390 | ||
|
|
8e443d23f4 | ||
|
|
c43c9c319b | ||
|
|
e32b128b0f | ||
|
|
5287421cea | ||
|
|
9bc95ef350 | ||
|
|
028de6addb | ||
|
|
a107605937 | ||
|
|
cec5d0147b | ||
|
|
e46b879fc4 | ||
|
|
b6764fbb29 | ||
|
|
e8899cc314 | ||
|
|
2b2054dd6b | ||
|
|
92705b0ae2 | ||
|
|
2ae8a36b7f | ||
|
|
899139dda1 | ||
|
|
b6b571e84b | ||
|
|
9e098fa997 | ||
|
|
13d00f954f | ||
|
|
fee75d0c8a | ||
|
|
abae3f14cd | ||
|
|
b8a98e2c6e | ||
|
|
d990572b5b | ||
|
|
2d37d392bc | ||
|
|
59015fc3bb | ||
|
|
c3be68f988 | ||
|
|
8610784958 | ||
|
|
f01d71c34a | ||
|
|
a06501e4b1 | ||
|
|
7c2c30fa1b | ||
|
|
903dc714f2 | ||
|
|
b1bd2c8805 | ||
|
|
0e4b4f6d8c | ||
|
|
d8d0eaf247 | ||
|
|
90096faef3 | ||
|
|
f794f75394 | ||
|
|
8de63e486c | ||
|
|
4308efca47 | ||
|
|
877fa292d6 | ||
|
|
df6ea6f497 | ||
|
|
5e9be391e1 | ||
|
|
49960b8234 | ||
|
|
d669dbe54b | ||
|
|
2d1859ac0c | ||
|
|
dc864055a3 | ||
|
|
bad9537aba | ||
|
|
303b1adc0a | ||
|
|
c582904d41 | ||
|
|
65eabf6153 | ||
|
|
cc9c9e2253 | ||
|
|
8747ae9564 | ||
|
|
9253e54958 | ||
|
|
fb774f2caa | ||
|
|
ba120024a6 | ||
|
|
1fe0e69914 | ||
|
|
0fe04e411c | ||
|
|
35074f959f | ||
|
|
0d698fbaf1 | ||
|
|
c9e2a52327 | ||
|
|
a58e06109b | ||
|
|
65375e4836 | ||
|
|
edc517d33f | ||
|
|
354c82dc34 | ||
|
|
fe2acd0665 | ||
|
|
19b5b1a90c | ||
|
|
e7281e2118 | ||
|
|
10f334cf15 | ||
|
|
a057fd6458 | ||
|
|
3bafabeb0a | ||
|
|
edefbe17da | ||
|
|
a65041ffcd | ||
|
|
0962c60844 | ||
|
|
3d72eceac0 | ||
|
|
57b5deff5b | ||
|
|
1163dee352 | ||
|
|
f7fe562bba | ||
|
|
3394f98a8a | ||
|
|
a575b35286 | ||
|
|
0f41e79e98 | ||
|
|
24bf22a31f | ||
|
|
05a1b8802e | ||
|
|
9c409d9507 | ||
|
|
4ee7161c36 | ||
|
|
4cd9ce0ff7 | ||
|
|
a2d0dfd8b2 | ||
|
|
d4164fe0ba | ||
|
|
6bbecdd7c0 | ||
|
|
0784c3ae05 | ||
|
|
679757ccfa | ||
|
|
63a4e693e3 | ||
|
|
b21041d9e0 | ||
|
|
f014bb4587 | ||
|
|
9030f7c4ce | ||
|
|
bde6c38bd4 | ||
|
|
0aefb6c6ec | ||
|
|
da650ccdef | ||
|
|
92d3e70c1f | ||
|
|
cb5dfaf066 | ||
|
|
cd75e5e4cc | ||
|
|
7bd6ab5ec0 | ||
|
|
92f9c89cf1 | ||
|
|
eb2632b23a | ||
|
|
8c90bc6808 | ||
|
|
66146532e1 | ||
|
|
dbf3a41c1d | ||
|
|
942bbf9c6c | ||
|
|
a9d7cc527c | ||
|
|
2d362722ed | ||
|
|
f81638e625 | ||
|
|
8ae77df0ff | ||
|
|
110bd0e5f7 | ||
|
|
1b3a366cb7 | ||
|
|
3b5d9a145f | ||
|
|
fc37fc067a | ||
|
|
bb18a28484 | ||
|
|
86ca08b9aa | ||
|
|
9522df7835 | ||
|
|
55e0fd0226 | ||
|
|
ff9a986a2e | ||
|
|
df31fe2a8b | ||
|
|
1aeaf996bc | ||
|
|
cb0e59e91a | ||
|
|
5f02f49e9d | ||
|
|
ce9f91fe2c | ||
|
|
ac37745721 | ||
|
|
e44aeefda8 | ||
|
|
dbff2b659a | ||
|
|
52ef8e8b76 | ||
|
|
b71567c754 | ||
|
|
d1670c743f | ||
|
|
009b38f471 | ||
|
|
0cf5f30c0d | ||
|
|
cd4e1f179d | ||
|
|
b89f9d6b35 | ||
|
|
c3ece96390 | ||
|
|
770739b44e | ||
|
|
84984c2cde | ||
|
|
86393c8674 | ||
|
|
d4aed3bc45 | ||
|
|
818cd77450 | ||
|
|
dc26c70400 | ||
|
|
6547d713ea | ||
|
|
e8dcb0c242 | ||
|
|
100d0be513 | ||
|
|
fc8db2d977 | ||
|
|
e5d98d7889 | ||
|
|
c7b1a05ed3 | ||
|
|
649a90bd5e | ||
|
|
0fc880cf21 | ||
|
|
0d82a4c2c6 | ||
|
|
fb3aa59683 | ||
|
|
b938792ca8 | ||
|
|
bb74946d54 | ||
|
|
3e78105509 | ||
|
|
c6adf0ac89 | ||
|
|
219b4dca94 | ||
|
|
acec292b0f | ||
|
|
7ba86cbd23 | ||
|
|
3002eaf479 | ||
|
|
15422ca504 | ||
|
|
7067f00bee | ||
|
|
a0616eb251 | ||
|
|
5d4da0e4c7 | ||
|
|
546e3475fb | ||
|
|
56c44ebb58 | ||
|
|
bd03bc730c | ||
|
|
e0e1e58104 | ||
|
|
940de662b6 | ||
|
|
d151c1192b | ||
|
|
15ae10579c | ||
|
|
359696408d | ||
|
|
d3766368f8 | ||
|
|
73af7451fc | ||
|
|
e27fcefe45 | ||
|
|
aa5ade5517 | ||
|
|
94b6a66ce4 | ||
|
|
a51e486fa6 | ||
|
|
2b608daea4 | ||
|
|
aa60c2afd1 | ||
|
|
cff22c74a9 | ||
|
|
b77b8ee0da | ||
|
|
610aef2f14 | ||
|
|
e15ca41d6e | ||
|
|
60e9e53af4 | ||
|
|
9812000724 | ||
|
|
d72401fc39 | ||
|
|
822eba7982 | ||
|
|
9327564e6e | ||
|
|
6e9fe42a83 | ||
|
|
a32cf28ccb | ||
|
|
55b101802a | ||
|
|
ecda8b4394 | ||
|
|
6d4f8d4c26 | ||
|
|
26aa53f3fa | ||
|
|
2f7a94ea3f | ||
|
|
2b622b4ee9 | ||
|
|
3e280f1e08 | ||
|
|
c54d52ad45 | ||
|
|
e54d3ea96b | ||
|
|
e07cb8257d | ||
|
|
74b6a8fdba | ||
|
|
e48c76397c | ||
|
|
64aff5d041 | ||
|
|
87b24e35d0 | ||
|
|
fb48c81207 | ||
|
|
cd9f302f8b | ||
|
|
df3bc97988 | ||
|
|
94191a2902 | ||
|
|
a0dd540075 | ||
|
|
5684665123 | ||
|
|
5db407d750 | ||
|
|
27a86f0dc1 | ||
|
|
767ea23173 | ||
|
|
85707508ed | ||
|
|
e8bc59ff9e | ||
|
|
7f9e622a64 | ||
|
|
b22e37596b | ||
|
|
642e90b1c8 | ||
|
|
8402ab896a | ||
|
|
d6dc7edf66 | ||
|
|
8f6dbf70f7 | ||
|
|
bd14d9a71e | ||
|
|
61a0a38dd2 | ||
|
|
102f438a99 | ||
|
|
586a2d1c79 | ||
|
|
5b04c39107 | ||
|
|
d96e91ea70 | ||
|
|
68ac209aae | ||
|
|
90b961f900 | ||
|
|
9945fe0fd8 | ||
|
|
1346ce0472 | ||
|
|
e914aaa961 | ||
|
|
70e17250e8 | ||
|
|
8c134904ba | ||
|
|
bfa4b14c13 | ||
|
|
36c7820d78 | ||
|
|
92e6530d3e | ||
|
|
9e47af301e | ||
|
|
b59eb57dce | ||
|
|
158a557867 | ||
|
|
5a95e9b1ee | ||
|
|
98d3ca31d7 | ||
|
|
52bd5e7ae6 | ||
|
|
b883f0c9ef | ||
|
|
e61b808c3c | ||
|
|
7bc049de68 | ||
|
|
de3f859d98 | ||
|
|
ea06277649 | ||
|
|
9a6bd67e43 | ||
|
|
01baee5cf2 | ||
|
|
bc855bc089 | ||
|
|
4bf1210651 | ||
|
|
a4f4398765 | ||
|
|
0f343841a6 | ||
|
|
41d26799b2 | ||
|
|
a807d7647b | ||
|
|
7bcd2a6206 | ||
|
|
a3c2c1bb22 | ||
|
|
355e47ec11 | ||
|
|
a01bee8330 | ||
|
|
c4c020aaa6 | ||
|
|
35e92bebbd | ||
|
|
2bd3e9f556 | ||
|
|
60705d1558 | ||
|
|
2cbd08c470 | ||
|
|
51a501611d | ||
|
|
46bcfb5695 | ||
|
|
e44bd55df2 | ||
|
|
efaa090053 | ||
|
|
8be13fbc5c | ||
|
|
e0a1be1417 | ||
|
|
7f841f6862 | ||
|
|
d1804996d7 | ||
|
|
34084a7ee8 | ||
|
|
fedde8bb87 | ||
|
|
7439e80eec | ||
|
|
e388f361af | ||
|
|
2a9ed6bac7 | ||
|
|
5936a86159 | ||
|
|
3b22911fb1 | ||
|
|
0dd3762685 | ||
|
|
fd9106e97e | ||
|
|
5a3d709e4f | ||
|
|
c0c357dc39 | ||
|
|
e76bec3d4f | ||
|
|
384b3974f7 | ||
|
|
3c37044cd8 | ||
|
|
541112a9eb | ||
|
|
59d7613485 | ||
|
|
d1a579372d | ||
|
|
16cd247db8 | ||
|
|
872d6d9880 | ||
|
|
79fe322d69 | ||
|
|
fe6f1cb547 | ||
|
|
8d7fcd0b84 | ||
|
|
46b0c3cee2 | ||
|
|
1ad9c3017c | ||
|
|
5d320fa22f | ||
|
|
5e1d9b6463 | ||
|
|
175dfde0b8 | ||
|
|
87717ae214 | ||
|
|
111c8f3160 | ||
|
|
6c756352cf | ||
|
|
97a5d11c1f | ||
|
|
44415d824e | ||
|
|
286e8f3afe | ||
|
|
4c2e3db2d5 | ||
|
|
6faf699fcc | ||
|
|
80b765e56a | ||
|
|
3f9ebe5c8c | ||
|
|
481371fd03 | ||
|
|
36c2165fc3 | ||
|
|
36402626e0 | ||
|
|
40c39e61e1 | ||
|
|
4e1b13e776 | ||
|
|
7f355ff43a | ||
|
|
22df449013 | ||
|
|
68303f5124 | ||
|
|
33babfef6b | ||
|
|
910a9185c7 | ||
|
|
f5bd74dfa1 | ||
|
|
b982640c85 | ||
|
|
245bcc46ed | ||
|
|
08a346bfa1 | ||
|
|
0cb4dd6d63 | ||
|
|
1a7a78cc51 | ||
|
|
9b69875b78 | ||
|
|
999ae4782b | ||
|
|
3c61ca8c94 | ||
|
|
0790e0a0ef | ||
|
|
0a2d77b699 | ||
|
|
051ada3f21 | ||
|
|
faf8d053dd | ||
|
|
5ece56f9b0 | ||
|
|
c4d9474f34 | ||
|
|
3b8481ccac | ||
|
|
a31d0215f5 | ||
|
|
f6cce68553 | ||
|
|
636b100b9f | ||
|
|
7a3574eaa0 | ||
|
|
9cd6176e60 | ||
|
|
f2641029cb | ||
|
|
6350e191f8 | ||
|
|
c570ba3488 | ||
|
|
b64abd24c8 | ||
|
|
13c9ab3962 | ||
|
|
b9f1c6ee79 | ||
|
|
938170a957 | ||
|
|
ca0a28c0cf | ||
|
|
c3967173a1 | ||
|
|
9ebbd9a1fd | ||
|
|
16e343ca19 | ||
|
|
1de9132f16 | ||
|
|
dad54a8e47 | ||
|
|
7d73b96b78 | ||
|
|
1260fe34aa | ||
|
|
3b4678e059 | ||
|
|
ff8294b5db | ||
|
|
7e94e19f5d | ||
|
|
ae18e2575a | ||
|
|
a5c493a29c | ||
|
|
8ee89fe51c | ||
|
|
b4909847bf | ||
|
|
568be88127 | ||
|
|
67144c1d08 | ||
|
|
285c78d41a | ||
|
|
7d15735444 | ||
|
|
4191749564 | ||
|
|
9e68dbbf09 | ||
|
|
f02ad5b051 | ||
|
|
08e20a25ed | ||
|
|
ee0468038d | ||
|
|
04afc297d9 | ||
|
|
ed06ca76e0 | ||
|
|
a68510a814 | ||
|
|
4040efab01 | ||
|
|
f08cc1061e | ||
|
|
3b5b11891e | ||
|
|
72427c9309 | ||
|
|
befe2068fd | ||
|
|
c7a3ef32fa | ||
|
|
51d394f4ef | ||
|
|
8db341dd70 | ||
|
|
5cd7fec3ab | ||
|
|
f672ecbb45 | ||
|
|
9a12b2c9b6 | ||
|
|
c31407fd87 | ||
|
|
646c81f136 | ||
|
|
aca6232d2a | ||
|
|
c61da10523 | ||
|
|
1f53d65ac2 | ||
|
|
844ab1287e | ||
|
|
d6d318b23b | ||
|
|
7504ffd934 | ||
|
|
e8078908c9 | ||
|
|
431848280f | ||
|
|
2e0c575512 | ||
|
|
ebf0d99417 | ||
|
|
423d893fee | ||
|
|
56fe0d3b04 | ||
|
|
7172d9d203 | ||
|
|
bd770a6902 | ||
|
|
628508faed | ||
|
|
826634f061 | ||
|
|
c7ed959a71 | ||
|
|
aea6befc1e | ||
|
|
7bb3484788 | ||
|
|
4eafc67a7e | ||
|
|
b5197b6f20 | ||
|
|
d306304195 | ||
|
|
35f47dae4f | ||
|
|
f1be4810d5 | ||
|
|
d4f9406869 | ||
|
|
a41b81d724 | ||
|
|
da56b86102 | ||
|
|
f12d2c9be0 | ||
|
|
322b9b4c25 | ||
|
|
fb7fe410f9 | ||
|
|
ee392a079c | ||
|
|
0502579d50 | ||
|
|
f10f3175d9 | ||
|
|
f155af99f1 | ||
|
|
0fbf3575a6 | ||
|
|
f6b9b5f54e | ||
|
|
2dadd9d8c9 | ||
|
|
5786b3cdfe | ||
|
|
769fc30c0c | ||
|
|
fb2dad9c6f | ||
|
|
c43d308a2d | ||
|
|
742a12ce6a | ||
|
|
d953f27865 | ||
|
|
4719cded95 | ||
|
|
9d489bdb69 | ||
|
|
537c307550 | ||
|
|
d5575e0323 | ||
|
|
592962bbc8 | ||
|
|
7a371c86dc | ||
|
|
0e295bebde | ||
|
|
8d98759be6 | ||
|
|
924317b01b | ||
|
|
a08a601dc3 | ||
|
|
e652c15d6c | ||
|
|
4534eb60de | ||
|
|
54c6c0c24c | ||
|
|
393694ae96 | ||
|
|
818bb730e4 | ||
|
|
61bd35a192 | ||
|
|
81f9df000a | ||
|
|
0dcb31c108 | ||
|
|
cd4140fda9 | ||
|
|
117cc25cdb | ||
|
|
832ad26151 | ||
|
|
5a344cd8ad | ||
|
|
0b0a67cdad | ||
|
|
a326a240f0 | ||
|
|
dfdb033769 | ||
|
|
a31af45d97 | ||
|
|
cae955c483 | ||
|
|
0bfff5259e | ||
|
|
0fa83e603b | ||
|
|
906b03804f | ||
|
|
1ed7a61bea | ||
|
|
ad6ae7ab05 | ||
|
|
5e2de1d47d | ||
|
|
591ca51104 | ||
|
|
b210841158 | ||
|
|
a223a3985b | ||
|
|
8baa6a73cd | ||
|
|
508efac2ea | ||
|
|
b6eed9e228 | ||
|
|
b62cad07f0 | ||
|
|
6d552d27ac | ||
|
|
39d0f2a060 | ||
|
|
0b03abee91 | ||
|
|
b4fb69d0ee | ||
|
|
5b9ed029b3 | ||
|
|
c29397371b | ||
|
|
fe47e27788 | ||
|
|
a14c702c79 | ||
|
|
fe4246036d | ||
|
|
074063b814 | ||
|
|
0271a8b721 | ||
|
|
08617383be | ||
|
|
3840459b0a | ||
|
|
aee7ac28bc | ||
|
|
9e6cd39b2d | ||
|
|
8898429046 | ||
|
|
a74def6dbd | ||
|
|
9f783cd35e | ||
|
|
9d2f061d81 | ||
|
|
a6045b2d54 | ||
|
|
6142b2b564 | ||
|
|
ed53ccb919 | ||
|
|
1ab45c6cfd | ||
|
|
c2fdb090e1 | ||
|
|
a10b47eb7e | ||
|
|
7b234e2e37 | ||
|
|
90d45a4698 | ||
|
|
5e40b70e19 | ||
|
|
a27d0c3efb | ||
|
|
46ed506b35 | ||
|
|
0ad813a1fe | ||
|
|
e57e42f47a | ||
|
|
cc58157d37 | ||
|
|
9b5bb0a339 | ||
|
|
84ebbea287 | ||
|
|
1c2021f54b | ||
|
|
98a7f00d7d | ||
|
|
60d97d0e67 | ||
|
|
03f4a07cbe | ||
|
|
3fbe526793 | ||
|
|
a892f03382 | ||
|
|
8db69f6e69 | ||
|
|
f9037ff9fc | ||
|
|
366ddffa2b | ||
|
|
b8468588de | ||
|
|
62dd6f7f4a | ||
|
|
858202f1bb | ||
|
|
db7900dc26 | ||
|
|
aa87e437dc | ||
|
|
741e5e86f8 | ||
|
|
390a171891 | ||
|
|
1fc5e2b47c | ||
|
|
8926dc7cc6 | ||
|
|
5c3d3c0d70 | ||
|
|
41b29cbf60 | ||
|
|
44463c199a | ||
|
|
9071adcdcc | ||
|
|
21cd92ca13 | ||
|
|
d8a3e8dc43 | ||
|
|
3f5707952c | ||
|
|
38a9590be0 | ||
|
|
a7ce117b87 | ||
|
|
c3b24a243c | ||
|
|
cb2c11df43 | ||
|
|
98f88dcf68 | ||
|
|
9fb12e0e23 | ||
|
|
0ca106f16b | ||
|
|
33210c6374 | ||
|
|
045ab67f5d | ||
|
|
19259c93ea | ||
|
|
9b6784a018 | ||
|
|
2fe3706e9c | ||
|
|
a4e310a7f8 | ||
|
|
802da9f3b2 | ||
|
|
0692979b19 | ||
|
|
fb8b146bc4 | ||
|
|
fdbaa43ec9 | ||
|
|
4a807530c1 | ||
|
|
84784faa1e | ||
|
|
bd40978dee | ||
|
|
d4d94ffe0e | ||
|
|
d984382a33 | ||
|
|
02d05812d2 | ||
|
|
1dbf568bac | ||
|
|
6bc3aed348 | ||
|
|
c1e8e0f09c | ||
|
|
8f8bcfc474 | ||
|
|
b386df982d | ||
|
|
fa846ddf9b | ||
|
|
16685ed8ea | ||
|
|
4addccbd55 | ||
|
|
e9a20c4326 | ||
|
|
f073aeef13 | ||
|
|
eb08739eea | ||
|
|
aff3b01e39 | ||
|
|
2d64b26e86 | ||
|
|
7b7bd02503 | ||
|
|
7904516c97 | ||
|
|
6e0bafdd89 | ||
|
|
2b1e605e5f | ||
|
|
f454310a09 | ||
|
|
b6579b5b96 | ||
|
|
4ef06d7b92 | ||
|
|
e0c594a71b | ||
|
|
a48d386d8b | ||
|
|
d592858a90 | ||
|
|
a3cb3e983a | ||
|
|
4eb9029b2c | ||
|
|
b3835b0aa0 | ||
|
|
b2db58a922 | ||
|
|
acb6b8e0d3 | ||
|
|
21c6e1e860 | ||
|
|
571b65c423 | ||
|
|
1631a62abe | ||
|
|
e88a7ca8ce | ||
|
|
d54f64a9cc | ||
|
|
03dbd5db82 | ||
|
|
2446c292bf | ||
|
|
5ab99dfd69 | ||
|
|
605f46fda8 | ||
|
|
e53f1eafe8 | ||
|
|
1457185e84 | ||
|
|
ad43bec18d | ||
|
|
c0a4b223ea | ||
|
|
65917e1957 | ||
|
|
a19cede60d | ||
|
|
a3ead95ea4 | ||
|
|
c46bc1b923 | ||
|
|
b327212cbe | ||
|
|
4f348c7e48 | ||
|
|
f2d0001b11 | ||
|
|
66a224a0e3 | ||
|
|
1fa1206568 | ||
|
|
87eea28ac2 | ||
|
|
29ca0217b3 | ||
|
|
306f75ff3b | ||
|
|
6ecb203570 | ||
|
|
2e7a3a6b61 | ||
|
|
86bc4a92ba | ||
|
|
f652277e18 | ||
|
|
14a3535439 | ||
|
|
edce7424de | ||
|
|
0fa121bbe2 | ||
|
|
9c046a92be | ||
|
|
e4506e14da | ||
|
|
515a00346c | ||
|
|
399e27e24e | ||
|
|
9413376138 | ||
|
|
3caec14783 | ||
|
|
6e427a0a34 | ||
|
|
40c5e95d93 | ||
|
|
ab70e27266 | ||
|
|
301b6f57d3 | ||
|
|
4c9f5ed907 | ||
|
|
04d65150ed | ||
|
|
d29165c9a5 | ||
|
|
30db175d52 | ||
|
|
5c666c0dd4 | ||
|
|
3ccc0469c0 | ||
|
|
5ab3244d6a | ||
|
|
05bc8abbee | ||
|
|
8e9056a0cd | ||
|
|
ea75b357a7 | ||
|
|
2b607e9b44 | ||
|
|
57d3528a96 | ||
|
|
660a04aa52 | ||
|
|
d0a408001d | ||
|
|
48391533a6 | ||
|
|
f2113adbbc | ||
|
|
abfcbc7397 | ||
|
|
96f2982325 | ||
|
|
3a521bc06c | ||
|
|
6fbaec0083 | ||
|
|
cae94c7b5c | ||
|
|
bce012f692 | ||
|
|
b23b639a8b | ||
|
|
5a628907ed | ||
|
|
6da479c0d7 | ||
|
|
cbb321df6d | ||
|
|
0a7beb6909 | ||
|
|
2105094ac3 | ||
|
|
d8fee686f8 | ||
|
|
75fc1fd7e0 | ||
|
|
e718652491 | ||
|
|
bcb49f2be7 | ||
|
|
dab63e4060 | ||
|
|
3fdacbb6ac | ||
|
|
f02b62e620 | ||
|
|
51acce14a0 | ||
|
|
b63f6ac39e | ||
|
|
9e107a8a76 | ||
|
|
8c083242d3 | ||
|
|
2a068d561f | ||
|
|
ef96fbf8c5 | ||
|
|
2c381a0b9c | ||
|
|
fef2bcb6e2 | ||
|
|
2f6ed2fb58 | ||
|
|
4eb23d2c09 | ||
|
|
ec3de8dbcf | ||
|
|
f6f09747dc | ||
|
|
27a534a605 | ||
|
|
3cbd940843 | ||
|
|
e728eec01a | ||
|
|
d06d2b78a8 | ||
|
|
5878a9196e | ||
|
|
85dd061f96 | ||
|
|
8381c3e946 | ||
|
|
6ff8a9705d | ||
|
|
fb5819b966 | ||
|
|
5cc1af3f82 | ||
|
|
c279de694c | ||
|
|
f7a1787f1f | ||
|
|
5442fd6e93 | ||
|
|
b8c8ca7653 | ||
|
|
5ca7dbc84b | ||
|
|
d836c9706f | ||
|
|
318be43a4f | ||
|
|
95bebce731 | ||
|
|
27b8c9d058 | ||
|
|
10d4d24526 | ||
|
|
b19917bd3d | ||
|
|
4b6774b1db | ||
|
|
360e0a7951 | ||
|
|
171f516eee | ||
|
|
01943f6970 | ||
|
|
953d48ee1f | ||
|
|
2b6028bcb1 | ||
|
|
79b7b7d2a2 | ||
|
|
93a6a7b87f | ||
|
|
3b499d6af5 | ||
|
|
6481919597 | ||
|
|
228f6ec8c5 | ||
|
|
6723f201b7 | ||
|
|
c250bb49f0 | ||
|
|
3b607662e5 | ||
|
|
f2d7041cb0 | ||
|
|
afc06794c2 | ||
|
|
33eaafcff1 | ||
|
|
e5a8743b9a | ||
|
|
3322c4e2a5 | ||
|
|
aa39aaf489 | ||
|
|
a238581f44 | ||
|
|
00814ef64e | ||
|
|
6362d2966b | ||
|
|
4e181a82b6 | ||
|
|
374199905d | ||
|
|
642079ab1f | ||
|
|
215c9d96b9 | ||
|
|
5369eda705 | ||
|
|
5e42bbe1eb | ||
|
|
79dd2ee03e | ||
|
|
78d3eae44b | ||
|
|
a9ee68fe1a | ||
|
|
ee5678ccfb | ||
|
|
248a675c1a | ||
|
|
6b1d84cf38 | ||
|
|
c0977538ff | ||
|
|
c8727e8b2f | ||
|
|
55a41e19f4 | ||
|
|
6096c0b158 | ||
|
|
161c6dfd95 | ||
|
|
078bcf7652 | ||
|
|
57c6df3970 | ||
|
|
1130ee1b21 | ||
|
|
c9c0a29740 | ||
|
|
e60d972bff | ||
|
|
3c4a139f52 | ||
|
|
bf615efa96 | ||
|
|
2af3940be7 | ||
|
|
64e145909e | ||
|
|
e90f34cc96 | ||
|
|
a23cf2dd26 | ||
|
|
7b623d0faa | ||
|
|
14788b1d53 | ||
|
|
e1bf8cf735 | ||
|
|
2117731b46 | ||
|
|
74f8fe1caf | ||
|
|
9388a3b277 | ||
|
|
13e7a05e1b | ||
|
|
885fa25c0c | ||
|
|
966b35094f | ||
|
|
65607bf3b3 | ||
|
|
5fcba7a7db | ||
|
|
f9ec3cb753 | ||
|
|
bf6ffd65a9 | ||
|
|
d680b6b43a | ||
|
|
3ead3a0ba3 | ||
|
|
4c5c977310 | ||
|
|
89a9ce87da | ||
|
|
52e469f6ac | ||
|
|
4f275de8d4 | ||
|
|
d9de41e350 | ||
|
|
a4c279ba25 | ||
|
|
e68282a9e4 | ||
|
|
8613206c3a | ||
|
|
31b8cde8bf | ||
|
|
2f2f9520db | ||
|
|
2501ae836e | ||
|
|
2c8bf91e77 | ||
|
|
0ac063498e | ||
|
|
2bf672f0cd | ||
|
|
ca209adca0 | ||
|
|
dd10c82c5e | ||
|
|
c3ad2a0931 | ||
|
|
30aacc8946 | ||
|
|
723a644937 | ||
|
|
165eeb5bf2 | ||
|
|
f1776d4a26 | ||
|
|
2f2a1b3182 | ||
|
|
0477cf7db5 | ||
|
|
2d7c7986aa | ||
|
|
bf84ea8fca | ||
|
|
f3ef8a7f96 | ||
|
|
ec214f610a | ||
|
|
013fd46722 | ||
|
|
32d583b9ef | ||
|
|
df6e201ba5 | ||
|
|
789b613b82 | ||
|
|
b698342db4 | ||
|
|
d853f2fe99 | ||
|
|
20d66b5b31 | ||
|
|
da7264c5e8 | ||
|
|
f3299f78d9 | ||
|
|
7ef0805c91 | ||
|
|
ba1afa2569 | ||
|
|
936e2d0863 | ||
|
|
166410f66a | ||
|
|
f8c9298694 | ||
|
|
b0b642b6bb | ||
|
|
5fa20343da | ||
|
|
bbbf181011 | ||
|
|
6af547d2af | ||
|
|
d72b1822cb | ||
|
|
d34df18da8 | ||
|
|
27cb247760 | ||
|
|
7a66d6fccd | ||
|
|
f1cb372339 | ||
|
|
46d65e6fc0 | ||
|
|
6168298b5e | ||
|
|
d5fef413ca | ||
|
|
0802ee6106 | ||
|
|
65d0f87eeb | ||
|
|
488868d80e | ||
|
|
154965efcb | ||
|
|
f364f0b495 | ||
|
|
82e2f5bcb9 | ||
|
|
f7a2228925 | ||
|
|
dadc9d4efd | ||
|
|
df59867431 | ||
|
|
a7c6e9dc00 | ||
|
|
6b51cfd448 | ||
|
|
98b86ca527 | ||
|
|
9a2ef4c52a | ||
|
|
d69d4bd29d | ||
|
|
97cdc56c26 | ||
|
|
c2440b9c79 | ||
|
|
f0b69d089a | ||
|
|
ac79549934 | ||
|
|
e6b8b5e904 | ||
|
|
5a22e5b2b2 | ||
|
|
431d7bf9d9 | ||
|
|
865ea6e66b | ||
|
|
2f787d6a53 | ||
|
|
939ac28d00 | ||
|
|
76068add09 | ||
|
|
0730dd6756 | ||
|
|
64cce8e8be | ||
|
|
2d12dc0f3e | ||
|
|
82a60e99cc | ||
|
|
1cbd18db76 | ||
|
|
48ccd636cd | ||
|
|
573dd0da7e | ||
|
|
df8583801f | ||
|
|
ff2b434133 | ||
|
|
30cae74d50 | ||
|
|
a896d32d37 | ||
|
|
d9d4527894 | ||
|
|
e93caac34b | ||
|
|
cd5182f8ee | ||
|
|
b185c02252 | ||
|
|
58aa10be9c | ||
|
|
5d3d3bf03a | ||
|
|
11af1cb390 | ||
|
|
32057573b7 | ||
|
|
0b6058654e | ||
|
|
a7fe444166 | ||
|
|
f4f734d8ff | ||
|
|
983e8dbbd3 | ||
|
|
b2150477d7 | ||
|
|
f83eeac33a |
No files matched your search
+7
-1
@@ -1,11 +1,17 @@
|
||||
Dockerfile
|
||||
tokens
|
||||
.*sessions
|
||||
files
|
||||
venv
|
||||
.wwebjs_auth
|
||||
docs
|
||||
examples
|
||||
.github
|
||||
.git
|
||||
|
||||
# compiled output
|
||||
/dist
|
||||
/node_modules
|
||||
.yarn
|
||||
|
||||
# Logs
|
||||
logs
|
||||
|
||||
+33
-22
@@ -1,25 +1,36 @@
|
||||
module.exports = {
|
||||
parser: '@typescript-eslint/parser',
|
||||
parserOptions: {
|
||||
project: 'tsconfig.json',
|
||||
sourceType: 'module',
|
||||
},
|
||||
plugins: ['@typescript-eslint/eslint-plugin'],
|
||||
extends: [
|
||||
'plugin:@typescript-eslint/eslint-recommended',
|
||||
'plugin:@typescript-eslint/recommended',
|
||||
'prettier',
|
||||
'prettier/@typescript-eslint',
|
||||
parser: '@typescript-eslint/parser',
|
||||
parserOptions: {
|
||||
project: 'tsconfig.json',
|
||||
sourceType: 'module',
|
||||
},
|
||||
plugins: ['@typescript-eslint/eslint-plugin', 'simple-import-sort'],
|
||||
extends: [
|
||||
'plugin:@typescript-eslint/eslint-recommended',
|
||||
'plugin:@typescript-eslint/recommended',
|
||||
'prettier',
|
||||
'prettier/@typescript-eslint',
|
||||
],
|
||||
root: true,
|
||||
env: {
|
||||
node: true,
|
||||
jest: true,
|
||||
},
|
||||
rules: {
|
||||
'simple-import-sort/imports': 'error',
|
||||
'simple-import-sort/exports': 'error',
|
||||
'@typescript-eslint/interface-name-prefix': 'off',
|
||||
'@typescript-eslint/explicit-function-return-type': 'off',
|
||||
'@typescript-eslint/explicit-module-boundary-types': 'off',
|
||||
'@typescript-eslint/no-inferrable-types': 'off',
|
||||
'@typescript-eslint/no-explicit-any': 'off',
|
||||
'@typescript-eslint/ban-ts-ignore': 'off',
|
||||
'@typescript-eslint/ban-ts-comment': 'off',
|
||||
'@typescript-eslint/no-unused-vars': 'off',
|
||||
'lines-between-class-members': [
|
||||
'error',
|
||||
'always',
|
||||
{ exceptAfterSingleLine: true },
|
||||
],
|
||||
root: true,
|
||||
env: {
|
||||
node: true,
|
||||
jest: true,
|
||||
},
|
||||
rules: {
|
||||
'@typescript-eslint/interface-name-prefix': 'off',
|
||||
'@typescript-eslint/explicit-function-return-type': 'off',
|
||||
'@typescript-eslint/explicit-module-boundary-types': 'off',
|
||||
'@typescript-eslint/no-explicit-any': 'off',
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,14 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
|
||||
patreon: wa_http_api
|
||||
open_collective: # doks Replace with a single Open Collective username
|
||||
ko_fi: # Replace with a single Ko-fi username
|
||||
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
||||
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
||||
liberapay: # Replace with a single Liberapay username
|
||||
issuehunt: # Replace with a single IssueHunt username
|
||||
otechie: # Replace with a single Otechie username
|
||||
custom:
|
||||
- https://boosty.to/wa-http-api
|
||||
- https://portal.devlike.pro/
|
||||
@@ -0,0 +1,61 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: '[BUG][WEBJS] - '
|
||||
labels: bug
|
||||
assignees: ''
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
|
||||
> A clear and concise description of what the bug is. Feel free to remove
|
||||
> sections that you don't feel to make the text shorter!
|
||||
|
||||
**Version**
|
||||
|
||||
```json
|
||||
{
|
||||
"version": "2023.11.1",
|
||||
"engine": "NOWEB",
|
||||
"tier": "PLUS",
|
||||
"browser": "/usr/bin/google-chrome-stable"
|
||||
}
|
||||
```
|
||||
|
||||
> You can get the WAHA version by calling `GET /api/version` Try to update to
|
||||
> [the latest version](https://github.com/devlikeapro/waha/releases) before
|
||||
> creating an issue!
|
||||
|
||||
**To Reproduce** Steps to reproduce the behavior:
|
||||
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. Scroll down to '....'
|
||||
4. See error
|
||||
|
||||
**Expected behavior**
|
||||
|
||||
> A clear and concise description of what you expected to happen.
|
||||
|
||||
**Engine** I'm using **WEBJS** engine.
|
||||
|
||||
> If you set `WHATSAPP_DEFAULT_ENGINE` explicitly to
|
||||
> [one of the engine](https://waha.devlike.pro/docs/how-to/engines/) - please
|
||||
> provide that information here and in the issue title.
|
||||
|
||||
**Requests - Responses**
|
||||
|
||||
> Please attach all sent requests, responses that you sent.
|
||||
|
||||
**Docker Logs**
|
||||
|
||||
> Collect and attach related docker logs if you have any
|
||||
> https://waha.devlike.pro/docs/how-to/deploy/#viewing-the-logs
|
||||
|
||||
**Screenshots**
|
||||
|
||||
> If applicable, add screenshots to help explain your problem.
|
||||
|
||||
**Additional context**
|
||||
|
||||
> Add any other context about the problem here.
|
||||
@@ -0,0 +1,24 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: '[Feature Request]'
|
||||
labels: ''
|
||||
assignees: ''
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.** I'm always
|
||||
frustrated when [...]
|
||||
|
||||
> A clear and concise description of what the problem is. Ex.
|
||||
|
||||
**Describe the solution you'd like**
|
||||
|
||||
> A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
|
||||
> A clear and concise description of any alternative solutions or features
|
||||
> you've considered.
|
||||
|
||||
**Additional context** Add any other context or screenshots about the feature
|
||||
request here.
|
||||
@@ -0,0 +1,18 @@
|
||||
# Idea from
|
||||
# https://stackoverflow.com/a/65539398/6753144
|
||||
#
|
||||
|
||||
OWNER=devlikeapro
|
||||
REPO=waha-plus
|
||||
|
||||
# list workflows
|
||||
gh api -X GET /repos/$OWNER/$REPO/actions/workflows | jq '.workflows[] | .name,.id'
|
||||
|
||||
# copy the ID of the workflow you want to clear and set it
|
||||
WORKFLOW_ID=40218610
|
||||
|
||||
# list runs
|
||||
gh api -X GET /repos/$OWNER/$REPO/actions/workflows/$WORKFLOW_ID/runs --paginate | jq '.workflow_runs[] | .id'
|
||||
|
||||
# delete runs
|
||||
gh api -X GET /repos/$OWNER/$REPO/actions/workflows/$WORKFLOW_ID/runs --paginate | jq '.workflow_runs[] | .id' | xargs -I{} gh api -X DELETE /repos/$OWNER/$REPO/actions/runs/{} --silent
|
||||
@@ -0,0 +1,10 @@
|
||||
teamLabels:
|
||||
- team: waha-patrons-pro
|
||||
label: patron:PRO
|
||||
comment: '[](https://waha.devlike.pro/docs/how-to/plus-version/#tiers)'
|
||||
- team: waha-patrons-advanced
|
||||
label: patron:ADVANCED
|
||||
comment: '[](https://waha.devlike.pro/docs/how-to/plus-version/#tiers)'
|
||||
- team: waha-patrons-plus
|
||||
label: patron:PLUS
|
||||
comment: '[](https://waha.devlike.pro/docs/how-to/plus-version/#tiers)'
|
||||
@@ -3,16 +3,47 @@ name: Release
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- "*"
|
||||
- '*'
|
||||
|
||||
jobs:
|
||||
deploy-docker:
|
||||
runs-on: ubuntu-latest
|
||||
docker-build:
|
||||
runs-on: ${{ matrix.runner }}
|
||||
name: ${{ matrix.platform }} - ${{ matrix.browser }} - ${{ matrix.tag }}
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
# Chromium
|
||||
- runner: 'buildjet-4vcpu-ubuntu-2204'
|
||||
tag: 'latest'
|
||||
platform: 'amd64'
|
||||
browser: 'chromium'
|
||||
- runner: 'buildjet-4vcpu-ubuntu-2204-arm'
|
||||
tag: 'arm'
|
||||
platform: 'linux/arm64'
|
||||
browser: 'chromium'
|
||||
# Chrome
|
||||
- runner: 'buildjet-4vcpu-ubuntu-2204'
|
||||
tag: 'chrome'
|
||||
platform: 'amd64'
|
||||
browser: 'chrome'
|
||||
# Chrome is not supported on arm64
|
||||
# - runner: "buildjet-4vcpu-ubuntu-2204-arm"
|
||||
# tag: "chrome-arm"
|
||||
# platform: "linux/arm64"
|
||||
# browser: "chrome"
|
||||
# No browser
|
||||
- runner: 'buildjet-4vcpu-ubuntu-2204'
|
||||
tag: 'noweb'
|
||||
platform: 'amd64'
|
||||
browser: 'none'
|
||||
- runner: 'buildjet-4vcpu-ubuntu-2204-arm'
|
||||
tag: 'noweb-arm'
|
||||
platform: 'linux/arm64'
|
||||
browser: 'none'
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
|
||||
@@ -20,10 +51,11 @@ jobs:
|
||||
id: meta
|
||||
uses: docker/metadata-action@v4
|
||||
with:
|
||||
images: allburov/whatsapp-http-api
|
||||
flavor: latest=true
|
||||
images: ${{ vars.DOCKER_IMAGE }}
|
||||
flavor: latest=false
|
||||
tags: |
|
||||
type=semver,pattern={{version}}
|
||||
type=raw,value=${{ matrix.tag }}
|
||||
type=raw,value=${{ matrix.tag }}-{{tag}}
|
||||
|
||||
- name: Login to image repository
|
||||
if: github.ref_type == 'tag'
|
||||
@@ -33,12 +65,13 @@ jobs:
|
||||
password: ${{ secrets.DOCKER_TOKEN }}
|
||||
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v3
|
||||
uses: docker/build-push-action@v4
|
||||
with:
|
||||
context: .
|
||||
file: Dockerfile
|
||||
platforms: ${{ matrix.platform }}
|
||||
build-args: |
|
||||
USE_BROWSER=${{ matrix.browser }}
|
||||
push: ${{ github.ref_type == 'tag' }}
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha
|
||||
@@ -0,0 +1,84 @@
|
||||
name: Manual push
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
tag:
|
||||
description: 'Tag to push'
|
||||
required: true
|
||||
default: 'dev'
|
||||
push:
|
||||
description: 'Push to registry'
|
||||
required: true
|
||||
type: boolean
|
||||
default: true
|
||||
|
||||
jobs:
|
||||
docker-build:
|
||||
runs-on: ${{ matrix.runner }}
|
||||
name: ${{ matrix.platform }} - ${{ matrix.browser }} - ${{ matrix.tag }}
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
# Chromium
|
||||
- runner: 'buildjet-4vcpu-ubuntu-2204'
|
||||
tag: 'latest'
|
||||
platform: 'amd64'
|
||||
browser: 'chromium'
|
||||
- runner: 'buildjet-4vcpu-ubuntu-2204-arm'
|
||||
tag: 'arm'
|
||||
platform: 'linux/arm64'
|
||||
browser: 'chromium'
|
||||
# Chrome
|
||||
- runner: 'buildjet-4vcpu-ubuntu-2204'
|
||||
tag: 'chrome'
|
||||
platform: 'amd64'
|
||||
browser: 'chrome'
|
||||
# Chrome is not supported on arm64
|
||||
# - runner: "buildjet-4vcpu-ubuntu-2204-arm"
|
||||
# tag: "chrome-arm"
|
||||
# platform: "linux/arm64"
|
||||
# browser: "chrome"
|
||||
# No browser
|
||||
- runner: 'buildjet-4vcpu-ubuntu-2204'
|
||||
tag: 'noweb'
|
||||
platform: 'amd64'
|
||||
browser: 'none'
|
||||
- runner: 'buildjet-4vcpu-ubuntu-2204-arm'
|
||||
tag: 'noweb-arm'
|
||||
platform: 'linux/arm64'
|
||||
browser: 'none'
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
|
||||
- name: Docker meta
|
||||
id: meta
|
||||
uses: docker/metadata-action@v4
|
||||
with:
|
||||
images: ${{ vars.DOCKER_IMAGE }}
|
||||
flavor: latest=false
|
||||
tags: |
|
||||
type=raw,value=${{ inputs.tag }}-${{ matrix.tag }}
|
||||
|
||||
- name: Login to image repository
|
||||
if: github.ref_type == 'tag'
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_USER }}
|
||||
password: ${{ secrets.DOCKER_TOKEN }}
|
||||
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v4
|
||||
with:
|
||||
context: .
|
||||
file: Dockerfile
|
||||
platforms: ${{ matrix.platform }}
|
||||
build-args: |
|
||||
USE_BROWSER=${{ matrix.browser }}
|
||||
push: ${{ inputs.push }}
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
+14
@@ -1,6 +1,20 @@
|
||||
*.heapsnapshot
|
||||
.yarn
|
||||
example.ts
|
||||
.*sessions
|
||||
.secrets
|
||||
.minio
|
||||
.media
|
||||
tokens
|
||||
files
|
||||
venv
|
||||
.wwebjs_auth
|
||||
src/test.ts
|
||||
src/test.js
|
||||
test.js
|
||||
.wwebjs_cache
|
||||
src/dashboard
|
||||
.eslintcache
|
||||
|
||||
# compiled output
|
||||
/dist
|
||||
|
||||
@@ -1,5 +1,74 @@
|
||||
exclude: |
|
||||
(?x)^(
|
||||
^src/dashboard/.*|
|
||||
^src/core/engines/webjs/.*html|
|
||||
^src/plus/engines/webjs/.*html
|
||||
)$
|
||||
|
||||
repos:
|
||||
- repo: local
|
||||
hooks:
|
||||
- id: commit-message-prefix
|
||||
name: Validate Commit Message for src/plus Changes
|
||||
entry: python ./.precommit/validate_commit_message.py
|
||||
language: python
|
||||
stages: [commit-msg]
|
||||
|
||||
- repo: local
|
||||
hooks:
|
||||
- id: lint
|
||||
stages: [pre-commit]
|
||||
name: lint
|
||||
language: system
|
||||
entry: bash -c '$HOME/.nvm/nvm-exec npm run lint-cache'
|
||||
pass_filenames: false
|
||||
files: |
|
||||
(?x)^(
|
||||
src/.*|
|
||||
)$
|
||||
|
||||
- repo: https://github.com/pre-commit/mirrors-prettier
|
||||
rev: 'v3.1.0'
|
||||
hooks:
|
||||
- id: prettier
|
||||
stages: [pre-commit]
|
||||
exclude: |
|
||||
(?x)^(
|
||||
docs/.*|
|
||||
README.md|
|
||||
)$
|
||||
- repo: https://github.com/Lucas-C/pre-commit-hooks-nodejs
|
||||
rev: v1.1.2
|
||||
hooks:
|
||||
- id: markdown-toc
|
||||
stages: [pre-commit]
|
||||
name: README.md
|
||||
files: ^README.md$
|
||||
- repo: local
|
||||
hooks:
|
||||
- id: no-plus-in-core
|
||||
stages: [pre-commit]
|
||||
name: No "plus" in core
|
||||
language: pygrep
|
||||
entry: 'plus'
|
||||
files: |
|
||||
(?x)^(
|
||||
src/api/.*|
|
||||
src/core/.*|
|
||||
src/structures/.*|
|
||||
src/config.service.ts|
|
||||
)$
|
||||
|
||||
- repo: local
|
||||
hooks:
|
||||
- id: no-console-log
|
||||
stages: [pre-commit]
|
||||
name: No console.log() calls
|
||||
language: pygrep
|
||||
entry: 'console\.log'
|
||||
exclude: |
|
||||
(?x)^(
|
||||
entrypoint.sh|
|
||||
.pre-commit-config.yaml|
|
||||
^src/core/engines/webjs/session.webjs.core.ts|
|
||||
)$
|
||||
@@ -0,0 +1,38 @@
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
|
||||
def get_staged_files():
|
||||
result = subprocess.run(["git", "diff", "--cached", "--name-only"], stdout=subprocess.PIPE, text=True)
|
||||
files = result.stdout.splitlines()
|
||||
return files
|
||||
|
||||
|
||||
def get_commit_message(commit_msg_filepath):
|
||||
with open(commit_msg_filepath, 'r') as f:
|
||||
return f.readline().strip()
|
||||
|
||||
|
||||
def main():
|
||||
commit_msg_filepath = sys.argv[1]
|
||||
commit_message = get_commit_message(commit_msg_filepath)
|
||||
staged_files = get_staged_files()
|
||||
|
||||
has_plus_changes = any(f.startswith('src/plus') for f in staged_files)
|
||||
print(commit_message)
|
||||
starts_with_plus = commit_message.startswith('[PLUS]')
|
||||
|
||||
if has_plus_changes and not starts_with_plus:
|
||||
print("'[PLUS]' not found in commit message, but there's changes are from 'src/plus'.")
|
||||
return 1
|
||||
|
||||
if starts_with_plus and not all(f.startswith('src/plus') for f in staged_files):
|
||||
print("'[PLUS]' found in commit message, but there's changes from other directories. \n"
|
||||
"Changes MUST be only from 'src/plus'.")
|
||||
return 1
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
+11
-2
@@ -1,4 +1,13 @@
|
||||
{
|
||||
"singleQuote": true,
|
||||
"trailingComma": "all"
|
||||
}
|
||||
"trailingComma": "all",
|
||||
"endOfLine": "auto",
|
||||
"arrowParens": "always",
|
||||
"bracketSpacing": true,
|
||||
"useTabs": false,
|
||||
"tabWidth": 2,
|
||||
"semi": true,
|
||||
"quoteProps": "as-needed",
|
||||
"proseWrap": "always",
|
||||
"printWidth": 80
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
nodeLinker: node-modules
|
||||
|
||||
yarnPath: .yarn/releases/yarn-3.6.3.cjs
|
||||
@@ -0,0 +1,4 @@
|
||||
# Changelog
|
||||
|
||||
You can find changelog on
|
||||
[the changelog page](https://waha.devlike.pro/docs/help/changelog/)
|
||||
+96
-18
@@ -1,24 +1,102 @@
|
||||
FROM node:15
|
||||
# Chrome
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y wget gnupg \
|
||||
&& wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add - \
|
||||
&& sh -c 'echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google.list' \
|
||||
&& apt-get update \
|
||||
&& apt-get install -y google-chrome-unstable fonts-ipafont-gothic fonts-wqy-zenhei fonts-thai-tlwg fonts-kacst fonts-freefont-ttf \
|
||||
--no-install-recommends \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
#
|
||||
# Build
|
||||
#
|
||||
ARG NODE_VERSION=22.8-bullseye
|
||||
FROM node:${NODE_VERSION} as build
|
||||
ENV PUPPETEER_SKIP_DOWNLOAD=True
|
||||
|
||||
# npm packages
|
||||
WORKDIR /app
|
||||
WORKDIR /src
|
||||
COPY package.json .
|
||||
COPY package-lock.json .
|
||||
RUN npm install
|
||||
COPY yarn.lock .
|
||||
ENV YARN_CHECKSUM_BEHAVIOR=update
|
||||
RUN npm install -g corepack && corepack enable
|
||||
RUN yarn set version 3.6.3
|
||||
RUN yarn install
|
||||
|
||||
# App
|
||||
WORKDIR /app
|
||||
ADD . /app
|
||||
RUN npm run build
|
||||
EXPOSE 3000
|
||||
WORKDIR /src
|
||||
ADD . /src
|
||||
RUN yarn install
|
||||
RUN yarn build && find ./dist -name "*.d.ts" -delete
|
||||
|
||||
CMD npm run start:prod
|
||||
#
|
||||
# Dashboard
|
||||
#
|
||||
FROM node:${NODE_VERSION} as dashboard
|
||||
|
||||
# Download WAHA Dashboard
|
||||
ENV WAHA_DASHBOARD_SHA 9afbd7b68618ea67be01bdefa978cf98a6336611
|
||||
RUN \
|
||||
wget https://github.com/devlikeapro/dashboard/archive/${WAHA_DASHBOARD_SHA}.zip \
|
||||
&& unzip ${WAHA_DASHBOARD_SHA}.zip -d /tmp/dashboard \
|
||||
&& mkdir -p /dashboard \
|
||||
&& mv /tmp/dashboard/dashboard-${WAHA_DASHBOARD_SHA}/* /dashboard/ \
|
||||
&& rm -rf ${WAHA_DASHBOARD_SHA}.zip \
|
||||
&& rm -rf /tmp/dashboard/dashboard-${WAHA_DASHBOARD_SHA}
|
||||
|
||||
#
|
||||
# Final
|
||||
#
|
||||
FROM node:${NODE_VERSION} as release
|
||||
ENV PUPPETEER_SKIP_DOWNLOAD=True
|
||||
# Quick fix for memory potential memory leaks
|
||||
# https://github.com/devlikeapro/waha/issues/347
|
||||
ENV NODE_OPTIONS="--max-old-space-size=16384"
|
||||
ARG USE_BROWSER=chromium
|
||||
|
||||
RUN echo "USE_BROWSER=$USE_BROWSER"
|
||||
|
||||
# Install ffmpeg to generate previews for videos
|
||||
RUN apt-get update && apt-get install -y ffmpeg --no-install-recommends && rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Install zip and unzip
|
||||
RUN apt-get update && apt-get install -y zip unzip --no-install-recommends && rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Install fonts if using either chromium or chrome
|
||||
RUN if [ "$USE_BROWSER" = "chromium" ] || [ "$USE_BROWSER" = "chrome" ]; then \
|
||||
apt-get update \
|
||||
&& apt-get install -y fonts-ipafont-gothic fonts-wqy-zenhei fonts-thai-tlwg fonts-kacst fonts-freefont-ttf \
|
||||
--no-install-recommends \
|
||||
&& rm -rf /var/lib/apt/lists/*; \
|
||||
fi
|
||||
|
||||
# Install Chromium
|
||||
RUN if [ "$USE_BROWSER" = "chromium" ]; then \
|
||||
apt-get update \
|
||||
&& apt-get update \
|
||||
&& apt-get install -y chromium \
|
||||
--no-install-recommends \
|
||||
&& rm -rf /var/lib/apt/lists/*; \
|
||||
fi
|
||||
|
||||
# Install Chrome
|
||||
# Available versions:
|
||||
# https://www.ubuntuupdates.org/package/google_chrome/stable/main/base/google-chrome-stable
|
||||
ARG CHROME_VERSION="126.0.6478.182-1"
|
||||
RUN if [ "$USE_BROWSER" = "chrome" ]; then \
|
||||
wget --no-verbose -O /tmp/chrome.deb https://dl.google.com/linux/chrome/deb/pool/main/g/google-chrome-stable/google-chrome-stable_${CHROME_VERSION}_amd64.deb \
|
||||
&& apt-get update \
|
||||
&& apt install -y /tmp/chrome.deb \
|
||||
&& rm /tmp/chrome.deb \
|
||||
&& rm -rf /var/lib/apt/lists/*; \
|
||||
fi
|
||||
|
||||
# Attach sources, install packages
|
||||
WORKDIR /app
|
||||
COPY package.json ./
|
||||
COPY --from=build /src/node_modules ./node_modules
|
||||
COPY --from=build /src/dist ./dist
|
||||
COPY --from=dashboard /dashboard ./dist/dashboard
|
||||
COPY entrypoint.sh /entrypoint.sh
|
||||
|
||||
# Chokidar options to monitor file changes
|
||||
ENV CHOKIDAR_USEPOLLING=1
|
||||
ENV CHOKIDAR_INTERVAL=5000
|
||||
|
||||
# WAHA variables
|
||||
ENV WAHA_ZIPPER=ZIPUNZIP
|
||||
|
||||
# Run command, etc
|
||||
EXPOSE 3000
|
||||
CMD ["/entrypoint.sh"]
|
||||
@@ -1,8 +1,41 @@
|
||||
build:
|
||||
docker build . -t allburov/whatsapp-http-api
|
||||
docker build . -t devlikeapro/waha
|
||||
|
||||
run:
|
||||
docker run --rm -v `pwd`/tokens:/app/tokens -p 127.0.0.1:3000:3000/tcp --name whatsapp-http-api allburov/whatsapp-http-api
|
||||
build-chrome:
|
||||
docker build . -t devlikeapro/waha-plus:chrome --build-arg USE_BROWSER=chrome
|
||||
|
||||
build-noweb:
|
||||
docker build . -t devlikeapro/waha:noweb --build-arg USE_BROWSER=none
|
||||
|
||||
build-all: build build-chrome build-noweb
|
||||
|
||||
build-plus:
|
||||
docker build . -t devlikeapro/waha-plus
|
||||
|
||||
build-ssh:
|
||||
# check IMAGE provided
|
||||
@[ "${IMAGE}" ] || ( echo "Add APP: make build-ssh image=devlikeapro/waha"; exit 1 );
|
||||
eval $(ssh-agent) && \
|
||||
ssh-add ~/.ssh/id_rsa && \
|
||||
docker buildx build --ssh default=${SSH_AUTH_SOCK} . -t ${IMAGE} --build-arg USE_BROWSER=none
|
||||
|
||||
stop:
|
||||
docker stop waha
|
||||
|
||||
clean: stop
|
||||
sudo rm -rf .sessions
|
||||
|
||||
push:
|
||||
docker push allburov/whatsapp-http-api
|
||||
docker push devlikeapro/waha
|
||||
|
||||
for-swagger:
|
||||
WHATSAPP_SWAGGER_CONFIG_ADVANCED=true . ${NVM_DIR}/nvm.sh && nvm exec yarn start
|
||||
|
||||
up-noweb:
|
||||
. ${NVM_DIR}/nvm.sh && nvm exec yarn up @adiwajshing/baileys@github:devlikeapro/Baileys#fork-master-channels-2
|
||||
|
||||
up-webjs:
|
||||
. ${NVM_DIR}/nvm.sh && nvm exec yarn up whatsapp-web.js@github:devlikeapro/whatsapp-web.js#fork-webpack-exodus-channels
|
||||
|
||||
start-proxy:
|
||||
docker run --rm -d --name squid-container -e TZ=UTC -p 3128:3128 ubuntu/squid:5.2-22.04_beta
|
||||
@@ -1,184 +1,197 @@
|
||||
# WhatsApp HTTP API Free
|
||||
# WAHA
|
||||
|
||||
WhatsApp HTTP API that you can configure in a click! It's really Free! :)
|
||||
<p align="center">
|
||||
<img src="./logo.png" style='border-radius: 50%' width='150'/>
|
||||
</p>
|
||||
|
||||
The project provides you HTTP API for Whatsapp that you can use to send and receive messages. You can
|
||||
go [through currently supported methods in Swagger](https://allburov.github.io/whatsapp-http-api/)
|
||||
**WAHA** - **W**hats**A**pp **H**TTP **A**PI (REST API) that you can install on your own server and run in less than 5 minutes!
|
||||
|
||||
The project is an HTTP API wrapper around https://github.com/orkestral/venom
|
||||
[](https://hub.docker.com/r/devlikeapro/waha)
|
||||
|
||||
- Documentation: [https://waha.devlike.pro/](https://waha.devlike.pro/)
|
||||
- Swagger Example: [https://waha.devlike.pro/swagger](https://waha.devlike.pro/swagger)
|
||||
|
||||
# Tables of Contents
|
||||
|
||||
<!-- toc -->
|
||||
|
||||
- [Installation](#installation)
|
||||
- [First Steps](#first-steps)
|
||||
* [Run WhatsAPP HTTP API](#run-whatsapp-http-api)
|
||||
* [Create a new session and login](#create-a-new-session-and-login)
|
||||
* [Send a text message](#send-a-text-message)
|
||||
- [Receive messages](#receive-messages)
|
||||
+ [Python echo server](#python-echo-server)
|
||||
- [Environment variables](#environment-variables)
|
||||
* [Common](#common)
|
||||
* [Webhooks](#webhooks)
|
||||
* [File storage](#file-storage)
|
||||
- [How to](#how-to)
|
||||
* [How to build the newest Docker image locally](#how-to-build-the-newest-docker-image-locally)
|
||||
- [Support](#support)
|
||||
- [Features](#features)
|
||||
* [Messages](#messages)
|
||||
* [Sessions](#sessions)
|
||||
* [Security](#security)
|
||||
* [Updates](#updates)
|
||||
* [Groups](#groups)
|
||||
* [Contacts](#contacts)
|
||||
- [Quick start](#quick-start)
|
||||
* [Requirements](#requirements)
|
||||
* [Send your first message](#send-your-first-message)
|
||||
+ [1. Download image](#1-download-image)
|
||||
+ [2. Run WhatsApp HTTP API](#2-run-whatsapp-http-api)
|
||||
+ [3. Start a new session](#3-start-a-new-session)
|
||||
+ [4. Get and scan QR](#4-get-and-scan-qr)
|
||||
+ [5. Get the screenshot](#5-get-the-screenshot)
|
||||
+ [6. Send a text message](#6-send-a-text-message)
|
||||
* [What is next?](#what-is-next)
|
||||
|
||||
<!-- tocstop -->
|
||||
|
||||
# Installation
|
||||
# Features
|
||||
|
||||
## Messages
|
||||
- Send messages
|
||||
- Receive messages
|
||||
- Message replies
|
||||
- Send location
|
||||
- Receive location
|
||||
- React to messages
|
||||
- Send media (images/documents/files)
|
||||
- Send voice messages
|
||||
- Receive media (images/audio/video/documents)
|
||||
- Receive messages - webhook retries
|
||||
|
||||
## Sessions
|
||||
- Multi Device
|
||||
- Get the screenshot
|
||||
- Session saving (don't have to scan QR on every restart)
|
||||
- Single WhatsApp account running inside one container
|
||||
- Multiple WhatsApp account running inside one container
|
||||
|
||||
## Security
|
||||
- Swagger panel authentication
|
||||
- API authentication
|
||||
|
||||
## Updates
|
||||
- Bug fixes and updates come as soon as they are implemented.
|
||||
|
||||
## Groups
|
||||
- Create a group
|
||||
- Get invite for group
|
||||
- Modify group info (subject, description)
|
||||
- Add group participants
|
||||
- Kick group participants
|
||||
- Promote/demote group participants
|
||||
|
||||
## Contacts
|
||||
- Mute/unmute chats
|
||||
- Block/unblock contacts
|
||||
- Get contact info
|
||||
- Get profile pictures
|
||||
- Get presence (online\offline\typing) status
|
||||
|
||||
# Quick start
|
||||
|
||||
## Requirements
|
||||
|
||||
Only thing that you must have - installed docker. Please follow the original
|
||||
instruction [how to install docker](https://docs.docker.com/get-docker/)
|
||||
instruction <a href="https://docs.docker.com/get-docker/" target="_blank" rel="noopener">how to install docker -></a>.
|
||||
|
||||
When you are ready - come back and follows the below steps to send the first text message to WhatsApp via HTTP API!
|
||||
|
||||
{{< details "Why Docker?" >}}
|
||||
Docker makes it easy to ship all-in-one solution with the runtime and dependencies. You don't have to worry about
|
||||
language-specific libraries or chrome installation.
|
||||
|
||||
Also Docker makes installation and update processes so simple, just one command!
|
||||
{{< /details >}}
|
||||
|
||||
## Send your first message
|
||||
|
||||
Let's go over steps that allow you to send your first text message via WhatsApp HTTP API!
|
||||
|
||||
### 1. Download image
|
||||
|
||||
Assuming you have installed [Docker](https://docs.docker.com/get-docker/), let's download the image.
|
||||
|
||||
|
||||
```bash
|
||||
docker pull allburov/whatsapp-http-api
|
||||
docker pull devlikeapro/waha
|
||||
```
|
||||
|
||||
# First Steps
|
||||
|
||||
We're going to go through basic steps and send a text message at the end!
|
||||
```bash
|
||||
docker login -u devlikeapro -p {KEY}
|
||||
docker pull devlikeapro/waha-plus
|
||||
docker logout
|
||||
```
|
||||
|
||||
## Run WhatsAPP HTTP API
|
||||
Read more about how to get `PASSWORD` for [Plus Version →]({{< relref "plus-version" >}})
|
||||
|
||||
### 2. Run WhatsApp HTTP API
|
||||
|
||||
Run WhatsApp HTTP API:
|
||||
|
||||
```bash
|
||||
docker run -it --rm -v `pwd`/tokens:/app/tokens -p 127.0.0.1:3000:3000/tcp --name whatsapp-http-api allburov/whatsapp-http-api
|
||||
docker run -it --rm -p 3000:3000/tcp --name waha devlikeapro/waha
|
||||
|
||||
# It prints logs and the last line must be
|
||||
# It prints logs and the last line must be
|
||||
# WhatsApp HTTP API is running on: http://[::1]:3000
|
||||
```
|
||||
|
||||
Open the link in your browser: http://localhost:3000/
|
||||
Open the link in your browser [http://localhost:3000/](http://localhost:3000/) and you'll see API documentation
|
||||
(Swagger).
|
||||
|
||||
Note: We don't recommend expose the API outside the world because it does not
|
||||
support ([yet](https://github.com/allburov/whatsapp-http-api/issues/4)) authorization!
|
||||
|
||||
## Create a new session and login
|
||||
### 3. Start a new session
|
||||
|
||||
1. To start a new session you should have your mobile phone with installed WhatsApp application close to you. Please go
|
||||
and read how what we'll need to a bit
|
||||
later: [How to log in - the instruction on WhatsApp site](https://faq.whatsapp.com/381777293328336/?helpref=hc_fnav)
|
||||
1. Open API documentation at http://localhost:3000/
|
||||
1. **Start a new session with a name** (you can use `default` for the start) - find `POST /api/session/start`, click
|
||||
on **Try it out**, then **Execute** a bit below.
|
||||

|
||||
1. **Scan QR Code** - find `GET /api/screenshot` and execute it, it'll show you QR code that you must scan with your
|
||||
device.
|
||||

|
||||
1. **Get a screenshot again** - it'll show you the screenshot of you Whatsapp instance. If you can get the actual
|
||||
screenshot - then you're ready to start sending messages!
|
||||

|
||||
To start a new session you should have your mobile phone with installed WhatsApp application close to you.
|
||||
|
||||
## Send a text message
|
||||
Please go and read how what we'll need to a bit
|
||||
later:
|
||||
<a href="https://faq.whatsapp.com/381777293328336/?helpref=hc_fnav" target="_blank">
|
||||
How to log in - the instruction on WhatsApp site
|
||||
</a>
|
||||
|
||||
Let's try to send a message - you can either find `POST /api/sendText` in swagger (http://localhost:3000/) or
|
||||
use `curl` or just open a link in a browser (change the phone
|
||||
before!) http://localhost:3000/api/sendText?phone=79776772457&text=Hello+from+WhatsApp+HTTP+API+Free!
|
||||
When your ready - find `POST /api/sessions`, click on **Try it out**, then **Execute** a bit below.
|
||||
|
||||
|
||||
The example payload:
|
||||
```json
|
||||
{
|
||||
"name": "default"
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
By using the request with `name` values you can start multiple session (WhatsApp accounts) inside the single docker container in Plus
|
||||
|
||||
|
||||
### 4. Get and scan QR
|
||||
|
||||
Find `GET /api/screenshot` and execute it, it shows you QR code.
|
||||
|
||||
|
||||
**Scan the QR with your cell phone's WhatsApp app.**
|
||||
|
||||
|
||||
### 5. Get the screenshot
|
||||
|
||||
Execute `GET /api/screenshot` after a few seconds after scanning the QR - it'll show you the screenshot of you Whatsapp
|
||||
instance. If you can get the actual screenshot - then you're ready to start sending messages!
|
||||
|
||||
|
||||
### 6. Send a text message
|
||||
|
||||
Let's send a text message - find `POST /api/sendText` in [swagger](http://localhost:3000/) and change `chatId` this
|
||||
way: use a phone international phone number without `+` symbol and add `@c.us` at the end.
|
||||
|
||||
For phone number `12132132131` the `chatId` is `12132132131@c.us`.
|
||||
|
||||
The example payload:
|
||||
```json
|
||||
{
|
||||
"chatId": "12132132130@c.us",
|
||||
"text": "Hi there!",
|
||||
"session": "default"
|
||||
}
|
||||
```
|
||||
|
||||
Also, you can use `curl` and send POST request like this:
|
||||
|
||||
```bash
|
||||
# Phone without +
|
||||
# Using GET
|
||||
curl "http://localhost:3000/api/sendText?phone=79776772457&text=Hello+from+WhatsApp+HTTP+API+Free!"
|
||||
|
||||
# Using POST
|
||||
export PHONE=79776772457
|
||||
curl -d "{\"chatId\": \"${PHONE}@c.us\", \"text\": \"Hello from WhatsApp HTTP API Free\" }" -H "Content-Type: application/json" -X POST http://localhost:3000/api/sendText
|
||||
export PHONE=12132132130
|
||||
curl -d "{\"chatId\": \"${PHONE}@c.us\", \"text\": \"Hello from WhatsApp HTTP API\" }" -H "Content-Type: application/json" -X POST http://localhost:3000/api/sendText
|
||||
```
|
||||
|
||||
# Receive messages
|
||||
|
||||
To show how to receive messages we'll create a simple "echo" server with two functions:
|
||||
|
||||
1. When we receive a text message - just send the text back
|
||||
2. When we receive a message with a file (an image, a voice message) - download it and send the path back
|
||||
|
||||
In order to send you messages we use **Webhooks** (look at them below) and configure them via environments variables. So
|
||||
what you need to
|
||||
create "echo" server is HTTP server that will receive JSON POST request and then call back WhatsApp HTTP API
|
||||
via `POST /api/sendText` endpoint with JSON body.
|
||||
|
||||
### Python echo server
|
||||
|
||||
We use Python. Feel free to create your favorite language example and contribute to the project!
|
||||
|
||||
Run "echo" server in one terminal and leave it working:
|
||||
|
||||
```bash
|
||||
# if you haven't already
|
||||
git clone https://github.com/allburov/whatsapp-http-api.git
|
||||
cd whatsapp-http-api
|
||||
python -mpip install -r examples/requirements.txt
|
||||
export FLASK_APP=examples/echo.py
|
||||
flask run
|
||||
```
|
||||
|
||||
Visit http://localhost:5000 and check that we are good to go further.
|
||||
|
||||
Let's start WhatsApp HTTP API and configure the "on message" webhook and point it on our "http://localhost:5000/message"
|
||||
endpoint:
|
||||
|
||||
```bash
|
||||
docker run -it -v `pwd`/tokens:/app/tokens --network=host -e WHATSAPP_HOOK_ONMESSAGE=http://localhost:5000/message allburov/whatsapp-http-api
|
||||
```
|
||||
|
||||
Now go ahead, open the second whatsapp and send to our WhatsApp HTTP API a text message! It must reply the same text.
|
||||
|
||||
If you try to send an image the "echo server" will send a path to the downloaded file.
|
||||
|
||||
# Environment variables
|
||||
|
||||
## Common
|
||||
|
||||
- `DEBUG` - show debug and verbose logs, set in any value
|
||||
- `WHATSAPP_API_PORT` - listen port for HTTP server (default: `3000`)
|
||||
- `WHATSAPP_API_HOSTNAME` - Hostname for HTTP server (default: `localhost`)
|
||||
- `WHATSAPP_START_SESSION` - start session with that name right after launching the app
|
||||
|
||||
## Webhooks
|
||||
|
||||
The description of [webhooks you can in Venom README.md, section Events](https://github.com/orkestral/venom#events)
|
||||
|
||||
All webhooks are disabled by default:
|
||||
|
||||
- `WHATSAPP_HOOK_ONANYMESSAGE=http://localhost/uri`
|
||||
- `WHATSAPP_HOOK_ONMESSAGE=http://localhost/uri`
|
||||
- `WHATSAPP_HOOK_ONSTATECHANGE=http://localhost/uri`
|
||||
- `WHATSAPP_HOOK_ONACK=http://localhost/uri`
|
||||
- `WHATSAPP_HOOK_ONADDEDTOGROUP=http://localhost/uri`
|
||||
|
||||
If you want to look at the payload of the webhooks - use the url for that `https://httpbin.org/post`.
|
||||
|
||||
```bash
|
||||
docker run -it -v `pwd`/tokens:/app/tokens --network=host -e WHATSAPP_HOOK_ONMESSAGE=https://httpbin.org/post allburov/whatsapp-http-api
|
||||
```
|
||||
|
||||
## File storage
|
||||
|
||||
- `WHATSAPP_FILES_FOLDER` - folder where will be stored files from chats (images, voice messages) (
|
||||
default: `/tmp/whatsapp-files`)
|
||||
- `WHATSAPP_FILES_MIMETYPES` - download only these mimetypes from messages (download all files be default). Mimetypes
|
||||
must be separated by a comma, without spaces: `audio,image/png,image/gif`. In order to choose type use prefix (
|
||||
like `audio,image`).
|
||||
- `WHATSAPP_FILES_LIFETIME`- to keep free space files will be removed after this time (default: `180`, in seconds)
|
||||
|
||||
# How to
|
||||
## How to build the newest Docker image locally
|
||||
|
||||
If the changes haven't been released yet from `main` branch - you can build your own version from the git repository
|
||||
```bash
|
||||
git clone https://github.com/allburov/whatsapp-http-api.git
|
||||
cd whatsapp-http-api
|
||||
docker build . -t allburov/whatsapp-http-api
|
||||
```
|
||||
|
||||
# Support
|
||||
|
||||
If you want to support the project - you can either:
|
||||
|
||||
1. Create a Pull Request for desired functionality
|
||||
2. Support [the project one time, every month or get access to telegram channel on Boosty](https://boosty.to/allburov)
|
||||
|
||||
## What is next?
|
||||
[Go and read the full documentation!](https://waha.devlike.pro/docs/overview/introduction/)
|
||||
@@ -0,0 +1,35 @@
|
||||
services:
|
||||
waha:
|
||||
restart: always
|
||||
container_name: waha
|
||||
# image: devlikeapro/waha:latest
|
||||
image: devlikeapro/waha-plus:latest
|
||||
ports:
|
||||
- '3000:3000/tcp'
|
||||
volumes:
|
||||
- './.sessions:/app/.sessions'
|
||||
- './.media:/app/.media'
|
||||
environment:
|
||||
- WHATSAPP_API_KEY=321
|
||||
- WAHA_DASHBOARD_USERNAME=waha
|
||||
- WAHA_DASHBOARD_PASSWORD=waha
|
||||
- WHATSAPP_DEFAULT_ENGINE=WEBJS
|
||||
- WHATSAPP_RESTART_ALL_SESSIONS=True
|
||||
- WAHA_PRINT_QR=False
|
||||
- WAHA_MEDIA_STORAGE=LOCAL
|
||||
- WHATSAPP_FILES_LIFETIME=0
|
||||
- WHATSAPP_FILES_FOLDER=/app/.media
|
||||
|
||||
n8n:
|
||||
image: docker.n8n.io/n8nio/n8n
|
||||
container_name: n8n
|
||||
restart: always
|
||||
environment:
|
||||
- WEBHOOK_URL=http://n8n:5678
|
||||
ports:
|
||||
- '5678:5678'
|
||||
volumes:
|
||||
- n8n_data:/home/node/.n8n
|
||||
|
||||
volumes:
|
||||
n8n_data: {}
|
||||
@@ -0,0 +1,114 @@
|
||||
services:
|
||||
waha:
|
||||
# Check available Docker images:
|
||||
# https://waha.devlike.pro/docs/how-to/engines/#docker-images
|
||||
image: devlikeapro/waha
|
||||
container_name: waha
|
||||
ports:
|
||||
- '3000:3000/tcp'
|
||||
environment:
|
||||
# Environment variables from https://waha.devlike.pro/docs/how-to/config/
|
||||
# https://docs.webhook.site/
|
||||
- WHATSAPP_HOOK_URL=https://webhook.site/11111111-1111-1111-1111-11111111
|
||||
- WHATSAPP_HOOK_EVENTS=message
|
||||
- WHATSAPP_DEFAULT_ENGINE=WEBJS
|
||||
restart: always
|
||||
|
||||
waha-plus:
|
||||
# Check available Docker images:
|
||||
# https://waha.devlike.pro/docs/how-to/engines/#docker-images
|
||||
image: devlikeapro/waha-plus:chrome
|
||||
container_name: waha-plus
|
||||
ports:
|
||||
- '3000:3000/tcp'
|
||||
volumes:
|
||||
- './.sessions:/app/.sessions'
|
||||
environment:
|
||||
# Environment variables from https://waha.devlike.pro/docs/how-to/config/
|
||||
# - WHATSAPP_RESTART_ALL_SESSIONS=True
|
||||
# - WHATSAPP_HOOK_URL=https://webhook.site/11111111-1111-1111-1111-11111111
|
||||
# - WHATSAPP_HOOK_EVENTS=message
|
||||
# - WHATSAPP_API_HOSTNAME=localhost
|
||||
- WHATSAPP_DEFAULT_ENGINE=WEBJS
|
||||
- WHATSAPP_API_KEY=321
|
||||
# Username and password for Swagger
|
||||
- WHATSAPP_SWAGGER_USERNAME=swagger
|
||||
- WHATSAPP_SWAGGER_PASSWORD=admin
|
||||
# Username and password for WAHA Dashboard
|
||||
# you can use the same username and password for both Swagger and Dashboard
|
||||
# Default - waha / waha
|
||||
- WAHA_DASHBOARD_USERNAME=waha
|
||||
- WAHA_DASHBOARD_PASSWORD=waha
|
||||
# Engine specific environment variables
|
||||
# - WAHA_WEBJS_WEB_VERSION=2.2412.54-videofix
|
||||
restart: always
|
||||
|
||||
waha-plus--mongodb:
|
||||
# Check available Docker images:
|
||||
# https://waha.devlike.pro/docs/how-to/engines/#docker-images
|
||||
image: devlikeapro/waha-plus:chrome
|
||||
container_name: waha-plus--mongodb
|
||||
ports:
|
||||
- '3000:3000/tcp'
|
||||
volumes:
|
||||
- './.sessions:/app/.sessions'
|
||||
environment:
|
||||
# Environment variables from https://waha.devlike.pro/docs/how-to/config/
|
||||
# - WHATSAPP_RESTART_ALL_SESSIONS=True
|
||||
# - WHATSAPP_HOOK_URL=https://webhook.site/11111111-1111-1111-1111-11111111
|
||||
# - WHATSAPP_HOOK_EVENTS=message
|
||||
# - WHATSAPP_API_KEY=321
|
||||
# - WHATSAPP_SWAGGER_USERNAME=admin
|
||||
# - WHATSAPP_SWAGGER_PASSWORD=123
|
||||
# - WAHA_DASHBOARD_USERNAME=admin
|
||||
# - WAHA_DASHBOARD_PASSWORD=123
|
||||
# - WHATSAPP_API_HOSTNAME=localhost
|
||||
- WHATSAPP_DEFAULT_ENGINE=WEBJS
|
||||
- WHATSAPP_SESSIONS_MONGO_URL=mongodb://mongodb:27017
|
||||
restart: always
|
||||
|
||||
# Let's Encrypt + Certbot
|
||||
# https://waha.devlike.pro/docs/how-to/security/#https
|
||||
waha-https--certbot:
|
||||
image: devlikeapro/waha-plus
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- '3000:3000'
|
||||
volumes:
|
||||
- './.sessions:/app/.sessions'
|
||||
- '/etc/letsencrypt:/etc/letsencrypt'
|
||||
environment:
|
||||
- DOMAIN=waha.example.pro
|
||||
- WHATSAPP_API_HOSTNAME=${DOMAIN}
|
||||
- WAHA_HTTPS_ENABLED=true
|
||||
- WAHA_HTTPS_PATH_KEY=/etc/letsencrypt/live/${DOMAIN}/privkey.pem
|
||||
- WAHA_HTTPS_PATH_CERT=/etc/letsencrypt/live/${DOMAIN}/cert.pem
|
||||
- WAHA_HTTPS_PATH_CA=/etc/letsencrypt/live/${DOMAIN}/chain.pem
|
||||
|
||||
# Self-signed certificates example
|
||||
# https://waha.devlike.pro/docs/how-to/security/#https
|
||||
waha-https--self-signed:
|
||||
image: devlikeapro/waha-plus
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- '3000:3000'
|
||||
volumes:
|
||||
- './.sessions:/app/.sessions'
|
||||
- './.secrets:/app/.secrets'
|
||||
environment:
|
||||
- WAHA_HTTPS_ENABLED=true
|
||||
- WAHA_HTTPS_PATH_KEY=.secrets/privkey.pem
|
||||
- WAHA_HTTPS_PATH_CERT=.secrets/cert.pem
|
||||
- WAHA_HTTPS_PATH_CA=.secrets/chain.pem
|
||||
|
||||
mongodb:
|
||||
image: mongo
|
||||
container_name: mongodb
|
||||
ports:
|
||||
- '27017:27017/tcp'
|
||||
volumes:
|
||||
- mongodb_data:/data/db
|
||||
restart: always
|
||||
|
||||
volumes:
|
||||
mongodb_data: {}
|
||||
+237
-9
@@ -1,15 +1,243 @@
|
||||
version: '3.4'
|
||||
#
|
||||
# https://waha.devlike.pro/docs/how-to/install/
|
||||
# Here's we provide a simple example of how to install WAHA using Docker Compose.
|
||||
#
|
||||
# We're assuming you have Docker and Docker Compose installed.
|
||||
# If you don't have Docker Compose installed, you can follow the official guide:
|
||||
# https://docs.docker.com/compose/install/
|
||||
#
|
||||
|
||||
services:
|
||||
whatsapp-http-api:
|
||||
build: .
|
||||
image: allburov/whatsapp-http-api
|
||||
waha:
|
||||
# Always restart the container on failure
|
||||
restart: always
|
||||
|
||||
#
|
||||
# Image
|
||||
#
|
||||
image: devlikeapro/waha-plus:latest # or devlikeapro/waha:latest if you're using WAHA Core
|
||||
#
|
||||
# We strongly recommend using the pinned version of WAHA
|
||||
# https://waha.devlike.pro/docs/how-to/engines/#versions
|
||||
# image: devlikeapro/waha-plus:latest-2024.7.6 # 'latest' is not mistaken here
|
||||
#
|
||||
# Multiple images are available
|
||||
# https://waha.devlike.pro/docs/how-to/engines/#docker-images
|
||||
# image: devlikeapro/waha-plus:chrome
|
||||
#
|
||||
|
||||
# The first port can be any free port you want
|
||||
# The second port must be the same as WHATSAPP_API_PORT or 3000 by default
|
||||
# Consider using the same port for simplicity
|
||||
ports:
|
||||
- '3000:3000/tcp'
|
||||
# NOTE: if you're using HTTP, you can use the following port mapping
|
||||
#- '80:3000/tcp'
|
||||
# NOTE: if you're using HTTPS, you can use the following port mapping
|
||||
#- '443:3000/tcp'
|
||||
|
||||
volumes:
|
||||
# Store sessions in the .sessions folder (comment it if you're using MongoDB)
|
||||
# Doesn't work with WAHA Core
|
||||
- './.sessions:/app/.sessions'
|
||||
|
||||
# https://waha.devlike.pro/docs/how-to/storages/#save-media-files-between-the-container-restarts
|
||||
- './.media:/app/.media'
|
||||
|
||||
# If you're using certbot for HTTPS, you can use the following volume mapping
|
||||
# https://waha.devlike.pro/blog/setting-up-https-for-waha/#lets-encrypt-certbot--waha
|
||||
# - '/etc/letsencrypt:/etc/letsencrypt'
|
||||
|
||||
# WAHA Configuration
|
||||
# https://waha.devlike.pro/docs/how-to/config/
|
||||
environment:
|
||||
#
|
||||
# Common
|
||||
#
|
||||
# If you know the right URL, you can use it here.
|
||||
# Use any port in WHATSAPP_API_PORT and domain in WHATSAPP_API_HOSTNAME
|
||||
# https://waha.devlike.pro/docs/how-to/config/#common
|
||||
#- WHATSAPP_API_SCHEMA=http
|
||||
#- WHATSAPP_API_PORT=3000
|
||||
#- WHATSAPP_API_HOSTNAME=localhost
|
||||
|
||||
#
|
||||
# Security
|
||||
#
|
||||
# https://waha.devlike.pro/docs/how-to/security/
|
||||
#
|
||||
- WHATSAPP_API_KEY=321
|
||||
- WAHA_DASHBOARD_USERNAME=waha
|
||||
- WAHA_DASHBOARD_PASSWORD=waha
|
||||
#- WHATSAPP_SWAGGER_USERNAME=admin
|
||||
#- WHATSAPP_SWAGGER_PASSWORD=admin
|
||||
|
||||
#
|
||||
# Dashboard
|
||||
#
|
||||
# https://waha.devlike.pro/docs/how-to/waha-dashboard/#configuration
|
||||
# Enabled by default
|
||||
#- WAHA_DASHBOARD_ENABLED=true
|
||||
#- WAHA_DASHBOARD_USERNAME=waha
|
||||
#- WAHA_DASHBOARD_PASSWORD=waha
|
||||
|
||||
#
|
||||
# Logging
|
||||
# https://waha.devlike.pro/docs/how-to/observability/#logging
|
||||
#
|
||||
# Set log format to JSON if you consume logs with log management systems
|
||||
- WAHA_LOG_FORMAT=JSON
|
||||
- WAHA_LOG_LEVEL=info
|
||||
# - WAHA_LOG_FORMAT=PRETTY
|
||||
# If you want to see more logs, you can set the log level to debug
|
||||
#- WAHA_LOG_LEVEL=debug
|
||||
# DEBUG=1 # shortcut for setting log level to debug
|
||||
|
||||
#
|
||||
# Engine
|
||||
#
|
||||
# Choose the right engine for your needs (WEBJS by default)
|
||||
# https://waha.devlike.pro/docs/how-to/engines/
|
||||
- WHATSAPP_DEFAULT_ENGINE=WEBJS
|
||||
# Use NOWEB engine for the fastest performance
|
||||
#- WHATSAPP_DEFAULT_ENGINE=NOWEB
|
||||
|
||||
#
|
||||
# Sessions
|
||||
#
|
||||
# https://waha.devlike.pro/docs/how-to/config/#sessions
|
||||
# Restart all sessions when container starts
|
||||
- WHATSAPP_RESTART_ALL_SESSIONS=True
|
||||
# Do not print QR codes in logs
|
||||
- WAHA_PRINT_QR=False
|
||||
#
|
||||
# OR you can specify sessions by name, if you have handful amount of sessions
|
||||
#- WHATSAPP_START_SESSION=session1,session2
|
||||
# Do not use both WHATSAPP_RESTART_ALL_SESSIONS and WHATSAPP_START_SESSION parameters
|
||||
#
|
||||
|
||||
#
|
||||
# Swagger
|
||||
#
|
||||
# https://waha.devlike.pro/docs/how-to/swagger/
|
||||
#- WHATSAPP_SWAGGER_USERNAME=admin
|
||||
#- WHATSAPP_SWAGGER_PASSWORD=admin
|
||||
# Disable Swagger
|
||||
#- WHATSAPP_SWAGGER_ENABLED=false
|
||||
# Enable advanced Swagger configuration
|
||||
#- WHATSAPP_SWAGGER_CONFIG_ADVANCED=true
|
||||
#
|
||||
# Swagger White Label customization
|
||||
# https://waha.devlike.pro/docs/how-to/swagger/#white-label
|
||||
#- WHATSAPP_SWAGGER_TITLE=WAHA API
|
||||
#
|
||||
|
||||
#
|
||||
# Global Proxy Settings
|
||||
# https://waha.devlike.pro/docs/how-to/config/#global-proxy-configuration
|
||||
# You can also set proxy settings for each session later when starting it
|
||||
# https://waha.devlike.pro/docs/how-to/sessions/#configure-proxy
|
||||
#- WHATSAPP_PROXY_SERVER=proxy.example.com:3128
|
||||
#-WHATSAPP_PROXY_SERVER_USERNAME=user
|
||||
#-WHATSAPP_PROXY_SERVER_PASSWORD=pass
|
||||
#
|
||||
|
||||
#
|
||||
# HTTPS Configuration
|
||||
#
|
||||
# https://waha.devlike.pro/docs/how-to/config/#https
|
||||
# Consider using certbot for HTTPS
|
||||
# https://waha.devlike.pro/blog/setting-up-https-for-waha/#lets-encrypt-certbot--waha
|
||||
#- WAHA_HTTPS_ENABLED=true
|
||||
#- WAHA_HTTPS_PATH_KEY=/etc/letsencrypt/live/waha.example.pro/privkey.pem
|
||||
#- WAHA_HTTPS_PATH_CERT=/etc/letsencrypt/live/waha.example.pro/cert.pem \
|
||||
#- WAHA_HTTPS_PATH_CA=/etc/letsencrypt/live/waha.example.pro/chain.pem
|
||||
#- WHATSAPP_API_SCHEMA=https
|
||||
#- WHATSAPP_API_PORT=3000
|
||||
#- WHATSAPP_API_HOSTNAME=waha.example.pro
|
||||
#
|
||||
|
||||
#
|
||||
# Media Configuration
|
||||
#
|
||||
# https://waha.devlike.pro/docs/how-to/config/#files
|
||||
#
|
||||
# Disable media (images, videos, files) download for incoming messages
|
||||
#- WHATSAPP_DOWNLOAD_MEDIA=false
|
||||
#
|
||||
# Download only specific media types
|
||||
#- WHATSAPP_FILES_MIMETYPES=image/jpeg,image/png
|
||||
#
|
||||
|
||||
# Media - Local Storage
|
||||
# https://waha.devlike.pro/docs/how-to/storages/#media---local
|
||||
#
|
||||
# Set to 0 to keep media files forever
|
||||
# https://waha.devlike.pro/docs/how-to/storages/#save-media-files-between-the-container-restarts
|
||||
- WAHA_MEDIA_STORAGE=LOCAL
|
||||
- WHATSAPP_FILES_LIFETIME=0
|
||||
- WHATSAPP_FILES_FOLDER=/app/.media
|
||||
# Keep media files for 180 seconds (3 minutes)
|
||||
#- WHATSAPP_FILES_LIFETIME=180
|
||||
#
|
||||
|
||||
# Media - S3 Storage
|
||||
# https://waha.devlike.pro/docs/how-to/storages/#media---s3
|
||||
#- WAHA_MEDIA_STORAGE=S3
|
||||
#- WAHA_S3_REGION=eu-west-2
|
||||
#- WAHA_S3_BUCKET=waha
|
||||
#- WAHA_S3_ACCESS_KEY_ID=minioadmin
|
||||
#- WAHA_S3_SECRET_ACCESS_KEY=minioadmin
|
||||
#- WAHA_S3_ENDPOINT=http://minio:9000 # Not required if you're using AWS S3
|
||||
#- WAHA_S3_FORCE_PATH_STYLE=True # Required for Minio
|
||||
#- WAHA_S3_PROXY_FILES=True # Required for docker-compose setup
|
||||
|
||||
#
|
||||
# Global Webhooks
|
||||
#
|
||||
# https://waha.devlike.pro/docs/how-to/config/#webhooks
|
||||
# https://waha.devlike.pro/docs/how-to/webhooks/
|
||||
#- WHATSAPP_HOOK_URL=https://webhook.site/11111111-1111-1111-1111-11111111
|
||||
#- WHATSAPP_HOOK_EVENTS=session.status,message,message.reaction
|
||||
#
|
||||
|
||||
#
|
||||
# MongoDB Configuration
|
||||
# https://waha.devlike.pro/docs/how-to/storages/#sessions---mongodb
|
||||
#- WHATSAPP_SESSIONS_MONGO_URL=mongodb://mongodb:27017
|
||||
#
|
||||
|
||||
# NOTE: Only if you're using MongoDB
|
||||
# https://waha.devlike.pro/docs/how-to/storages/#sessions---mongodb
|
||||
# Comment this block if you're using MongoDB
|
||||
mongodb:
|
||||
image: mongo
|
||||
container_name: mongodb
|
||||
ports:
|
||||
- '27017:27017/tcp'
|
||||
volumes:
|
||||
- mongodb_data:/data/db
|
||||
restart: always
|
||||
|
||||
# NOTE: Only if you're using S3 to save media files
|
||||
# https://waha.devlike.pro/docs/how-to/storages/#media---s3
|
||||
# Comment this block if you're using AWS S3
|
||||
minio:
|
||||
image: quay.io/minio/minio
|
||||
container_name: minio
|
||||
restart: always
|
||||
ports:
|
||||
- 3000:3000
|
||||
shm_size: "1gb"
|
||||
- '9000:9000'
|
||||
- '9001:9001'
|
||||
environment:
|
||||
- WHATSAPP_DOWNLOADS_FOLDER=/app/files/downloads
|
||||
MINIO_REGION: 'eu-west-2'
|
||||
MINIO_ROOT_USER: 'minioadmin'
|
||||
MINIO_ROOT_PASSWORD: 'minioadmin'
|
||||
volumes:
|
||||
- ./tokens:/app/tokens
|
||||
- ./files:/app/files
|
||||
- minio_data:/data
|
||||
command: server /data --console-address ":9001"
|
||||
|
||||
volumes:
|
||||
# NOTE: Only if you're using MongoDB
|
||||
mongodb_data: {}
|
||||
minio_data: {}
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 665 B |
Binary file not shown.
|
Before Width: | Height: | Size: 628 B |
Binary file not shown.
|
Before Width: | Height: | Size: 59 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 139 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 42 KiB |
@@ -1,57 +0,0 @@
|
||||
<!-- HTML for static distribution bundle build -->
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>allburov/whatsapp-http-api</title>
|
||||
<link href="./swagger-ui.css" rel="stylesheet" type="text/css">
|
||||
<link href="./favicon-32x32.png" rel="icon" sizes="32x32" type="image/png"/>
|
||||
<link href="./favicon-16x16.png" rel="icon" sizes="16x16" type="image/png"/>
|
||||
<style>
|
||||
html {
|
||||
box-sizing: border-box;
|
||||
overflow: -moz-scrollbars-vertical;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
*,
|
||||
*:before,
|
||||
*:after {
|
||||
box-sizing: inherit;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
background: #fafafa;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="swagger-ui"></div>
|
||||
|
||||
<script charset="UTF-8" src="./swagger-ui-bundle.js"></script>
|
||||
<script charset="UTF-8" src="./swagger-ui-standalone-preset.js"></script>
|
||||
<script>
|
||||
window.onload = function () {
|
||||
// Begin Swagger UI call region
|
||||
const ui = SwaggerUIBundle({
|
||||
url: "openapi.json",
|
||||
dom_id: '#swagger-ui',
|
||||
deepLinking: true,
|
||||
presets: [
|
||||
SwaggerUIBundle.presets.apis,
|
||||
SwaggerUIStandalonePreset
|
||||
],
|
||||
plugins: [
|
||||
SwaggerUIBundle.plugins.DownloadUrl
|
||||
],
|
||||
layout: "StandaloneLayout"
|
||||
})
|
||||
// End Swagger UI call region
|
||||
|
||||
window.ui = ui
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,77 +0,0 @@
|
||||
<!doctype html>
|
||||
<html lang="en-US">
|
||||
<head>
|
||||
<title>Swagger UI: OAuth2 Redirect</title>
|
||||
</head>
|
||||
<body>
|
||||
</body>
|
||||
</html>
|
||||
<script>
|
||||
'use strict';
|
||||
|
||||
function run() {
|
||||
var oauth2 = window.opener.swaggerUIRedirectOauth2;
|
||||
var sentState = oauth2.state;
|
||||
var redirectUrl = oauth2.redirectUrl;
|
||||
var isValid, qp, arr;
|
||||
|
||||
if (/code|token|error/.test(window.location.hash)) {
|
||||
qp = window.location.hash.substring(1);
|
||||
} else {
|
||||
qp = location.search.substring(1);
|
||||
}
|
||||
|
||||
arr = qp.split("&")
|
||||
arr.forEach(function (v, i, _arr) {
|
||||
_arr[i] = '"' + v.replace('=', '":"') + '"';
|
||||
})
|
||||
qp = qp ? JSON.parse('{' + arr.join() + '}',
|
||||
function (key, value) {
|
||||
return key === "" ? value : decodeURIComponent(value)
|
||||
}
|
||||
) : {}
|
||||
|
||||
isValid = qp.state === sentState
|
||||
|
||||
if ((
|
||||
oauth2.auth.schema.get("flow") === "accessCode" ||
|
||||
oauth2.auth.schema.get("flow") === "authorizationCode"
|
||||
) && !oauth2.auth.code) {
|
||||
if (!isValid) {
|
||||
oauth2.errCb({
|
||||
authId: oauth2.auth.name,
|
||||
source: "auth",
|
||||
level: "warning",
|
||||
message: "Authorization may be unsafe, passed state was changed in server Passed state wasn't returned from auth server"
|
||||
});
|
||||
}
|
||||
|
||||
if (qp.code) {
|
||||
delete oauth2.state;
|
||||
oauth2.auth.code = qp.code;
|
||||
oauth2.callback({auth: oauth2.auth, redirectUrl: redirectUrl});
|
||||
} else {
|
||||
let oauthErrorMsg
|
||||
if (qp.error) {
|
||||
oauthErrorMsg = "[" + qp.error + "]: " +
|
||||
(qp.error_description ? qp.error_description + ". " : "no accessCode received from the server. ") +
|
||||
(qp.error_uri ? "More info: " + qp.error_uri : "");
|
||||
}
|
||||
|
||||
oauth2.errCb({
|
||||
authId: oauth2.auth.name,
|
||||
source: "auth",
|
||||
level: "error",
|
||||
message: oauthErrorMsg || "[Authorization failed]: no accessCode received from the server"
|
||||
});
|
||||
}
|
||||
} else {
|
||||
oauth2.callback({auth: oauth2.auth, token: qp, isValid: isValid, redirectUrl: redirectUrl});
|
||||
}
|
||||
window.close();
|
||||
}
|
||||
|
||||
window.addEventListener('DOMContentLoaded', function () {
|
||||
run();
|
||||
});
|
||||
</script>
|
||||
@@ -1,908 +0,0 @@
|
||||
{
|
||||
"openapi": "3.0.0",
|
||||
"paths": {
|
||||
"/api/sessions/start": {
|
||||
"post": {
|
||||
"operationId": "SessionsController_start",
|
||||
"parameters": [],
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/SessionRequest"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
"201": {
|
||||
"description": ""
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
"sessions"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/api/sessions/stop": {
|
||||
"post": {
|
||||
"operationId": "SessionsController_stop",
|
||||
"parameters": [],
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/SessionRequest"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
"201": {
|
||||
"description": ""
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
"sessions"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/api/sessions": {
|
||||
"get": {
|
||||
"operationId": "SessionsController_list",
|
||||
"parameters": [],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": ""
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
"sessions"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/api/sendContactVcard": {
|
||||
"post": {
|
||||
"operationId": "ChattingController_sendContactVcard",
|
||||
"parameters": [],
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/MessageContactVcard"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
"201": {
|
||||
"description": "",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
"chatting"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/api/sendText": {
|
||||
"get": {
|
||||
"operationId": "ChattingController_sendTextGet",
|
||||
"summary": "Send a text message",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "phone",
|
||||
"required": true,
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "text",
|
||||
"required": true,
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "sessionName",
|
||||
"required": true,
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"default": "default",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
"chatting"
|
||||
]
|
||||
},
|
||||
"post": {
|
||||
"operationId": "ChattingController_sendText",
|
||||
"summary": "Send a text message",
|
||||
"parameters": [],
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/MessageText"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
"201": {
|
||||
"description": "",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
"chatting"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/api/sendLocation": {
|
||||
"post": {
|
||||
"operationId": "ChattingController_sendLocation",
|
||||
"parameters": [],
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/MessageLocation"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
"201": {
|
||||
"description": "",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
"chatting"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/api/sendLinkPreview": {
|
||||
"post": {
|
||||
"operationId": "ChattingController_sendLinkPreview",
|
||||
"parameters": [],
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/MessageLinkPreview"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
"201": {
|
||||
"description": "",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
"chatting"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/api/sendImage": {
|
||||
"post": {
|
||||
"operationId": "ChattingController_sendImage",
|
||||
"summary": "NOT IMPLEMENTED YET",
|
||||
"parameters": [],
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/MessageImage"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
"201": {
|
||||
"description": "",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
"chatting"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/api/sendFile": {
|
||||
"post": {
|
||||
"operationId": "ChattingController_sendFile",
|
||||
"summary": "NOT IMPLEMENTED YET",
|
||||
"parameters": [],
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/MessageFile"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
"201": {
|
||||
"description": "",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
"chatting"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/api/reply": {
|
||||
"post": {
|
||||
"operationId": "ChattingController_reply",
|
||||
"summary": "Reply to a text message",
|
||||
"parameters": [],
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/MessageReply"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
"201": {
|
||||
"description": "",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
"chatting"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/api/sendSeen": {
|
||||
"post": {
|
||||
"operationId": "ChattingController_sendSeen",
|
||||
"parameters": [],
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ChatRequest"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
"201": {
|
||||
"description": ""
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
"chatting"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/api/startTyping": {
|
||||
"post": {
|
||||
"operationId": "ChattingController_startTyping",
|
||||
"parameters": [],
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ChatRequest"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
"201": {
|
||||
"description": "",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
"chatting"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/api/stopTyping": {
|
||||
"post": {
|
||||
"operationId": "ChattingController_stopTyping",
|
||||
"parameters": [],
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ChatRequest"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
"201": {
|
||||
"description": "",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
"chatting"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/api/killServiceWorker": {
|
||||
"post": {
|
||||
"operationId": "DeviceController_killServiceWorker",
|
||||
"parameters": [],
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/SessionRequest"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
"201": {
|
||||
"description": "",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
"device"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/api/restartService": {
|
||||
"post": {
|
||||
"operationId": "DeviceController_restartService",
|
||||
"parameters": [],
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/SessionRequest"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
"201": {
|
||||
"description": "",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
"device"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/api/getHostDevice": {
|
||||
"get": {
|
||||
"operationId": "DeviceController_getHostDevice",
|
||||
"parameters": [],
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/SessionRequest"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
"device"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/api/getConnectionState": {
|
||||
"get": {
|
||||
"operationId": "DeviceController_getConnectionState",
|
||||
"parameters": [],
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/SessionRequest"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": ""
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
"device"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/api/getBatteryLevel": {
|
||||
"get": {
|
||||
"operationId": "DeviceController_getBatteryLevel",
|
||||
"parameters": [],
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/SessionRequest"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "number"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
"device"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/api/isConnected": {
|
||||
"get": {
|
||||
"operationId": "DeviceController_isConnected",
|
||||
"parameters": [],
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/SessionRequest"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
"device"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/api/getWAVersion": {
|
||||
"get": {
|
||||
"operationId": "DeviceController_getWAVersion",
|
||||
"parameters": [],
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/SessionRequest"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
"device"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/api/screenshot": {
|
||||
"get": {
|
||||
"operationId": "ScreenshotController_screenshot",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "sessionName",
|
||||
"required": true,
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"default": "default",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": ""
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
"screenshot"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"info": {
|
||||
"title": "WhatsApp HTTP API",
|
||||
"description": "WhatsApp HTTP API that you can configure in a click!",
|
||||
"version": "1.0",
|
||||
"contact": {}
|
||||
},
|
||||
"tags": [
|
||||
{
|
||||
"name": "sessions",
|
||||
"description": "Control your WhatsApp sessions"
|
||||
},
|
||||
{
|
||||
"name": "screenshot",
|
||||
"description": "Get screenshot of WhatsApp and show QR code"
|
||||
},
|
||||
{
|
||||
"name": "chatting",
|
||||
"description": "Chat methods"
|
||||
},
|
||||
{
|
||||
"name": "device",
|
||||
"description": "Device information"
|
||||
}
|
||||
],
|
||||
"servers": [],
|
||||
"components": {
|
||||
"securitySchemes": {
|
||||
"api_key": {
|
||||
"type": "apiKey",
|
||||
"in": "header",
|
||||
"name": "X-VENOM-TOKEN",
|
||||
"description": "Your secret key"
|
||||
}
|
||||
},
|
||||
"schemas": {
|
||||
"SessionRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"sessionName": {
|
||||
"type": "string",
|
||||
"default": "default"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"sessionName"
|
||||
]
|
||||
},
|
||||
"MessageContactVcard": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"sessionName": {
|
||||
"type": "string",
|
||||
"default": "default"
|
||||
},
|
||||
"chatId": {
|
||||
"type": "string",
|
||||
"example": "791231234567@c.us"
|
||||
},
|
||||
"contactsId": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"sessionName",
|
||||
"chatId",
|
||||
"contactsId",
|
||||
"name"
|
||||
]
|
||||
},
|
||||
"MessageText": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"sessionName": {
|
||||
"type": "string",
|
||||
"default": "default"
|
||||
},
|
||||
"chatId": {
|
||||
"type": "string",
|
||||
"example": "791231234567@c.us"
|
||||
},
|
||||
"text": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"sessionName",
|
||||
"chatId",
|
||||
"text"
|
||||
]
|
||||
},
|
||||
"MessageLocation": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"sessionName": {
|
||||
"type": "string",
|
||||
"default": "default"
|
||||
},
|
||||
"chatId": {
|
||||
"type": "string",
|
||||
"example": "791231234567@c.us"
|
||||
},
|
||||
"latitude": {
|
||||
"type": "string"
|
||||
},
|
||||
"longitude": {
|
||||
"type": "string"
|
||||
},
|
||||
"title": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"sessionName",
|
||||
"chatId",
|
||||
"latitude",
|
||||
"longitude",
|
||||
"title"
|
||||
]
|
||||
},
|
||||
"MessageLinkPreview": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"sessionName": {
|
||||
"type": "string",
|
||||
"default": "default"
|
||||
},
|
||||
"chatId": {
|
||||
"type": "string",
|
||||
"example": "791231234567@c.us"
|
||||
},
|
||||
"url": {
|
||||
"type": "string"
|
||||
},
|
||||
"title": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"sessionName",
|
||||
"chatId",
|
||||
"url",
|
||||
"title"
|
||||
]
|
||||
},
|
||||
"MessageImage": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"sessionName": {
|
||||
"type": "string",
|
||||
"default": "default"
|
||||
},
|
||||
"chatId": {
|
||||
"type": "string",
|
||||
"example": "791231234567@c.us"
|
||||
},
|
||||
"path": {
|
||||
"type": "string"
|
||||
},
|
||||
"filename": {
|
||||
"type": "string"
|
||||
},
|
||||
"caption": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"sessionName",
|
||||
"chatId",
|
||||
"path",
|
||||
"filename",
|
||||
"caption"
|
||||
]
|
||||
},
|
||||
"MessageFile": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"sessionName": {
|
||||
"type": "string",
|
||||
"default": "default"
|
||||
},
|
||||
"chatId": {
|
||||
"type": "string",
|
||||
"example": "791231234567@c.us"
|
||||
},
|
||||
"path": {
|
||||
"type": "string"
|
||||
},
|
||||
"filename": {
|
||||
"type": "string"
|
||||
},
|
||||
"caption": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"sessionName",
|
||||
"chatId",
|
||||
"path",
|
||||
"filename",
|
||||
"caption"
|
||||
]
|
||||
},
|
||||
"MessageReply": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"sessionName": {
|
||||
"type": "string",
|
||||
"default": "default"
|
||||
},
|
||||
"chatId": {
|
||||
"type": "string",
|
||||
"example": "791231234567@c.us"
|
||||
},
|
||||
"reply_to": {
|
||||
"type": "string",
|
||||
"example": "message.id"
|
||||
},
|
||||
"text": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"sessionName",
|
||||
"chatId",
|
||||
"reply_to",
|
||||
"text"
|
||||
]
|
||||
},
|
||||
"ChatRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"sessionName": {
|
||||
"type": "string",
|
||||
"default": "default"
|
||||
},
|
||||
"chatId": {
|
||||
"type": "string",
|
||||
"example": "791231234567@c.us"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"sessionName",
|
||||
"chatId"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"externalDocs": {
|
||||
"description": "Github WhatsApp HTTP API",
|
||||
"url": "https://github.com/allburov/whatsapp-http-api"
|
||||
}
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
/*! For license information please see swagger-ui-bundle.js.LICENSE.txt */
|
||||
!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t(function(){try{return require("esprima")}catch(e){}}()):"function"==typeof define&&define.amd?define(["esprima"],t):"object"==typeof exports?exports.SwaggerUIBundle=t(function(){try{return require("esprima")}catch(e){}}()):e.SwaggerUIBundle=t(e.esprima)}(this,(function(e){return function(e){var t={};function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}return n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)n.d(r,o,function(t){return e[t]}.bind(null,o));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="/dist",n(n.s=544)}([function(e,t,n){"use strict";e.exports=n(129)},function(e,t,n){e.exports=function(){"use strict";var e=Array.prototype.slice;function t(e,t){t&&(e.prototype=Object.create(t.prototype)),e.prototype.constructor=e}function n(e){return i(e)?e:J(e)}function r(e){return s(e)?e:K(e)}function o(e){return u(e)?e:Y(e)}function a(e){return i(e)&&!c(e)?e:G(e)}function i(e){return!(!e||!e[p])}function s(e){return!(!e||!e[f])}function u(e){return!(!e||!e[h])}function c(e){return s(e)||u(e)}function l(e){return!(!e||!e[d])}t(r,n),t(o,n),t(a,n),n.isIterable=i,n.isKeyed=s,n.isIndexed=u,n.isAssociative=c,n.isOrdered=l,n.Keyed=r,n.Indexed=o,n.Set=a;var p="@@__IMMUTABLE_ITERABLE__@@",f="@@__IMMUTABLE_KEYED__@@",h="@@__IMMUTABLE_INDEXED__@@",d="@@__IMMUTABLE_ORDERED__@@",m="delete",v=5,g=1<<v,y=g-1,b={},_={value:!1},w={value:!1};function x(e){return e.value=!1,e}function E(e){e&&(e.value=!0)}function S(){}function C(e,t){t=t||0;for(var n=Math.max(0,e.length-t),r=new Array(n),o=0;o<n;o++)r[o]=e[o+t];return r}function A(e){return void 0===e.size&&(e.size=e.__iterate(O)),e.size}function k(e,t){if("number"!=typeof t){var n=t>>>0;if(""+n!==t||4294967295===n)return NaN;t=n}return t<0?A(e)+t:t}function O(){return!0}function j(e,t,n){return(0===e||void 0!==n&&e<=-n)&&(void 0===t||void 0!==n&&t>=n)}function T(e,t){return N(e,t,0)}function I(e,t){return N(e,t,t)}function N(e,t,n){return void 0===e?n:e<0?Math.max(0,t+e):void 0===t?e:Math.min(t,e)}var P=0,M=1,R=2,D="function"==typeof Symbol&&Symbol.iterator,L="@@iterator",B=D||L;function F(e){this.next=e}function U(e,t,n,r){var o=0===e?t:1===e?n:[t,n];return r?r.value=o:r={value:o,done:!1},r}function q(){return{value:void 0,done:!0}}function z(e){return!!H(e)}function V(e){return e&&"function"==typeof e.next}function W(e){var t=H(e);return t&&t.call(e)}function H(e){var t=e&&(D&&e[D]||e[L]);if("function"==typeof t)return t}function $(e){return e&&"number"==typeof e.length}function J(e){return null==e?ie():i(e)?e.toSeq():ce(e)}function K(e){return null==e?ie().toKeyedSeq():i(e)?s(e)?e.toSeq():e.fromEntrySeq():se(e)}function Y(e){return null==e?ie():i(e)?s(e)?e.entrySeq():e.toIndexedSeq():ue(e)}function G(e){return(null==e?ie():i(e)?s(e)?e.entrySeq():e:ue(e)).toSetSeq()}F.prototype.toString=function(){return"[Iterator]"},F.KEYS=P,F.VALUES=M,F.ENTRIES=R,F.prototype.inspect=F.prototype.toSource=function(){return this.toString()},F.prototype[B]=function(){return this},t(J,n),J.of=function(){return J(arguments)},J.prototype.toSeq=function(){return this},J.prototype.toString=function(){return this.__toString("Seq {","}")},J.prototype.cacheResult=function(){return!this._cache&&this.__iterateUncached&&(this._cache=this.entrySeq().toArray(),this.size=this._cache.length),this},J.prototype.__iterate=function(e,t){return pe(this,e,t,!0)},J.prototype.__iterator=function(e,t){return fe(this,e,t,!0)},t(K,J),K.prototype.toKeyedSeq=function(){return this},t(Y,J),Y.of=function(){return Y(arguments)},Y.prototype.toIndexedSeq=function(){return this},Y.prototype.toString=function(){return this.__toString("Seq [","]")},Y.prototype.__iterate=function(e,t){return pe(this,e,t,!1)},Y.prototype.__iterator=function(e,t){return fe(this,e,t,!1)},t(G,J),G.of=function(){return G(arguments)},G.prototype.toSetSeq=function(){return this},J.isSeq=ae,J.Keyed=K,J.Set=G,J.Indexed=Y;var Z,X,Q,ee="@@__IMMUTABLE_SEQ__@@";function te(e){this._array=e,this.size=e.length}function ne(e){var t=Object.keys(e);this._object=e,this._keys=t,this.size=t.length}function re(e){this._iterable=e,this.size=e.length||e.size}function oe(e){this._iterator=e,this._iteratorCache=[]}function ae(e){return!(!e||!e[ee])}function ie(){return Z||(Z=new te([]))}function se(e){var t=Array.isArray(Line truncated
|
||||
//# sourceMappingURL=swagger-ui-bundle.js.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"sources":["webpack://SwaggerUIBundle/webpack/universalModuleDefinition","webpack://SwaggerUIBundle/webpack/bootstrap","webpack://SwaggerUIBundle/./node_modules/react/react.js","webpack://SwaggerUIBundle/./node_modules/immutable/dist/immutable.js","webpack://SwaggerUIBundle/./node_modules/@babel/runtime-corejs3/core-js-stable/instance/concat.js","webpack://SwaggerUIBundle/./node_modules/@babel/runtime-corejs3/helpers/defineProperty.js","webpack://SwaggerUIBundle/./node_modules/@babel/runtime-corejs3/core-js-stable/instance/map.js","webpack://SwaggerUIBundle/./node_modules/@babel/runtime-corejs3/helpers/createClass.js","webpack://SwaggerUIBundle/./node_modules/@babel/runtime-corejs3/helpers/classCallCheck.js","webpack://SwaggerUIBundle/./src/core/utils.js","webpack://SwaggerUIBundle/./node_modules/@babel/runtime-corejs3/helpers/inherits.js","webpack://SwaggerUIBundle/./node_modules/@babel/runtime-corejs3/helpers/createSuper.js","webpack://SwaggerUIBundle/./node_modules/@babel/runtime-corejs3/helpers/assertThisInitialized.js","webpack://SwaggerUIBundle/./node_modules/prop-types/index.js","webpack://SwaggerUIBundle/./node_modules/@babel/runtime-corejs3/core-js-stable/instance/filter.js","webpack://SwaggerUIBundle/./node_modules/@babel/runtime-corejs3/core-js-stable/instance/index-of.js","webpack://SwaggerUIBundle/./node_modules/@babel/runtime-corejs3/core-js-stable/array/is-array.js","webpack://SwaggerUIBundle/./node_modules/@babel/runtime-corejs3/helpers/slicedToArray.js","webpack://SwaggerUIBundle/./node_modules/@babel/runtime-corejs3/core-js-stable/object/keys.js","webpack://SwaggerUIBundle/./node_modules/@babel/runtime-corejs3/helpers/toConsumableArray.js","webpack://SwaggerUIBundle/./node_modules/@babel/runtime-corejs3/core-js-stable/instance/for-each.js","webpack://SwaggerUIBundle/./node_modules/reselect/es/index.js","webpack://SwaggerUIBundle/./node_modules/@babel/runtime-corejs3/helpers/typeof.js","webpack://SwaggerUIBundle/./node_modules/@babel/runtime-corejs3/core-js-stable/object/assign.js","webpack://SwaggerUIBundle/./node_modules/@babel/runtime-corejs3/core-js-stable/instance/reduce.js","webpack://SwaggerUIBundle/./node_modules/core-js-pure/internals/export.js","webpack://SwaggerUIBundle/./node_modules/@babel/runtime-corejs3/core-js-stable/instance/slice.js","webpack://SwaggerUIBundle/./node_modules/@babel/runtime-corejs3/helpers/objectSpread2.js","webpack://SwaggerUIBundle/./node_modules/fbjs/lib/invariant.js","webpack://SwaggerUIBundle/./src/core/window.js","webpack://SwaggerUIBundle/./node_modules/react-immutable-proptypes/dist/ImmutablePropTypes.js","webpack://SwaggerUIBundle/./node_modules/@babel/runtime-corejs3/helpers/extends.js","webpack://SwaggerUIBundle/./node_modules/@babel/runtime-corejs3/core-js-stable/instance/bind.js","webpack://SwaggerUIBundle/./node_modules/react-dom/lib/reactProdInvariant.js","webpack://SwaggerUIBundle/./node_modules/@babel/runtime-corejs3/core-js-stable/json/stringify.js","webpack://SwaggerUIBundle/./node_modules/core-js-pure/internals/path.js","webpack://SwaggerUIBundle/./node_modules/fbjs/lib/warning.js","webpack://SwaggerUIBundle/./node_modules/@babel/runtime-corejs3/core-js-stable/instance/some.js","webpack://SwaggerUIBundle/./node_modules/core-js-pure/internals/fails.js","webpack://SwaggerUIBundle/./node_modules/@babel/runtime-corejs3/core-js-stable/instance/includes.js","webpack://SwaggerUIBundle/./src/core/plugins/oas3/helpers.jsx","webpack://SwaggerUIBundle/./node_modules/core-js-pure/internals/well-known-symbol.js","webpack://SwaggerUIBundle/./node_modules/object-assign/index.js","webpack://SwaggerUIBundle/./node_modules/@babel/runtime-corejs3/regenerator/index.js","webpack://SwaggerUIBundle/./node_modules/core-js-pure/internals/global.js","webpack://SwaggerUIBundle/./node_modules/core-js-pure/internals/entry-virtual.js","webpack://SwaggerUIBundle/./node_modules/react-dom/lib/ReactDOMComponentTree.js","webpack://SwaggerUIBundle/./node_modules/core-js-pure/internals/is-object.js","webpack://SwaggerUIBundle/./node_modules/lodash/get.js","webpack://SwaggerUIBundle/./src/core/plugins/spec/actions.js","webpack://SwaggerUIBundle/./node_modules/core-js-pure/internals/descriptors.js","webpack://SwaggerUIBundle/./node_modules/core-js-pure/internals/define-well-known-symbol.js","webpack://SwaggerUIBundle/./node_modules/js-yaml/lib/js-yaml/type.js","webpack://SwaggerUIBundle/./node_modules/@babel/runtime-corejs3/helpers/createForOfIteratorHelper.js","webpack://SwaggerUIBundle/./node_modules/core-js-pure/internals/has.js","webpack://SwaggerUIBundle/./node_modules/lodash/isArray.js","webpack://SwaggerUIBundle/(webpack)/buildin/global.js","webpack://SwaggerUIBundle/./node_modules/core-js-pure/internals/an-object.js","webpack://SwaggerUIBundle/./node_modules/@babel/runtime-corejs3/helpers/objectWithoutProperties.js","webpack://SwaggerUIBundle/./node_modules/fbjs/lib/ExecutionEnvironment.js","webpack://SwaggerUIBundle/./node_modules/@babel/runtime-corejs3Line truncated
|
||||
@@ -1,3 +0,0 @@
|
||||
/*! For license information please see swagger-ui-es-bundle-core.js.LICENSE.txt */
|
||||
module.exports=function(e){var t={};function n(r){if(t[r])return t[r].exports;var a=t[r]={i:r,l:!1,exports:{}};return e[r].call(a.exports,a,a.exports,n),a.l=!0,a.exports}return n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var a in e)n.d(r,a,function(t){return e[t]}.bind(null,a));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="/dist",n(n.s=410)}([function(e,t){e.exports=require("react")},function(e,t){e.exports=require("immutable")},function(e,t,n){e.exports=n(449)},function(e,t,n){var r=n(191);e.exports=function(e,t,n){return t in e?r(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}},function(e,t,n){e.exports=n(452)},function(e,t,n){var r=n(191);function a(e,t){for(var n=0;n<t.length;n++){var a=t[n];a.enumerable=a.enumerable||!1,a.configurable=!0,"value"in a&&(a.writable=!0),r(e,a.key,a)}}e.exports=function(e,t,n){return t&&a(e.prototype,t),n&&a(e,n),e}},function(e,t){e.exports=function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}},function(e,t,n){"use strict";(function(e){n.d(t,"t",(function(){return me})),n.d(t,"A",(function(){return ve})),n.d(t,"i",(function(){return ge})),n.d(t,"w",(function(){return ye})),n.d(t,"r",(function(){return be})),n.d(t,"u",(function(){return Ee})),n.d(t,"s",(function(){return xe})),n.d(t,"p",(function(){return Se})),n.d(t,"v",(function(){return we})),n.d(t,"y",(function(){return je})),n.d(t,"z",(function(){return Ce})),n.d(t,"K",(function(){return Oe})),n.d(t,"f",(function(){return _e})),n.d(t,"n",(function(){return Ae})),n.d(t,"h",(function(){return ke})),n.d(t,"E",(function(){return Ie})),n.d(t,"L",(function(){return Fe})),n.d(t,"o",(function(){return Ye})),n.d(t,"D",(function(){return Ke})),n.d(t,"a",(function(){return Ge})),n.d(t,"I",(function(){return Ze})),n.d(t,"b",(function(){return Xe})),n.d(t,"H",(function(){return Qe})),n.d(t,"G",(function(){return et})),n.d(t,"F",(function(){return tt})),n.d(t,"k",(function(){return nt})),n.d(t,"d",(function(){return rt})),n.d(t,"g",(function(){return at})),n.d(t,"m",(function(){return ot})),n.d(t,"l",(function(){return it})),n.d(t,"e",(function(){return ct})),n.d(t,"J",(function(){return st})),n.d(t,"x",(function(){return ut})),n.d(t,"B",(function(){return lt})),n.d(t,"C",(function(){return pt})),n.d(t,"j",(function(){return ft})),n.d(t,"c",(function(){return dt})),n.d(t,"q",(function(){return mt}));var r=n(82),a=n.n(r),o=n(130),i=n.n(o),c=n(50),s=n.n(c),u=n(13),l=n.n(u),p=n(30),f=n.n(p),d=n(17),h=n.n(d),m=(n(34),n(32)),v=n.n(m),g=(n(24),n(15),n(157)),y=n.n(g),b=n(21),E=n.n(b),x=n(22),S=n.n(x),w=(n(29),n(16)),j=n.n(w),C=n(12),O=n.n(C),_=n(18),A=n.n(_),k=n(2),I=n.n(k),P=n(45),T=n.n(P),N=n(71),R=n.n(N),M=n(4),D=n.n(M),q=n(14),L=n.n(q),B=n(20),U=n.n(B),V=n(1),z=n.n(V),F=n(375),J=n(376),W=n.n(J),H=n(207),$=n.n(H),Y=n(208),K=n.n(Y),G=n(158),Z=n.n(G),X=n(266),Q=n.n(X),ee=n(102),te=n.n(ee),ne=n(58),re=n.n(ne),ae=n(112),oe=n(26),ie=n(378),ce=n.n(ie),se=n(114),ue=n(379),le=n.n(ue),pe=n(380),fe=n.n(pe),de="default",he=function(e){return z.a.Iterable.isIterable(e)};function me(e){try{var t=JSON.parse(e);if(t&&"object"===U()(t))return t}catch(e){}return!1}function ve(e){return Ee(e)?he(e)?e.toJS():e:{}}function ge(e){var t,n;if(he(e))return e;if(e instanceof oe.a.File)return e;if(!Ee(e))return e;if(L()(e))return D()(n=z.a.Seq(e)).call(n,ge).toList();if(re()(R()(e))){var r,a=function(e){if(!re()(R()(e)))return e;var t,n={},r="_**[]",a={},o=T()(R()(e).call(e));try{for(o.s();!(t=o.n()).done;){var i=t.value;if(n[i[0]]||a[i[0]]&&a[i[0]].containsMultiple){var c,s,u,l;if(!a[i[0]])a[i[0]]={containsMultiple:!0,length:1},n[I()(u=I()(l="".concat(i[0])).call(l,r)).call(u,a[i[0]].length)]=n[i[0]],delete n[i[0]];a[i[0]].length+=1,n[I()(c=I()(s="".concat(i[0])).call(s,r)).call(c,a[i[0]].length)]=i[1]}else n[i[0]]=i[1]}}catch(e){o.e(e)}finally{o.f()}return n}(e);return D()(r=z.a.OrderedMap(a)).call(r,ge)}return D()(t=z.a.OrderedMap(e)).call(t,ge)}function ye(e){return L()(e)?e:[e]}function be(e){return"function"==typeof e}function Ee(e){return!!e&&"object"===U()(e)}function xe(e){return"function"==typeof e}function Se(e){return L()(e)}var we=K.a;function je(e,t){var n;return S()(n=j()(e)).call(n,(function(n,r){return n[r]=t(e[r],r),n}),{})}function Ce(e,t){var n;return S()(n=j()(e)).call(n,(function(n,r){var a=t(e[r],r);return a&&"object"===U()(a)&&E()(n,a),n}),{})}function Oe(e){return function(t){t.dispatch,t.getStateLine truncated
|
||||
//# sourceMappingURL=swagger-ui-es-bundle-core.js.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"sources":["webpack://SwaggerUIBundle/webpack/bootstrap","webpack://SwaggerUIBundle/external \"react\"","webpack://SwaggerUIBundle/external \"immutable\"","webpack://SwaggerUIBundle/./node_modules/@babel/runtime-corejs3/core-js-stable/instance/concat.js","webpack://SwaggerUIBundle/./node_modules/@babel/runtime-corejs3/helpers/defineProperty.js","webpack://SwaggerUIBundle/./node_modules/@babel/runtime-corejs3/core-js-stable/instance/map.js","webpack://SwaggerUIBundle/./node_modules/@babel/runtime-corejs3/helpers/createClass.js","webpack://SwaggerUIBundle/./node_modules/@babel/runtime-corejs3/helpers/classCallCheck.js","webpack://SwaggerUIBundle/./src/core/utils.js","webpack://SwaggerUIBundle/./node_modules/@babel/runtime-corejs3/helpers/inherits.js","webpack://SwaggerUIBundle/./node_modules/@babel/runtime-corejs3/helpers/createSuper.js","webpack://SwaggerUIBundle/./node_modules/@babel/runtime-corejs3/helpers/assertThisInitialized.js","webpack://SwaggerUIBundle/external \"prop-types\"","webpack://SwaggerUIBundle/./node_modules/@babel/runtime-corejs3/core-js-stable/instance/filter.js","webpack://SwaggerUIBundle/./node_modules/@babel/runtime-corejs3/core-js-stable/instance/index-of.js","webpack://SwaggerUIBundle/./node_modules/@babel/runtime-corejs3/core-js-stable/array/is-array.js","webpack://SwaggerUIBundle/./node_modules/@babel/runtime-corejs3/helpers/slicedToArray.js","webpack://SwaggerUIBundle/./node_modules/@babel/runtime-corejs3/core-js-stable/object/keys.js","webpack://SwaggerUIBundle/./node_modules/@babel/runtime-corejs3/helpers/toConsumableArray.js","webpack://SwaggerUIBundle/./node_modules/@babel/runtime-corejs3/core-js-stable/instance/for-each.js","webpack://SwaggerUIBundle/external \"reselect\"","webpack://SwaggerUIBundle/./node_modules/@babel/runtime-corejs3/helpers/typeof.js","webpack://SwaggerUIBundle/./node_modules/@babel/runtime-corejs3/core-js-stable/object/assign.js","webpack://SwaggerUIBundle/./node_modules/@babel/runtime-corejs3/core-js-stable/instance/reduce.js","webpack://SwaggerUIBundle/./node_modules/core-js-pure/internals/export.js","webpack://SwaggerUIBundle/./node_modules/@babel/runtime-corejs3/core-js-stable/instance/slice.js","webpack://SwaggerUIBundle/./node_modules/@babel/runtime-corejs3/helpers/objectSpread2.js","webpack://SwaggerUIBundle/./src/core/window.js","webpack://SwaggerUIBundle/external \"react-immutable-proptypes\"","webpack://SwaggerUIBundle/./node_modules/@babel/runtime-corejs3/helpers/extends.js","webpack://SwaggerUIBundle/./node_modules/@babel/runtime-corejs3/core-js-stable/instance/bind.js","webpack://SwaggerUIBundle/./node_modules/@babel/runtime-corejs3/core-js-stable/json/stringify.js","webpack://SwaggerUIBundle/./node_modules/core-js-pure/internals/path.js","webpack://SwaggerUIBundle/./node_modules/@babel/runtime-corejs3/core-js-stable/instance/some.js","webpack://SwaggerUIBundle/./node_modules/core-js-pure/internals/fails.js","webpack://SwaggerUIBundle/./node_modules/@babel/runtime-corejs3/core-js-stable/instance/includes.js","webpack://SwaggerUIBundle/./src/core/plugins/oas3/helpers.jsx","webpack://SwaggerUIBundle/./node_modules/core-js-pure/internals/well-known-symbol.js","webpack://SwaggerUIBundle/./node_modules/@babel/runtime-corejs3/regenerator/index.js","webpack://SwaggerUIBundle/./node_modules/core-js-pure/internals/global.js","webpack://SwaggerUIBundle/./node_modules/core-js-pure/internals/entry-virtual.js","webpack://SwaggerUIBundle/./node_modules/core-js-pure/internals/is-object.js","webpack://SwaggerUIBundle/./node_modules/lodash/get.js","webpack://SwaggerUIBundle/./src/core/plugins/spec/actions.js","webpack://SwaggerUIBundle/./node_modules/core-js-pure/internals/descriptors.js","webpack://SwaggerUIBundle/./node_modules/core-js-pure/internals/define-well-known-symbol.js","webpack://SwaggerUIBundle/./node_modules/@babel/runtime-corejs3/helpers/createForOfIteratorHelper.js","webpack://SwaggerUIBundle/./node_modules/core-js-pure/internals/has.js","webpack://SwaggerUIBundle/./node_modules/lodash/isArray.js","webpack://SwaggerUIBundle/./node_modules/core-js-pure/internals/an-object.js","webpack://SwaggerUIBundle/./node_modules/@babel/runtime-corejs3/helpers/objectWithoutProperties.js","webpack://SwaggerUIBundle/./node_modules/@babel/runtime-corejs3/core-js-stable/instance/find.js","webpack://SwaggerUIBundle/./src/core/plugins/oas3/actions.js","webpack://SwaggerUIBundle/./node_modules/lodash/isObject.js","webpack://SwaggerUIBundle/./node_modules/swagger-client/es/helpers.js","webpack://SwaggerUIBundle/./src/core/plugins/err/actions.js","webpack://SwaggerUIBundle/./node_modules/core-js-pure/internals/object-define-property.js","webpack://SwaggerUIBundle/./node_modules/core-js-pure/internals/array-method-uses-to-length.js","webpack://SwaggerUIBundle/external \"classnames\"","webpack://SwaggerUIBundle/./node_modules/lodash/isFunction.js","webpack://SwaggerUIBundle/./node_modules/@babel/runtime-corejs3/core-js-stable/promise.js","webpack://SwagLine truncated
|
||||
@@ -1,3 +0,0 @@
|
||||
/*! For license information please see swagger-ui-es-bundle.js.LICENSE.txt */
|
||||
module.exports=function(e){var t={};function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}return n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)n.d(r,o,function(t){return e[t]}.bind(null,o));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="/dist",n(n.s=544)}([function(e,t,n){"use strict";e.exports=n(129)},function(e,t,n){e.exports=function(){"use strict";var e=Array.prototype.slice;function t(e,t){t&&(e.prototype=Object.create(t.prototype)),e.prototype.constructor=e}function n(e){return i(e)?e:J(e)}function r(e){return s(e)?e:K(e)}function o(e){return u(e)?e:Y(e)}function a(e){return i(e)&&!c(e)?e:G(e)}function i(e){return!(!e||!e[p])}function s(e){return!(!e||!e[f])}function u(e){return!(!e||!e[h])}function c(e){return s(e)||u(e)}function l(e){return!(!e||!e[d])}t(r,n),t(o,n),t(a,n),n.isIterable=i,n.isKeyed=s,n.isIndexed=u,n.isAssociative=c,n.isOrdered=l,n.Keyed=r,n.Indexed=o,n.Set=a;var p="@@__IMMUTABLE_ITERABLE__@@",f="@@__IMMUTABLE_KEYED__@@",h="@@__IMMUTABLE_INDEXED__@@",d="@@__IMMUTABLE_ORDERED__@@",m="delete",v=5,g=1<<v,y=g-1,b={},_={value:!1},w={value:!1};function x(e){return e.value=!1,e}function E(e){e&&(e.value=!0)}function S(){}function C(e,t){t=t||0;for(var n=Math.max(0,e.length-t),r=new Array(n),o=0;o<n;o++)r[o]=e[o+t];return r}function A(e){return void 0===e.size&&(e.size=e.__iterate(O)),e.size}function k(e,t){if("number"!=typeof t){var n=t>>>0;if(""+n!==t||4294967295===n)return NaN;t=n}return t<0?A(e)+t:t}function O(){return!0}function j(e,t,n){return(0===e||void 0!==n&&e<=-n)&&(void 0===t||void 0!==n&&t>=n)}function T(e,t){return N(e,t,0)}function I(e,t){return N(e,t,t)}function N(e,t,n){return void 0===e?n:e<0?Math.max(0,t+e):void 0===t?e:Math.min(t,e)}var P=0,M=1,R=2,D="function"==typeof Symbol&&Symbol.iterator,L="@@iterator",B=D||L;function F(e){this.next=e}function U(e,t,n,r){var o=0===e?t:1===e?n:[t,n];return r?r.value=o:r={value:o,done:!1},r}function q(){return{value:void 0,done:!0}}function z(e){return!!H(e)}function V(e){return e&&"function"==typeof e.next}function W(e){var t=H(e);return t&&t.call(e)}function H(e){var t=e&&(D&&e[D]||e[L]);if("function"==typeof t)return t}function $(e){return e&&"number"==typeof e.length}function J(e){return null==e?ie():i(e)?e.toSeq():ce(e)}function K(e){return null==e?ie().toKeyedSeq():i(e)?s(e)?e.toSeq():e.fromEntrySeq():se(e)}function Y(e){return null==e?ie():i(e)?s(e)?e.entrySeq():e.toIndexedSeq():ue(e)}function G(e){return(null==e?ie():i(e)?s(e)?e.entrySeq():e:ue(e)).toSetSeq()}F.prototype.toString=function(){return"[Iterator]"},F.KEYS=P,F.VALUES=M,F.ENTRIES=R,F.prototype.inspect=F.prototype.toSource=function(){return this.toString()},F.prototype[B]=function(){return this},t(J,n),J.of=function(){return J(arguments)},J.prototype.toSeq=function(){return this},J.prototype.toString=function(){return this.__toString("Seq {","}")},J.prototype.cacheResult=function(){return!this._cache&&this.__iterateUncached&&(this._cache=this.entrySeq().toArray(),this.size=this._cache.length),this},J.prototype.__iterate=function(e,t){return pe(this,e,t,!0)},J.prototype.__iterator=function(e,t){return fe(this,e,t,!0)},t(K,J),K.prototype.toKeyedSeq=function(){return this},t(Y,J),Y.of=function(){return Y(arguments)},Y.prototype.toIndexedSeq=function(){return this},Y.prototype.toString=function(){return this.__toString("Seq [","]")},Y.prototype.__iterate=function(e,t){return pe(this,e,t,!1)},Y.prototype.__iterator=function(e,t){return fe(this,e,t,!1)},t(G,J),G.of=function(){return G(arguments)},G.prototype.toSetSeq=function(){return this},J.isSeq=ae,J.Keyed=K,J.Set=G,J.Indexed=Y;var Z,X,Q,ee="@@__IMMUTABLE_SEQ__@@";function te(e){this._array=e,this.size=e.length}function ne(e){var t=Object.keys(e);this._object=e,this._keys=t,this.size=t.length}function re(e){this._iterable=e,this.size=e.length||e.size}function oe(e){this._iterator=e,this._iteratorCache=[]}function ae(e){return!(!e||!e[ee])}function ie(){return Z||(Z=new te([]))}function se(e){var t=Array.isArray(e)?new te(e).fromEntrySeq():V(e)?new oe(e).fromEntrySeq():z(e)?new re(e).fromEntrySeq():"object"==typeof e?new ne(e):void 0;if(!t)throw new TypeError("Expected Array or iterable object of [k, v] entries, or keyed object: "+e);return t}function ue(e){var t=le(e);if(!t)throw new TypeError("Expected Array or iterable object of values: "+e);return tLine truncated
|
||||
//# sourceMappingURL=swagger-ui-es-bundle.js.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"sources":["webpack://SwaggerUIBundle/webpack/bootstrap","webpack://SwaggerUIBundle/./node_modules/react/react.js","webpack://SwaggerUIBundle/./node_modules/immutable/dist/immutable.js","webpack://SwaggerUIBundle/./node_modules/@babel/runtime-corejs3/core-js-stable/instance/concat.js","webpack://SwaggerUIBundle/./node_modules/@babel/runtime-corejs3/helpers/defineProperty.js","webpack://SwaggerUIBundle/./node_modules/@babel/runtime-corejs3/core-js-stable/instance/map.js","webpack://SwaggerUIBundle/./node_modules/@babel/runtime-corejs3/helpers/createClass.js","webpack://SwaggerUIBundle/./node_modules/@babel/runtime-corejs3/helpers/classCallCheck.js","webpack://SwaggerUIBundle/./src/core/utils.js","webpack://SwaggerUIBundle/./node_modules/@babel/runtime-corejs3/helpers/inherits.js","webpack://SwaggerUIBundle/./node_modules/@babel/runtime-corejs3/helpers/createSuper.js","webpack://SwaggerUIBundle/./node_modules/@babel/runtime-corejs3/helpers/assertThisInitialized.js","webpack://SwaggerUIBundle/./node_modules/prop-types/index.js","webpack://SwaggerUIBundle/./node_modules/@babel/runtime-corejs3/core-js-stable/instance/filter.js","webpack://SwaggerUIBundle/./node_modules/@babel/runtime-corejs3/core-js-stable/instance/index-of.js","webpack://SwaggerUIBundle/./node_modules/@babel/runtime-corejs3/core-js-stable/array/is-array.js","webpack://SwaggerUIBundle/./node_modules/@babel/runtime-corejs3/helpers/slicedToArray.js","webpack://SwaggerUIBundle/./node_modules/@babel/runtime-corejs3/core-js-stable/object/keys.js","webpack://SwaggerUIBundle/./node_modules/@babel/runtime-corejs3/helpers/toConsumableArray.js","webpack://SwaggerUIBundle/./node_modules/@babel/runtime-corejs3/core-js-stable/instance/for-each.js","webpack://SwaggerUIBundle/./node_modules/reselect/es/index.js","webpack://SwaggerUIBundle/./node_modules/@babel/runtime-corejs3/helpers/typeof.js","webpack://SwaggerUIBundle/./node_modules/@babel/runtime-corejs3/core-js-stable/object/assign.js","webpack://SwaggerUIBundle/./node_modules/@babel/runtime-corejs3/core-js-stable/instance/reduce.js","webpack://SwaggerUIBundle/./node_modules/core-js-pure/internals/export.js","webpack://SwaggerUIBundle/./node_modules/@babel/runtime-corejs3/core-js-stable/instance/slice.js","webpack://SwaggerUIBundle/./node_modules/@babel/runtime-corejs3/helpers/objectSpread2.js","webpack://SwaggerUIBundle/./node_modules/fbjs/lib/invariant.js","webpack://SwaggerUIBundle/./src/core/window.js","webpack://SwaggerUIBundle/./node_modules/react-immutable-proptypes/dist/ImmutablePropTypes.js","webpack://SwaggerUIBundle/./node_modules/@babel/runtime-corejs3/helpers/extends.js","webpack://SwaggerUIBundle/./node_modules/@babel/runtime-corejs3/core-js-stable/instance/bind.js","webpack://SwaggerUIBundle/./node_modules/react-dom/lib/reactProdInvariant.js","webpack://SwaggerUIBundle/./node_modules/@babel/runtime-corejs3/core-js-stable/json/stringify.js","webpack://SwaggerUIBundle/./node_modules/core-js-pure/internals/path.js","webpack://SwaggerUIBundle/./node_modules/fbjs/lib/warning.js","webpack://SwaggerUIBundle/./node_modules/@babel/runtime-corejs3/core-js-stable/instance/some.js","webpack://SwaggerUIBundle/./node_modules/core-js-pure/internals/fails.js","webpack://SwaggerUIBundle/./node_modules/@babel/runtime-corejs3/core-js-stable/instance/includes.js","webpack://SwaggerUIBundle/./src/core/plugins/oas3/helpers.jsx","webpack://SwaggerUIBundle/./node_modules/core-js-pure/internals/well-known-symbol.js","webpack://SwaggerUIBundle/./node_modules/object-assign/index.js","webpack://SwaggerUIBundle/./node_modules/@babel/runtime-corejs3/regenerator/index.js","webpack://SwaggerUIBundle/./node_modules/core-js-pure/internals/global.js","webpack://SwaggerUIBundle/./node_modules/core-js-pure/internals/entry-virtual.js","webpack://SwaggerUIBundle/./node_modules/react-dom/lib/ReactDOMComponentTree.js","webpack://SwaggerUIBundle/./node_modules/core-js-pure/internals/is-object.js","webpack://SwaggerUIBundle/./node_modules/lodash/get.js","webpack://SwaggerUIBundle/./src/core/plugins/spec/actions.js","webpack://SwaggerUIBundle/./node_modules/core-js-pure/internals/descriptors.js","webpack://SwaggerUIBundle/./node_modules/core-js-pure/internals/define-well-known-symbol.js","webpack://SwaggerUIBundle/./node_modules/js-yaml/lib/js-yaml/type.js","webpack://SwaggerUIBundle/./node_modules/@babel/runtime-corejs3/helpers/createForOfIteratorHelper.js","webpack://SwaggerUIBundle/./node_modules/core-js-pure/internals/has.js","webpack://SwaggerUIBundle/./node_modules/lodash/isArray.js","webpack://SwaggerUIBundle/(webpack)/buildin/global.js","webpack://SwaggerUIBundle/./node_modules/core-js-pure/internals/an-object.js","webpack://SwaggerUIBundle/./node_modules/@babel/runtime-corejs3/helpers/objectWithoutProperties.js","webpack://SwaggerUIBundle/./node_modules/fbjs/lib/ExecutionEnvironment.js","webpack://SwaggerUIBundle/./node_modules/@babel/runtime-corejs3/core-js-stable/instance/find.js","webpack://SwaggerUIBundle/.Line truncated
|
||||
@@ -1,3 +0,0 @@
|
||||
/*! For license information please see swagger-ui-standalone-preset.js.LICENSE.txt */
|
||||
!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e(function(){try{return require("esprima")}catch(t){}}()):"function"==typeof define&&define.amd?define(["esprima"],e):"object"==typeof exports?exports.SwaggerUIStandalonePreset=e(function(){try{return require("esprima")}catch(t){}}()):t.SwaggerUIStandalonePreset=e(t.esprima)}(this,(function(t){return function(t){var e={};function n(r){if(e[r])return e[r].exports;var i=e[r]={i:r,l:!1,exports:{}};return t[r].call(i.exports,i,i.exports,n),i.l=!0,i.exports}return n.m=t,n.c=e,n.d=function(t,e,r){n.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:r})},n.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},n.t=function(t,e){if(1&e&&(t=n(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var i in t)n.d(r,i,function(e){return t[e]}.bind(null,i));return r},n.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return n.d(e,"a",e),e},n.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},n.p="/dist",n(n.s=241)}([function(t,e,n){t.exports=function(){"use strict";var t=Array.prototype.slice;function e(t,e){e&&(t.prototype=Object.create(e.prototype)),t.prototype.constructor=t}function n(t){return u(t)?t:J(t)}function r(t){return s(t)?t:Z(t)}function i(t){return a(t)?t:V(t)}function o(t){return u(t)&&!c(t)?t:X(t)}function u(t){return!(!t||!t[l])}function s(t){return!(!t||!t[p])}function a(t){return!(!t||!t[h])}function c(t){return s(t)||a(t)}function f(t){return!(!t||!t[d])}e(r,n),e(i,n),e(o,n),n.isIterable=u,n.isKeyed=s,n.isIndexed=a,n.isAssociative=c,n.isOrdered=f,n.Keyed=r,n.Indexed=i,n.Set=o;var l="@@__IMMUTABLE_ITERABLE__@@",p="@@__IMMUTABLE_KEYED__@@",h="@@__IMMUTABLE_INDEXED__@@",d="@@__IMMUTABLE_ORDERED__@@",y="delete",v=5,g=1<<v,w=g-1,M={},m={value:!1},_={value:!1};function L(t){return t.value=!1,t}function x(t){t&&(t.value=!0)}function b(){}function j(t,e){e=e||0;for(var n=Math.max(0,t.length-e),r=new Array(n),i=0;i<n;i++)r[i]=t[i+e];return r}function N(t){return void 0===t.size&&(t.size=t.__iterate(D)),t.size}function S(t,e){if("number"!=typeof e){var n=e>>>0;if(""+n!==e||4294967295===n)return NaN;e=n}return e<0?N(t)+e:e}function D(){return!0}function I(t,e,n){return(0===t||void 0!==n&&t<=-n)&&(void 0===e||void 0!==n&&e>=n)}function A(t,e){return C(t,e,0)}function E(t,e){return C(t,e,e)}function C(t,e,n){return void 0===t?n:t<0?Math.max(0,e+t):void 0===e?t:Math.min(e,t)}var O=0,T=1,z=2,k="function"==typeof Symbol&&Symbol.iterator,U="@@iterator",Y=k||U;function P(t){this.next=t}function R(t,e,n,r){var i=0===t?e:1===t?n:[e,n];return r?r.value=i:r={value:i,done:!1},r}function Q(){return{value:void 0,done:!0}}function F(t){return!!W(t)}function B(t){return t&&"function"==typeof t.next}function G(t){var e=W(t);return e&&e.call(t)}function W(t){var e=t&&(k&&t[k]||t[U]);if("function"==typeof e)return e}function q(t){return t&&"number"==typeof t.length}function J(t){return null==t?ut():u(t)?t.toSeq():ct(t)}function Z(t){return null==t?ut().toKeyedSeq():u(t)?s(t)?t.toSeq():t.fromEntrySeq():st(t)}function V(t){return null==t?ut():u(t)?s(t)?t.entrySeq():t.toIndexedSeq():at(t)}function X(t){return(null==t?ut():u(t)?s(t)?t.entrySeq():t:at(t)).toSetSeq()}P.prototype.toString=function(){return"[Iterator]"},P.KEYS=O,P.VALUES=T,P.ENTRIES=z,P.prototype.inspect=P.prototype.toSource=function(){return this.toString()},P.prototype[Y]=function(){return this},e(J,n),J.of=function(){return J(arguments)},J.prototype.toSeq=function(){return this},J.prototype.toString=function(){return this.__toString("Seq {","}")},J.prototype.cacheResult=function(){return!this._cache&&this.__iterateUncached&&(this._cache=this.entrySeq().toArray(),this.size=this._cache.length),this},J.prototype.__iterate=function(t,e){return lt(this,t,e,!0)},J.prototype.__iterator=function(t,e){return pt(this,t,e,!0)},e(Z,J),Z.prototype.toKeyedSeq=function(){return this},e(V,J),V.of=function(){return V(arguments)},V.prototype.toIndexedSeq=function(){return this},V.prototype.toString=function(){return this.__toString("Seq [","]")},V.prototype.__iterate=function(t,e){return lt(this,t,e,!1)},V.prototype.__iterator=function(t,e){return pt(this,t,e,!1)},e(X,J),X.of=function(){return X(arguments)},X.prototype.toSetSeq=function(){return this},J.isSeq=ot,J.Keyed=Z,J.Set=X,J.Indexed=V;var H,K,$,tt="@@__IMMUTABLE_SEQ__@@";function et(t){this._array=t,this.size=t.length}function nt(t){var e=Object.keys(t);this._object=t,this._keys=e,this.size=e.length}function rt(t){this._iterable=t,this.size=t.length||t.size}function it(t){this._iterator=t,this._iteratorCache=[]}function ot(t){return!(!t||!t[tt])}function ut(){return H||(H=new et([]))}function st(t){var e=Array.isArray(t)?new et(t).fromEntrySeq()Line truncated
|
||||
//# sourceMappingURL=swagger-ui-standalone-preset.js.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"sources":["webpack://SwaggerUIStandalonePreset/webpack/universalModuleDefinition","webpack://SwaggerUIStandalonePreset/webpack/bootstrap","webpack://SwaggerUIStandalonePreset/./node_modules/immutable/dist/immutable.js","webpack://SwaggerUIStandalonePreset/./node_modules/core-js-pure/internals/export.js","webpack://SwaggerUIStandalonePreset/./node_modules/react/react.js","webpack://SwaggerUIStandalonePreset/./node_modules/core-js-pure/internals/define-well-known-symbol.js","webpack://SwaggerUIStandalonePreset/./node_modules/@babel/runtime-corejs3/core-js-stable/instance/concat.js","webpack://SwaggerUIStandalonePreset/./node_modules/core-js-pure/internals/fails.js","webpack://SwaggerUIStandalonePreset/./node_modules/core-js-pure/internals/well-known-symbol.js","webpack://SwaggerUIStandalonePreset/./node_modules/js-yaml/lib/js-yaml/type.js","webpack://SwaggerUIStandalonePreset/./node_modules/@babel/runtime-corejs3/core-js-stable/array/is-array.js","webpack://SwaggerUIStandalonePreset/./node_modules/core-js-pure/internals/entry-virtual.js","webpack://SwaggerUIStandalonePreset/./node_modules/core-js-pure/internals/path.js","webpack://SwaggerUIStandalonePreset/./node_modules/@babel/runtime-corejs3/helpers/typeof.js","webpack://SwaggerUIStandalonePreset/./node_modules/@babel/runtime-corejs3/helpers/defineProperty.js","webpack://SwaggerUIStandalonePreset/./node_modules/core-js-pure/internals/global.js","webpack://SwaggerUIStandalonePreset/./node_modules/core-js-pure/internals/is-object.js","webpack://SwaggerUIStandalonePreset/./node_modules/inherits/inherits_browser.js","webpack://SwaggerUIStandalonePreset/./node_modules/safe-buffer/index.js","webpack://SwaggerUIStandalonePreset/./node_modules/@babel/runtime-corejs3/core-js-stable/instance/map.js","webpack://SwaggerUIStandalonePreset/./node_modules/core-js-pure/internals/has.js","webpack://SwaggerUIStandalonePreset/./src/core/utils.js","webpack://SwaggerUIStandalonePreset/(webpack)/buildin/global.js","webpack://SwaggerUIStandalonePreset/./node_modules/core-js-pure/internals/descriptors.js","webpack://SwaggerUIStandalonePreset/./node_modules/core-js-pure/internals/to-object.js","webpack://SwaggerUIStandalonePreset/./node_modules/lodash/isArray.js","webpack://SwaggerUIStandalonePreset/./node_modules/core-js-pure/internals/create-non-enumerable-property.js","webpack://SwaggerUIStandalonePreset/./node_modules/core-js-pure/internals/array-method-uses-to-length.js","webpack://SwaggerUIStandalonePreset/./node_modules/lodash/_root.js","webpack://SwaggerUIStandalonePreset/./node_modules/@babel/runtime-corejs3/helpers/assertThisInitialized.js","webpack://SwaggerUIStandalonePreset/./node_modules/@babel/runtime-corejs3/core-js-stable/object/keys.js","webpack://SwaggerUIStandalonePreset/./node_modules/core-js-pure/internals/an-object.js","webpack://SwaggerUIStandalonePreset/./node_modules/es5-ext/object/valid-callable.js","webpack://SwaggerUIStandalonePreset/./node_modules/@babel/runtime-corejs3/core-js-stable/instance/for-each.js","webpack://SwaggerUIStandalonePreset/./node_modules/@babel/runtime-corejs3/core-js-stable/instance/filter.js","webpack://SwaggerUIStandalonePreset/./node_modules/core-js-pure/internals/to-indexed-object.js","webpack://SwaggerUIStandalonePreset/./node_modules/core-js-pure/internals/object-define-property.js","webpack://SwaggerUIStandalonePreset/./node_modules/core-js-pure/internals/to-length.js","webpack://SwaggerUIStandalonePreset/./node_modules/core-js-pure/internals/iterators.js","webpack://SwaggerUIStandalonePreset/./node_modules/process/browser.js","webpack://SwaggerUIStandalonePreset/./node_modules/readable-stream/lib/_stream_duplex.js","webpack://SwaggerUIStandalonePreset/./node_modules/es5-ext/object/is-value.js","webpack://SwaggerUIStandalonePreset/./node_modules/es5-ext/number/to-pos-integer.js","webpack://SwaggerUIStandalonePreset/./node_modules/core-js-pure/internals/require-object-coercible.js","webpack://SwaggerUIStandalonePreset/./node_modules/core-js-pure/internals/classof.js","webpack://SwaggerUIStandalonePreset/./node_modules/core-js-pure/modules/web.dom-collections.iterator.js","webpack://SwaggerUIStandalonePreset/./node_modules/core-js-pure/internals/array-iteration.js","webpack://SwaggerUIStandalonePreset/./node_modules/react/lib/ReactElement.js","webpack://SwaggerUIStandalonePreset/./node_modules/lodash/_getNative.js","webpack://SwaggerUIStandalonePreset/./node_modules/es5-ext/object/valid-value.js","webpack://SwaggerUIStandalonePreset/./node_modules/sha.js/hash.js","webpack://SwaggerUIStandalonePreset/./node_modules/js-yaml/lib/js-yaml/common.js","webpack://SwaggerUIStandalonePreset/./node_modules/js-yaml/lib/js-yaml/schema.js","webpack://SwaggerUIStandalonePreset/./node_modules/lodash/eq.js","webpack://SwaggerUIStandalonePreset/./node_modules/lodash/isEmpty.js","webpack://SwaggerUIStandalonePreset/./node_modules/core-js-pure/internals/create-property-descriptor.js","webpack://SwaggerUIStandalonePreset/./node_moduLine truncated
|
||||
-11057
File diff suppressed because it is too large.
Load diff
@@ -1 +0,0 @@
|
||||
{"version":3,"sources":["webpack:///./src/style/main.scss","webpack:///./node_modules/tachyons-sass/scss/_normalize.scss","webpack:///./src/style/_type.scss","webpack:///./node_modules/tachyons-sass/scss/_debug-children.scss","webpack:///./node_modules/tachyons-sass/scss/_debug-grid.scss","webpack:///./node_modules/tachyons-sass/scss/_box-sizing.scss","webpack:///./node_modules/tachyons-sass/scss/_aspect-ratios.scss","webpack:///./node_modules/tachyons-sass/scss/_images.scss","webpack:///./node_modules/tachyons-sass/scss/_background-size.scss","webpack:///./node_modules/tachyons-sass/scss/_background-position.scss","webpack:///./node_modules/tachyons-sass/scss/_outlines.scss","webpack:///./node_modules/tachyons-sass/scss/_borders.scss","webpack:///./node_modules/tachyons-sass/scss/_border-colors.scss","webpack:///./node_modules/tachyons-sass/scss/_border-radius.scss","webpack:///./node_modules/tachyons-sass/scss/_border-style.scss","webpack:///./node_modules/tachyons-sass/scss/_border-widths.scss","webpack:///./node_modules/tachyons-sass/scss/_box-shadow.scss","webpack:///./node_modules/tachyons-sass/scss/_code.scss","webpack:///./node_modules/tachyons-sass/scss/_coordinates.scss","webpack:///./node_modules/tachyons-sass/scss/_clears.scss","webpack:///./node_modules/tachyons-sass/scss/_flexbox.scss","webpack:///./node_modules/tachyons-sass/scss/_display.scss","webpack:///./node_modules/tachyons-sass/scss/_floats.scss","webpack:///./node_modules/tachyons-sass/scss/_font-family.scss","webpack:///./node_modules/tachyons-sass/scss/_font-style.scss","webpack:///./node_modules/tachyons-sass/scss/_font-weight.scss","webpack:///./node_modules/tachyons-sass/scss/_forms.scss","webpack:///./node_modules/tachyons-sass/scss/_heights.scss","webpack:///./node_modules/tachyons-sass/scss/_letter-spacing.scss","webpack:///./node_modules/tachyons-sass/scss/_line-height.scss","webpack:///./node_modules/tachyons-sass/scss/_links.scss","webpack:///./node_modules/tachyons-sass/scss/_lists.scss","webpack:///./node_modules/tachyons-sass/scss/_max-widths.scss","webpack:///./node_modules/tachyons-sass/scss/_widths.scss","webpack:///./node_modules/tachyons-sass/scss/_overflow.scss","webpack:///./node_modules/tachyons-sass/scss/_position.scss","webpack:///./node_modules/tachyons-sass/scss/_opacity.scss","webpack:///./node_modules/tachyons-sass/scss/_rotations.scss","webpack:///./node_modules/tachyons-sass/scss/_skins.scss","webpack:///./node_modules/tachyons-sass/scss/_skins-pseudo.scss","webpack:///./node_modules/tachyons-sass/scss/_spacing.scss","webpack:///./node_modules/tachyons-sass/scss/_variables.scss","webpack:///./node_modules/tachyons-sass/scss/_negative-margins.scss","webpack:///./node_modules/tachyons-sass/scss/_tables.scss","webpack:///./node_modules/tachyons-sass/scss/_text-decoration.scss","webpack:///./node_modules/tachyons-sass/scss/_text-align.scss","webpack:///./node_modules/tachyons-sass/scss/_text-transform.scss","webpack:///./node_modules/tachyons-sass/scss/_type-scale.scss","webpack:///./node_modules/tachyons-sass/scss/_typography.scss","webpack:///./node_modules/tachyons-sass/scss/_utilities.scss","webpack:///./node_modules/tachyons-sass/scss/_visibility.scss","webpack:///./node_modules/tachyons-sass/scss/_white-space.scss","webpack:///./node_modules/tachyons-sass/scss/_vertical-align.scss","webpack:///./node_modules/tachyons-sass/scss/_hovers.scss","webpack:///./node_modules/tachyons-sass/scss/_z-index.scss","webpack:///./node_modules/tachyons-sass/scss/_nested.scss","webpack:///./src/style/_layout.scss","webpack:///./src/style/_variables.scss","webpack:///./src/style/_buttons.scss","webpack:///./src/style/_mixins.scss","webpack:///./src/style/_form.scss","webpack:///./src/style/_modal.scss","webpack:///./src/style/_models.scss","webpack:///./src/style/_servers.scss","webpack:///./src/style/_table.scss","webpack:///./src/style/_topbar.scss","webpack:///./src/style/_information.scss","webpack:///./src/style/_authorize.scss","webpack:///./src/style/_errors.scss","webpack:///./src/style/_split-pane-mode.scss","webpack:///./src/style/_markdown.scss"],"names":[],"mappings":"AAAA;ECOA,4ECLI,sBAAuB,CAEvB,aFmhCJ,CAvhCA,iBCmBE,gBAAiB,CACjB,yBAA0B,CAC1B,6BD2gCF,CAhiCA,iBCgCE,QDogCF,CApiCA,gHC6CE,aDggCF,CA7iCA,eCsDE,aAAc,CACd,cD2/BF,CAljCA,2DCqEE,aDo/BF,CAzjCA,mBC6EE,eDg/BF,CA7jCA,eCsFE,sBAAuB,CACvB,QAAS,CACT,gBD8+BF,CAtkCA,gBCiGE,+BAAiC,CACjC,aD2+BF,CA7kCA,cC8GE,4BAA6B,CAC7B,oCDq+BF,CAplCA,wBCwHE,kBAAmB,CACnB,yBAA0B,CAC1B,wCAAiC,CAAjC,gCDm+BF,CA7lCA,iCCmIE,mBAAoB,CASpB,kBDs9BF,CAlmCA,kDCuJE,+BAAiC,CACjC,aDw9BF,CAhnCA,gBCgKE,iBDo9BF,CApnCA,iBCwKE,qBAAsB,CACtB,UDg9BF,CAznCA,kBCiLE,aD48BF,CA7nCA,gCC2LE,aAAc,CACd,aAAc,CACd,iBAAkB,CAClB,uBDu8BF,CAroCA,gBCkME,aDu8BF,CAzoCA,gBCsME,SDu8BF,CA7oCA,oCCkNE,oBDg8BF,CAlpCA,kCC0NE,YAAa,CACb,QD47BF,CAvpCA,gBCmOE,iBDw7BF,CA3pCA,2BC2OE,eDo7BF,CA/pCA,kGC2PE,sBAAuB,CACvB,cAAe,CACf,gBAAiB,CACjB,QDg7BF,CA9qCA,qCCwQE,gBD46BF,CAprCA,sCCkRE,mBDw6BF,CA1rCA,qGC+RE,yBDm6BF,CAlsCA,wKC0SE,iBAAkB,CACLine truncated
|
||||
@@ -1,3 +0,0 @@
|
||||
/*! For license information please see swagger-ui.js.LICENSE.txt */
|
||||
!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.SwaggerUICore=t():e.SwaggerUICore=t()}(this,(function(){return function(e){var t={};function n(r){if(t[r])return t[r].exports;var a=t[r]={i:r,l:!1,exports:{}};return e[r].call(a.exports,a,a.exports,n),a.l=!0,a.exports}return n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var a in e)n.d(r,a,function(t){return e[t]}.bind(null,a));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="/dist",n(n.s=410)}([function(e,t){e.exports=require("react")},function(e,t){e.exports=require("immutable")},function(e,t,n){e.exports=n(449)},function(e,t,n){var r=n(191);e.exports=function(e,t,n){return t in e?r(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}},function(e,t,n){e.exports=n(452)},function(e,t,n){var r=n(191);function a(e,t){for(var n=0;n<t.length;n++){var a=t[n];a.enumerable=a.enumerable||!1,a.configurable=!0,"value"in a&&(a.writable=!0),r(e,a.key,a)}}e.exports=function(e,t,n){return t&&a(e.prototype,t),n&&a(e,n),e}},function(e,t){e.exports=function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}},function(e,t,n){"use strict";(function(e){n.d(t,"t",(function(){return me})),n.d(t,"A",(function(){return ve})),n.d(t,"i",(function(){return ge})),n.d(t,"w",(function(){return ye})),n.d(t,"r",(function(){return be})),n.d(t,"u",(function(){return Ee})),n.d(t,"s",(function(){return xe})),n.d(t,"p",(function(){return Se})),n.d(t,"v",(function(){return we})),n.d(t,"y",(function(){return je})),n.d(t,"z",(function(){return Ce})),n.d(t,"K",(function(){return Oe})),n.d(t,"f",(function(){return _e})),n.d(t,"n",(function(){return Ae})),n.d(t,"h",(function(){return ke})),n.d(t,"E",(function(){return Ie})),n.d(t,"L",(function(){return Fe})),n.d(t,"o",(function(){return Ye})),n.d(t,"D",(function(){return Ke})),n.d(t,"a",(function(){return Ge})),n.d(t,"I",(function(){return Ze})),n.d(t,"b",(function(){return Xe})),n.d(t,"H",(function(){return Qe})),n.d(t,"G",(function(){return et})),n.d(t,"F",(function(){return tt})),n.d(t,"k",(function(){return nt})),n.d(t,"d",(function(){return rt})),n.d(t,"g",(function(){return at})),n.d(t,"m",(function(){return ot})),n.d(t,"l",(function(){return it})),n.d(t,"e",(function(){return ct})),n.d(t,"J",(function(){return st})),n.d(t,"x",(function(){return ut})),n.d(t,"B",(function(){return lt})),n.d(t,"C",(function(){return pt})),n.d(t,"j",(function(){return ft})),n.d(t,"c",(function(){return dt})),n.d(t,"q",(function(){return mt}));var r=n(82),a=n.n(r),o=n(130),i=n.n(o),c=n(50),s=n.n(c),u=n(13),l=n.n(u),p=n(30),f=n.n(p),d=n(17),h=n.n(d),m=(n(34),n(32)),v=n.n(m),g=(n(24),n(15),n(157)),y=n.n(g),b=n(21),E=n.n(b),x=n(22),S=n.n(x),w=(n(29),n(16)),j=n.n(w),C=n(12),O=n.n(C),_=n(18),A=n.n(_),k=n(2),I=n.n(k),P=n(45),T=n.n(P),N=n(71),R=n.n(N),M=n(4),D=n.n(M),q=n(14),L=n.n(q),B=n(20),U=n.n(B),V=n(1),z=n.n(V),F=n(375),J=n(376),W=n.n(J),H=n(207),$=n.n(H),Y=n(208),K=n.n(Y),G=n(158),Z=n.n(G),X=n(266),Q=n.n(X),ee=n(102),te=n.n(ee),ne=n(58),re=n.n(ne),ae=n(112),oe=n(26),ie=n(378),ce=n.n(ie),se=n(114),ue=n(379),le=n.n(ue),pe=n(380),fe=n.n(pe),de="default",he=function(e){return z.a.Iterable.isIterable(e)};function me(e){try{var t=JSON.parse(e);if(t&&"object"===U()(t))return t}catch(e){}return!1}function ve(e){return Ee(e)?he(e)?e.toJS():e:{}}function ge(e){var t,n;if(he(e))return e;if(e instanceof oe.a.File)return e;if(!Ee(e))return e;if(L()(e))return D()(n=z.a.Seq(e)).call(n,ge).toList();if(re()(R()(e))){var r,a=function(e){if(!re()(R()(e)))return e;var t,n={},r="_**[]",a={},o=T()(R()(e).call(e));try{for(o.s();!(t=o.n()).done;){var i=t.value;if(n[i[0]]||a[i[0]]&&a[i[0]].containsMultiple){var c,s,u,l;if(!a[i[0]])a[i[0]]={containsMultiple:!0,length:1},n[I()(u=I()(l="".concat(i[0])).call(l,r)).call(u,a[i[0]].length)]=n[i[0]],delete n[i[0]];a[i[0]].length+=1,n[I()(c=I()(s="".concat(i[0])).call(s,r)).call(c,a[i[0]].length)]=i[1]}else n[i[0]]=i[1]}}catch(e){o.e(e)}finally{o.f()}return n}(e);return D()(r=z.a.OrderedMap(a)).call(r,ge)}return D()(t=z.a.OrderedMap(e)).call(t,ge)}function ye(e){return L()(e)?e:[e]}function be(e){return"function"==typeof e}function Ee(e){return!!e&&"object"===U()(e)}function xe(e){return"function"==typeof e}function Se(e){return L()(e)}var we=K.a;function je(e,t){var n;return S()(n=j()(e)).call(n,(function(n,Line truncated
|
||||
//# sourceMappingURL=swagger-ui.js.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"sources":["webpack://SwaggerUICore/webpack/universalModuleDefinition","webpack://SwaggerUICore/webpack/bootstrap","webpack://SwaggerUICore/external \"react\"","webpack://SwaggerUICore/external \"immutable\"","webpack://SwaggerUICore/./node_modules/@babel/runtime-corejs3/core-js-stable/instance/concat.js","webpack://SwaggerUICore/./node_modules/@babel/runtime-corejs3/helpers/defineProperty.js","webpack://SwaggerUICore/./node_modules/@babel/runtime-corejs3/core-js-stable/instance/map.js","webpack://SwaggerUICore/./node_modules/@babel/runtime-corejs3/helpers/createClass.js","webpack://SwaggerUICore/./node_modules/@babel/runtime-corejs3/helpers/classCallCheck.js","webpack://SwaggerUICore/./src/core/utils.js","webpack://SwaggerUICore/./node_modules/@babel/runtime-corejs3/helpers/inherits.js","webpack://SwaggerUICore/./node_modules/@babel/runtime-corejs3/helpers/createSuper.js","webpack://SwaggerUICore/./node_modules/@babel/runtime-corejs3/helpers/assertThisInitialized.js","webpack://SwaggerUICore/external \"prop-types\"","webpack://SwaggerUICore/./node_modules/@babel/runtime-corejs3/core-js-stable/instance/filter.js","webpack://SwaggerUICore/./node_modules/@babel/runtime-corejs3/core-js-stable/instance/index-of.js","webpack://SwaggerUICore/./node_modules/@babel/runtime-corejs3/core-js-stable/array/is-array.js","webpack://SwaggerUICore/./node_modules/@babel/runtime-corejs3/helpers/slicedToArray.js","webpack://SwaggerUICore/./node_modules/@babel/runtime-corejs3/core-js-stable/object/keys.js","webpack://SwaggerUICore/./node_modules/@babel/runtime-corejs3/helpers/toConsumableArray.js","webpack://SwaggerUICore/./node_modules/@babel/runtime-corejs3/core-js-stable/instance/for-each.js","webpack://SwaggerUICore/external \"reselect\"","webpack://SwaggerUICore/./node_modules/@babel/runtime-corejs3/helpers/typeof.js","webpack://SwaggerUICore/./node_modules/@babel/runtime-corejs3/core-js-stable/object/assign.js","webpack://SwaggerUICore/./node_modules/@babel/runtime-corejs3/core-js-stable/instance/reduce.js","webpack://SwaggerUICore/./node_modules/core-js-pure/internals/export.js","webpack://SwaggerUICore/./node_modules/@babel/runtime-corejs3/core-js-stable/instance/slice.js","webpack://SwaggerUICore/./node_modules/@babel/runtime-corejs3/helpers/objectSpread2.js","webpack://SwaggerUICore/./src/core/window.js","webpack://SwaggerUICore/external \"react-immutable-proptypes\"","webpack://SwaggerUICore/./node_modules/@babel/runtime-corejs3/helpers/extends.js","webpack://SwaggerUICore/./node_modules/@babel/runtime-corejs3/core-js-stable/instance/bind.js","webpack://SwaggerUICore/./node_modules/@babel/runtime-corejs3/core-js-stable/json/stringify.js","webpack://SwaggerUICore/./node_modules/core-js-pure/internals/path.js","webpack://SwaggerUICore/./node_modules/@babel/runtime-corejs3/core-js-stable/instance/some.js","webpack://SwaggerUICore/./node_modules/core-js-pure/internals/fails.js","webpack://SwaggerUICore/./node_modules/@babel/runtime-corejs3/core-js-stable/instance/includes.js","webpack://SwaggerUICore/./src/core/plugins/oas3/helpers.jsx","webpack://SwaggerUICore/./node_modules/core-js-pure/internals/well-known-symbol.js","webpack://SwaggerUICore/./node_modules/@babel/runtime-corejs3/regenerator/index.js","webpack://SwaggerUICore/./node_modules/core-js-pure/internals/global.js","webpack://SwaggerUICore/./node_modules/core-js-pure/internals/entry-virtual.js","webpack://SwaggerUICore/./node_modules/core-js-pure/internals/is-object.js","webpack://SwaggerUICore/./node_modules/lodash/get.js","webpack://SwaggerUICore/./src/core/plugins/spec/actions.js","webpack://SwaggerUICore/./node_modules/core-js-pure/internals/descriptors.js","webpack://SwaggerUICore/./node_modules/core-js-pure/internals/define-well-known-symbol.js","webpack://SwaggerUICore/./node_modules/@babel/runtime-corejs3/helpers/createForOfIteratorHelper.js","webpack://SwaggerUICore/./node_modules/core-js-pure/internals/has.js","webpack://SwaggerUICore/./node_modules/lodash/isArray.js","webpack://SwaggerUICore/./node_modules/core-js-pure/internals/an-object.js","webpack://SwaggerUICore/./node_modules/@babel/runtime-corejs3/helpers/objectWithoutProperties.js","webpack://SwaggerUICore/./node_modules/@babel/runtime-corejs3/core-js-stable/instance/find.js","webpack://SwaggerUICore/./src/core/plugins/oas3/actions.js","webpack://SwaggerUICore/./node_modules/lodash/isObject.js","webpack://SwaggerUICore/./node_modules/swagger-client/es/helpers.js","webpack://SwaggerUICore/./src/core/plugins/err/actions.js","webpack://SwaggerUICore/./node_modules/core-js-pure/internals/object-define-property.js","webpack://SwaggerUICore/./node_modules/core-js-pure/internals/array-method-uses-to-length.js","webpack://SwaggerUICore/external \"classnames\"","webpack://SwaggerUICore/./node_modules/lodash/isFunction.js","webpack://SwaggerUICore/./node_modules/@babel/runtime-corejs3/core-js-stable/promise.js","webpack://SwaggerUICore/./node_modules/core-js-pure/internals/to-indexed-objLine truncated
|
||||
Executable
+11
@@ -0,0 +1,11 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Calculate UV_THREADPOOL_SIZE based on number of CPUs
|
||||
cpus=$(node -e "const os = require('os'); console.log(os.cpus().length);")
|
||||
uv_threadpool_size=$(($cpus * 2))
|
||||
|
||||
# Set UV_THREADPOOL_SIZE as an environment variable
|
||||
export UV_THREADPOOL_SIZE="${UV_THREADPOOL_SIZE:-$uv_threadpool_size}"
|
||||
|
||||
# Start your application using yarn
|
||||
node dist/main
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 2.9 KiB |
Binary file not shown.
@@ -1,40 +0,0 @@
|
||||
from pprint import pprint
|
||||
|
||||
import requests
|
||||
from flask import Flask
|
||||
from flask import request
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
|
||||
@app.route("/")
|
||||
def whatsapp_echo():
|
||||
return "WhatsApp HTTP API Echo server is ready!"
|
||||
|
||||
|
||||
@app.route("/message", methods=["GET", "POST"])
|
||||
def whatsapp_webhook():
|
||||
data = request.get_json()
|
||||
if not data:
|
||||
return "WhatsApp HTTP API Echo server is ready!"
|
||||
pprint(data)
|
||||
# The text
|
||||
text = data["body"]
|
||||
# Number in format 791111111@c.us
|
||||
from_ = data["from"]
|
||||
|
||||
if data.get("clientUrl", None):
|
||||
# Download file and set text to path
|
||||
client_url = data["clientUrl"]
|
||||
filename = client_url.split("/")[-1]
|
||||
path = "/tmp/" + filename
|
||||
r = requests.get(client_url)
|
||||
open(path, "wb").write(r.content)
|
||||
text = f"We have downloaded file here: {path}"
|
||||
|
||||
# Send a text back via WhatsApp HTTP API
|
||||
response = requests.post(
|
||||
"http://localhost:3000/api/sendText", json={"chatId": from_, "text": text}
|
||||
)
|
||||
response.raise_for_status()
|
||||
return "OK"
|
||||
Binary file not shown.
@@ -0,0 +1,128 @@
|
||||
# WAHA (WhatsApp HTTP API) Python examples
|
||||
|
||||
## Installation
|
||||
|
||||
We assume that you have installed software:
|
||||
|
||||
1. Python 3
|
||||
2. Docker
|
||||
|
||||
### Download and start image
|
||||
|
||||
First of all, you must run WhatsApp HTTP API locally (which under the hood it
|
||||
runs real WhatsApp Web instance and expose HTTP API for interaction).
|
||||
|
||||
Here are the steps from
|
||||
[Quick Start](https://waha.devlike.pro/docs/overview/quick-start/):
|
||||
|
||||
Download and start WhatsApp HTTP API docker container
|
||||
|
||||
```bash
|
||||
# Download the image
|
||||
docker pull devlikeapro/waha
|
||||
# Run the docker container
|
||||
docker run -it --rm --network=host -e WHATSAPP_HOOK_URL=http://localhost:5000/bot -e "WHATSAPP_HOOK_EVENTS=*" --name waha devlikeapro/waha
|
||||
|
||||
# It prints logs and the last line must be
|
||||
# WhatsApp HTTP API is running on: http://[::1]:3000
|
||||
```
|
||||
|
||||
#### Download image - ARM
|
||||
|
||||
If you're using ARM (like Apple Silicon, Apple M1, etc.) - use following
|
||||
commands to download the image
|
||||
|
||||
 For Core version the command is
|
||||
|
||||
```bash
|
||||
# Download the image
|
||||
docker pull devlikeapro/waha:arm
|
||||
# Rename it, so you can use devlikeapro/waha image in other place
|
||||
docker tag devlikeapro/waha:arm devlikeapro/waha
|
||||
# Run the docker container
|
||||
docker run -it --rm --network=host -e WHATSAPP_HOOK_URL=http://localhost:5000/bot -e "WHATSAPP_HOOK_EVENTS=*" --name waha devlikeapro/waha
|
||||
|
||||
# It prints logs and the last line must be
|
||||
# WhatsApp HTTP API is running on: http://[::1]:3000
|
||||
```
|
||||
|
||||
### Start session and scan QR
|
||||
|
||||
2. Open Swagger API in the browser http://localhost:3000/
|
||||
3. Start session and scan QR code in swagger
|
||||
1. Find `POST /api/sessions/` request press **Try it out** and **Execute** it
|
||||
with `default` session name
|
||||
2. Find `GET /api/screenshot` and execute it - it must show QR code
|
||||
3. Scan QR code on your mobile WhatsApp application (that installed on your
|
||||
phone)
|
||||
4. Execute `GET /api/screenshot` once again - it must show the screenshot
|
||||
from WhatsApp Web.
|
||||
4. Send test text message - find `POST /api/sendText` and execute it with
|
||||
payload (change `12132132130` in the `chatId` to phone number that is
|
||||
registered in WhatsApp).
|
||||
|
||||
```json
|
||||
{
|
||||
"chatId": "12132132130@c.us",
|
||||
"text": "Hi there!",
|
||||
"session": "default"
|
||||
}
|
||||
```
|
||||
|
||||
If you see **Hi there!** message then you're ready to run bots!
|
||||
|
||||
## WhatsApp Echo Bot
|
||||
|
||||
The WhatsApp Echo Bot is a sample flask webhook server application that echoes
|
||||
back to you whatever you send it. It can serve as a basic reference for how to
|
||||
set up webhooks and reply to incoming messages.
|
||||
|
||||
```bash
|
||||
# Clone the git repository with example
|
||||
git clone https://github.com/devlikeapro/waha.git
|
||||
# Open python example folder
|
||||
cd waha/examples/python
|
||||
# Install requirements
|
||||
python -mpip install -r requirements.txt
|
||||
# Run the bot
|
||||
FLASK_APP=whatsapp_echo_bot.py flask run
|
||||
```
|
||||
|
||||
Open http://127.0.0.1:5000/bot - if you see **WhatsApp Echo Bot is ready!** then
|
||||
the bot is ready to receive message!
|
||||
|
||||
Send message to the WhatsApp (that you used to scan QR code) and it'll echo text
|
||||
back to you!
|
||||
|
||||
## WhatsApp Download Files Bot
|
||||
|
||||
The WhatsApp Download Image Bot downloads all files people send to your WhatsApp
|
||||
and log the path for the file.
|
||||
|
||||
**The bot works only with WAHA Plus version** available with donations. Visit
|
||||
[read more about difference between Core and Plus versions](https://waha.devlike.pro/docs/how-to/plus-version/).
|
||||
|
||||
Download **WAHA Plus** version:
|
||||
|
||||
```bash
|
||||
# Download the image
|
||||
docker pull devlikeapro/waha-plus
|
||||
# Run the docker container
|
||||
docker run -it --rm --network=host -e WHATSAPP_HOOK_URL=http://localhost:5000/bot -e "WHATSAPP_HOOK_EVENTS=*" --name waha devlikeapro/waha-plus
|
||||
```
|
||||
|
||||
Run the WhatsApp Download Files bot:
|
||||
|
||||
```bash
|
||||
# Clone the git repository with example
|
||||
git clone https://github.com/devlikeapro/waha.git
|
||||
# Open python example folder
|
||||
cd waha/examples/python
|
||||
# Install requirements
|
||||
python -mpip install -r requirements.txt
|
||||
# Run the bot
|
||||
FLASK_APP=whatsapp_download_files_bot.py flask run
|
||||
```
|
||||
|
||||
Open http://127.0.0.1:5000/bot - if you see **WhatsApp Download Files Bot!**
|
||||
then the bot is ready to receive message with files!
|
||||
@@ -0,0 +1,3 @@
|
||||
flask~=1.0
|
||||
requests~=2.0
|
||||
markupsafe==2.0.1
|
||||
@@ -0,0 +1,84 @@
|
||||
from os.path import abspath
|
||||
from pprint import pprint
|
||||
|
||||
import requests
|
||||
from flask import Flask
|
||||
from flask import request
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
|
||||
def send_message(chat_id, text):
|
||||
"""
|
||||
Send message to chat_id.
|
||||
:param chat_id: Phone number + "@c.us" suffix - 1231231231@c.us
|
||||
:param text: Message for the recipient
|
||||
"""
|
||||
# Send a text back via WhatsApp HTTP API
|
||||
response = requests.post(
|
||||
"http://localhost:3000/api/sendText",
|
||||
json={
|
||||
"chatId": chat_id,
|
||||
"text": text,
|
||||
"session": "default",
|
||||
},
|
||||
)
|
||||
response.raise_for_status()
|
||||
|
||||
def send_seen(chat_id, message_id, participant):
|
||||
response = requests.post(
|
||||
"http://localhost:3000/api/sendSeen",
|
||||
json={
|
||||
"session": "default",
|
||||
"chatId": chat_id,
|
||||
"messageId": message_id,
|
||||
"participant": participant,
|
||||
},
|
||||
)
|
||||
response.raise_for_status()
|
||||
|
||||
@app.route("/")
|
||||
def whatsapp_echo():
|
||||
return "WhatsApp Download Files Bot is ready!"
|
||||
|
||||
|
||||
@app.route("/bot", methods=["GET", "POST"])
|
||||
def whatsapp_webhook():
|
||||
if request.method == "GET":
|
||||
return "WhatsApp Download Files Bot is ready!"
|
||||
|
||||
data = request.get_json()
|
||||
pprint(data)
|
||||
if data["event"] != "message":
|
||||
# We can't process other event yet
|
||||
return f"Unknown event {data['event']}"
|
||||
|
||||
payload = data["payload"]
|
||||
# Ignore messages without files
|
||||
if not payload.get("mediaUrl", None):
|
||||
return "No files in the message"
|
||||
|
||||
# Number in format 791111111@c.us
|
||||
chat_id = payload["from"]
|
||||
# Message ID - false_11111111111@c.us_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
message_id = payload['id']
|
||||
# For groups - who sent the message
|
||||
participant = payload.get('participant')
|
||||
# IMPORTANT - Always send seen before sending new message
|
||||
send_seen(chat_id=chat_id, message_id=message_id, participant=participant)
|
||||
|
||||
# Download the file and download it to the current folder
|
||||
client_url = payload["mediaUrl"]
|
||||
filename = client_url.split("/")[-1]
|
||||
path = abspath("./" + filename)
|
||||
r = requests.get(client_url)
|
||||
with open(path, "wb") as f:
|
||||
f.write(r.content)
|
||||
|
||||
# Send a text back via WhatsApp HTTP API
|
||||
text = f"We have downloaded file here: {path}"
|
||||
print(text)
|
||||
send_message(chat_id=chat_id, text=text)
|
||||
|
||||
# Send OK back
|
||||
return "OK"
|
||||
@@ -0,0 +1,123 @@
|
||||
import random
|
||||
from pprint import pprint
|
||||
from time import sleep
|
||||
|
||||
import requests
|
||||
from flask import Flask
|
||||
from flask import request
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
|
||||
def send_message(chat_id, text):
|
||||
"""
|
||||
Send message to chat_id.
|
||||
:param chat_id: Phone number + "@c.us" suffix - 1231231231@c.us
|
||||
:param text: Message for the recipient
|
||||
"""
|
||||
# Send a text back via WhatsApp HTTP API
|
||||
response = requests.post(
|
||||
"http://localhost:3000/api/sendText",
|
||||
json={
|
||||
"chatId": chat_id,
|
||||
"text": text,
|
||||
"session": "default",
|
||||
},
|
||||
)
|
||||
response.raise_for_status()
|
||||
|
||||
def reply(chat_id, message_id, text):
|
||||
response = requests.post(
|
||||
"http://localhost:3000/api/reply",
|
||||
json={
|
||||
"chatId": chat_id,
|
||||
"text": text,
|
||||
"reply_to": message_id,
|
||||
"session": "default",
|
||||
},
|
||||
)
|
||||
response.raise_for_status()
|
||||
|
||||
|
||||
def send_seen(chat_id, message_id, participant):
|
||||
response = requests.post(
|
||||
"http://localhost:3000/api/sendSeen",
|
||||
json={
|
||||
"session": "default",
|
||||
"chatId": chat_id,
|
||||
"messageId": message_id,
|
||||
"participant": participant,
|
||||
},
|
||||
)
|
||||
response.raise_for_status()
|
||||
|
||||
def start_typing(chat_id):
|
||||
response = requests.post(
|
||||
"http://localhost:3000/api/startTyping",
|
||||
json={
|
||||
"session": "default",
|
||||
"chatId": chat_id,
|
||||
},
|
||||
)
|
||||
response.raise_for_status()
|
||||
|
||||
def stop_typing(chat_id):
|
||||
response = requests.post(
|
||||
"http://localhost:3000/api/stopTyping",
|
||||
json={
|
||||
"session": "default",
|
||||
"chatId": chat_id,
|
||||
},
|
||||
)
|
||||
response.raise_for_status()
|
||||
|
||||
def typing(chat_id, seconds):
|
||||
start_typing(chat_id=chat_id)
|
||||
sleep(seconds)
|
||||
stop_typing(chat_id=chat_id)
|
||||
|
||||
@app.route("/")
|
||||
def whatsapp_echo():
|
||||
return "WhatsApp Echo Bot is ready!"
|
||||
|
||||
|
||||
@app.route("/bot", methods=["GET", "POST"])
|
||||
def whatsapp_webhook():
|
||||
if request.method == "GET":
|
||||
return "WhatsApp Echo Bot is ready!"
|
||||
|
||||
data = request.get_json()
|
||||
pprint(data)
|
||||
if data["event"] != "message":
|
||||
# We can't process other event yet
|
||||
return f"Unknown event {data['event']}"
|
||||
|
||||
# Payload that we've got
|
||||
payload = data["payload"]
|
||||
# The text
|
||||
text = payload.get("body")
|
||||
if not text:
|
||||
# We can't process non-text messages yet
|
||||
print("No text in message")
|
||||
print(payload)
|
||||
return "OK"
|
||||
# Number in format 1231231231@c.us or @g.us for group
|
||||
chat_id = payload["from"]
|
||||
# Message ID - false_11111111111@c.us_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
message_id = payload['id']
|
||||
# For groups - who sent the message
|
||||
participant = payload.get('participant')
|
||||
# IMPORTANT - Always send seen before sending new message
|
||||
send_seen(chat_id=chat_id, message_id=message_id, participant=participant)
|
||||
|
||||
|
||||
# Send a text back via WhatsApp HTTP API
|
||||
typing(chat_id=chat_id, seconds=random.random() * 3)
|
||||
send_message(chat_id=chat_id, text=text)
|
||||
|
||||
# OR reply on the message
|
||||
typing(chat_id=chat_id, seconds=random.random() * 3)
|
||||
reply(chat_id=chat_id, message_id=message_id, text=text)
|
||||
|
||||
# Send OK back
|
||||
return "OK"
|
||||
@@ -1,2 +0,0 @@
|
||||
flask
|
||||
requests
|
||||
Binary file not shown.
+4
-4
@@ -2,8 +2,8 @@
|
||||
"collection": "@nestjs/schematics",
|
||||
"sourceRoot": "src",
|
||||
"compilerOptions": {
|
||||
"plugins": [
|
||||
"@nestjs/swagger/plugin"
|
||||
]
|
||||
"plugins": ["@nestjs/swagger"],
|
||||
"assets": ["dashboard/**", "core/engines/webjs/*", "plus/engines/webjs/*"],
|
||||
"watchAssets": true
|
||||
}
|
||||
}
|
||||
}
|
||||
Generated
-31599
File diff suppressed because it is too large.
Load diff
+56
-13
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "whatsapp-http-api",
|
||||
"name": "waha",
|
||||
"version": "0.0.1",
|
||||
"description": "",
|
||||
"author": "",
|
||||
@@ -12,9 +12,10 @@
|
||||
"start": "nest start",
|
||||
"start:dev": "nest start --watch",
|
||||
"start:debug": "nest start --debug --watch",
|
||||
"start:prod": "node --unhandled-rejections=none dist/main",
|
||||
"start:prod": "node dist/main",
|
||||
"start:prod-exit": "node dist/main",
|
||||
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
|
||||
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix --max-warnings=0",
|
||||
"lint-cache": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix --cache --max-warnings=0",
|
||||
"test": "jest",
|
||||
"test:watch": "jest --watch",
|
||||
"test:cov": "jest --coverage",
|
||||
@@ -22,21 +23,61 @@
|
||||
"test:e2e": "jest --config ./test/jest-e2e.json"
|
||||
},
|
||||
"dependencies": {
|
||||
"@adiwajshing/baileys": "github:devlikeapro/Baileys#fork-master-channels-2",
|
||||
"@adiwajshing/keyed-db": "^0.2.4",
|
||||
"@aws-sdk/client-s3": "^3.633.0",
|
||||
"@aws-sdk/s3-request-presigner": "^3.633.0",
|
||||
"@nestjs/axios": "^3.0.2",
|
||||
"@nestjs/common": "^9.0.9",
|
||||
"@nestjs/config": "^0.5.0",
|
||||
"@nestjs/config": "^3.2.3",
|
||||
"@nestjs/core": "^9.0.9",
|
||||
"@nestjs/passport": "^9.0.0",
|
||||
"@nestjs/platform-express": "^9.0.9",
|
||||
"@nestjs/platform-ws": "^9.0.9",
|
||||
"@nestjs/serve-static": "^2.1.3",
|
||||
"@nestjs/swagger": "^6.0.5",
|
||||
"class-validator": "^0.12.2",
|
||||
"@nestjs/swagger": "^7.1.11",
|
||||
"@nestjs/terminus": "^10.2.3",
|
||||
"@nestjs/websockets": "^9.0.9",
|
||||
"@types/better-sqlite3": "^7.6.10",
|
||||
"@types/lodash": "^4.14.194",
|
||||
"@types/ws": "^8.5.4",
|
||||
"adm-zip": "0.5.10",
|
||||
"async-lock": "^1.4.1",
|
||||
"better-sqlite3": "11.3.0",
|
||||
"check-disk-space": "^3.4.0",
|
||||
"chokidar": "^3.6.0",
|
||||
"class-transformer": "^0.5.1",
|
||||
"class-validator": "0.14.0",
|
||||
"del": "^6.0.0",
|
||||
"express-basic-auth": "^1.2.1",
|
||||
"file-type": "16.5.4",
|
||||
"fs-extra": "^11.2.0",
|
||||
"https-proxy-agent": "^7.0.0",
|
||||
"joi": "^17.13.3",
|
||||
"knex": "^3.1.0",
|
||||
"libphonenumber-js": "^1.10.36",
|
||||
"link-preview-js": "^3.0.4",
|
||||
"lodash": "^4.17.21",
|
||||
"mime-types": "^2.1.27",
|
||||
"mongodb": "^6.3.0",
|
||||
"nestjs-pino": "^4.1.0",
|
||||
"passport": "^0.6.0",
|
||||
"passport-headerapikey": "^1.2.2",
|
||||
"pino-http": "^10.2.0",
|
||||
"pino-pretty": "^11.2.1",
|
||||
"pretty-bytes": "5.6.0",
|
||||
"promise-retry": "^2.0.1",
|
||||
"puppeteer": "^21.0.0",
|
||||
"qrcode": "^1.5.1",
|
||||
"qrcode-terminal": "^0.12.0",
|
||||
"reflect-metadata": "^0.1.13",
|
||||
"request": "^2.88.2",
|
||||
"requestretry": "^4.1.1",
|
||||
"rimraf": "^3.0.2",
|
||||
"rxjs": "^7.1.0",
|
||||
"sharp": "^0.33.4",
|
||||
"swagger-ui-express": "^4.1.4",
|
||||
"venom-bot": "^4.3.0"
|
||||
"whatsapp-web.js": "github:devlikeapro/whatsapp-web.js#fork-webpack-exodus-channels"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@nestjs/cli": "^9.0.0",
|
||||
@@ -44,21 +85,22 @@
|
||||
"@nestjs/testing": "^9.0.9",
|
||||
"@types/express": "^4.17.3",
|
||||
"@types/jest": "26.0.10",
|
||||
"@types/node": "^13.9.1",
|
||||
"@types/node": "^18.14.2",
|
||||
"@types/supertest": "^2.0.8",
|
||||
"@typescript-eslint/eslint-plugin": "3.9.1",
|
||||
"@typescript-eslint/parser": "3.9.1",
|
||||
"eslint": "7.7.0",
|
||||
"eslint-config-prettier": "^6.10.0",
|
||||
"eslint-plugin-import": "^2.20.1",
|
||||
"jest": "26.4.2",
|
||||
"eslint-plugin-simple-import-sort": "^10.0.0",
|
||||
"jest": "^29.7.0",
|
||||
"prettier": "^1.19.1",
|
||||
"supertest": "^4.0.2",
|
||||
"ts-jest": "26.2.0",
|
||||
"ts-jest": "^29.1.3",
|
||||
"ts-loader": "^6.2.1",
|
||||
"ts-node": "^10.9.1",
|
||||
"tsconfig-paths": "^4.1.0",
|
||||
"typescript": "4.7.4"
|
||||
"typescript": "^4.8.4"
|
||||
},
|
||||
"jest": {
|
||||
"moduleFileExtensions": [
|
||||
@@ -67,11 +109,12 @@
|
||||
"ts"
|
||||
],
|
||||
"rootDir": "src",
|
||||
"testRegex": ".spec.ts$",
|
||||
"testRegex": ".test.ts$",
|
||||
"transform": {
|
||||
"^.+\\.(t|j)s$": "ts-jest"
|
||||
},
|
||||
"coverageDirectory": "../coverage",
|
||||
"testEnvironment": "node"
|
||||
}
|
||||
},
|
||||
"packageManager": "yarn@3.6.3"
|
||||
}
|
||||
Executable
+23
@@ -0,0 +1,23 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
echo "Starting WAHA..."
|
||||
|
||||
docker run -d \
|
||||
--restart=always \
|
||||
--name waha \
|
||||
-p 3000:3000/tcp \
|
||||
-v ./.sessions:/app/.sessions \
|
||||
-v ./.media:/app/.media \
|
||||
--env WHATSAPP_API_KEY=321 \
|
||||
--env WAHA_DASHBOARD_USERNAME=waha \
|
||||
--env WAHA_DASHBOARD_PASSWORD=waha \
|
||||
--env WAHA_LOG_FORMAT=JSON \
|
||||
--env WAHA_LOG_LEVEL=info \
|
||||
--env WHATSAPP_DEFAULT_ENGINE=WEBJS \
|
||||
--env WHATSAPP_RESTART_ALL_SESSIONS=True \
|
||||
--env WAHA_PRINT_QR=False \
|
||||
--env WHATSAPP_FILES_LIFETIME=0 \
|
||||
--env WHATSAPP_FILES_FOLDER=/app/.media \
|
||||
devlikeapro/waha-plus:latest
|
||||
|
||||
echo "WAHA started."
|
||||
Executable
+21
@@ -0,0 +1,21 @@
|
||||
set -e
|
||||
echo "Updating WAHA..."
|
||||
echo "Logging in to Docker..."
|
||||
docker login -u devlikeapro # -p {KEY} - you can hard code key here https://portal.devlike.pro/
|
||||
|
||||
echo "Pulling the latest image..."
|
||||
docker pull devlikeapro/waha-plus
|
||||
|
||||
echo "Logging out of Docker..."
|
||||
docker logout
|
||||
|
||||
# Just pull if you're using WAHA Core
|
||||
# docker pull devlikeapro/waha
|
||||
|
||||
echo "Stopping the current container..."
|
||||
docker stop waha || true
|
||||
echo "Removing the current container..."
|
||||
docker rm waha || true
|
||||
|
||||
echo "Running WAHA..."
|
||||
sh ./waha-run.sh
|
||||
@@ -1,91 +0,0 @@
|
||||
import {ApiProperty} from "@nestjs/swagger";
|
||||
import {IsNotEmpty, IsString} from "class-validator";
|
||||
|
||||
export const WHATSAPP_DEFAULT_SESSION_NAME = "default"
|
||||
|
||||
//
|
||||
// Queries
|
||||
//
|
||||
export class SessionQuery {
|
||||
@IsNotEmpty()
|
||||
sessionName: string = WHATSAPP_DEFAULT_SESSION_NAME;
|
||||
}
|
||||
|
||||
export class CheckNumberStatusQuery extends SessionQuery {
|
||||
@IsString()
|
||||
phone: string
|
||||
}
|
||||
|
||||
export class MessageTextQuery extends SessionQuery {
|
||||
@IsString()
|
||||
phone: string
|
||||
@IsString()
|
||||
text: string
|
||||
}
|
||||
|
||||
//
|
||||
// Requests
|
||||
//
|
||||
|
||||
const chatIdProperty = ApiProperty({
|
||||
example: '791231234567@c.us'
|
||||
})
|
||||
const sessionNameProperty = ApiProperty({
|
||||
default: WHATSAPP_DEFAULT_SESSION_NAME,
|
||||
})
|
||||
|
||||
export class SessionRequest {
|
||||
@sessionNameProperty
|
||||
sessionName: string;
|
||||
}
|
||||
|
||||
export class ChatRequest extends SessionRequest {
|
||||
@chatIdProperty
|
||||
chatId: string;
|
||||
}
|
||||
|
||||
export class MessageContactVcard extends ChatRequest {
|
||||
contactsId: string;
|
||||
name: string
|
||||
}
|
||||
|
||||
export class MessageText extends ChatRequest {
|
||||
text: string;
|
||||
}
|
||||
|
||||
export class MessageTextButtons extends ChatRequest {
|
||||
text: string;
|
||||
title: string;
|
||||
buttons: any[];
|
||||
}
|
||||
|
||||
export class MessageReply extends ChatRequest {
|
||||
text: string;
|
||||
@ApiProperty({
|
||||
example: 'message.id',
|
||||
})
|
||||
reply_to: string;
|
||||
}
|
||||
|
||||
export class MessageLocation extends ChatRequest {
|
||||
latitude: string;
|
||||
longitude: string;
|
||||
title: string;
|
||||
}
|
||||
|
||||
export class MessageImage extends ChatRequest {
|
||||
path: string;
|
||||
filename: string;
|
||||
caption: string;
|
||||
}
|
||||
|
||||
export class MessageFile extends ChatRequest {
|
||||
path: string;
|
||||
filename: string;
|
||||
caption: string;
|
||||
}
|
||||
|
||||
export class MessageLinkPreview extends ChatRequest {
|
||||
url: string;
|
||||
title: string;
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
import {
|
||||
Body,
|
||||
Controller,
|
||||
Get,
|
||||
Post,
|
||||
Query,
|
||||
UseInterceptors,
|
||||
} from '@nestjs/common';
|
||||
import { UnprocessableEntityException } from '@nestjs/common/exceptions/unprocessable-entity.exception';
|
||||
import { ApiOperation, ApiSecurity, ApiTags } from '@nestjs/swagger';
|
||||
import { ApiFileAcceptHeader } from '@waha/nestjs/ApiFileAcceptHeader';
|
||||
import {
|
||||
SessionApiParam,
|
||||
SessionParam,
|
||||
} from '@waha/nestjs/params/SessionApiParam';
|
||||
|
||||
import { SessionManager } from '../core/abc/manager.abc';
|
||||
import { WhatsappSession } from '../core/abc/session.abc';
|
||||
import { BufferResponseInterceptor } from '../nestjs/BufferResponseInterceptor';
|
||||
import {
|
||||
CaptchaBody,
|
||||
OTPRequest,
|
||||
QRCodeFormat,
|
||||
QRCodeQuery,
|
||||
QRCodeValue,
|
||||
RequestCodeRequest,
|
||||
} from '../structures/auth.dto';
|
||||
import { WAHASessionStatus } from '../structures/enums.dto';
|
||||
import { Base64File } from '../structures/files.dto';
|
||||
|
||||
@ApiSecurity('api_key')
|
||||
@Controller('api/:session/auth')
|
||||
@ApiTags('🔑 Auth')
|
||||
class AuthController {
|
||||
constructor(private manager: SessionManager) {}
|
||||
|
||||
@Get('qr')
|
||||
@ApiOperation({
|
||||
summary: 'Get QR code for pairing WhatsApp API.',
|
||||
})
|
||||
@SessionApiParam
|
||||
@ApiFileAcceptHeader(Base64File, QRCodeValue)
|
||||
@UseInterceptors(new BufferResponseInterceptor())
|
||||
async getQR(
|
||||
@SessionParam session: WhatsappSession,
|
||||
@Query() query: QRCodeQuery,
|
||||
): Promise<Buffer | QRCodeValue> {
|
||||
if (session.status != WAHASessionStatus.SCAN_QR_CODE) {
|
||||
const err = `Can get QR code only in SCAN_QR_CODE status. The current status is '${session.status}'`;
|
||||
throw new UnprocessableEntityException(err);
|
||||
}
|
||||
const qr = session.getQR();
|
||||
if (query.format == QRCodeFormat.RAW) {
|
||||
return { value: qr.raw };
|
||||
}
|
||||
return qr.get();
|
||||
}
|
||||
|
||||
@Post('request-code')
|
||||
@SessionApiParam
|
||||
@ApiOperation({
|
||||
summary: 'Request authentication code.',
|
||||
})
|
||||
requestCode(
|
||||
@SessionParam session: WhatsappSession,
|
||||
@Body() request: RequestCodeRequest,
|
||||
) {
|
||||
return session.requestCode(request.phoneNumber, request.method, request);
|
||||
}
|
||||
|
||||
@Post('authorize-code')
|
||||
@SessionApiParam
|
||||
@ApiOperation({
|
||||
summary: 'Send OTP authentication code.',
|
||||
})
|
||||
authorizeCode(
|
||||
@SessionParam session: WhatsappSession,
|
||||
@Body() request: OTPRequest,
|
||||
) {
|
||||
return session.authorizeCode(request.code);
|
||||
}
|
||||
|
||||
@Get('captcha')
|
||||
@SessionApiParam
|
||||
@ApiFileAcceptHeader()
|
||||
@UseInterceptors(new BufferResponseInterceptor())
|
||||
@ApiOperation({
|
||||
summary: 'Get captcha image.',
|
||||
})
|
||||
async getCaptcha(@SessionParam session: WhatsappSession): Promise<Buffer> {
|
||||
const image = await session.getCaptcha();
|
||||
return image.get();
|
||||
}
|
||||
|
||||
@Post('captcha')
|
||||
@SessionApiParam
|
||||
@ApiOperation({
|
||||
summary: 'Enter captcha code.',
|
||||
})
|
||||
saveCaptcha(
|
||||
@SessionParam session: WhatsappSession,
|
||||
@Body() body: CaptchaBody,
|
||||
) {
|
||||
return session.saveCaptcha(body.code);
|
||||
}
|
||||
}
|
||||
|
||||
export { AuthController };
|
||||
@@ -0,0 +1,125 @@
|
||||
import {
|
||||
Body,
|
||||
Controller,
|
||||
Delete,
|
||||
Get,
|
||||
Param,
|
||||
Post,
|
||||
Query,
|
||||
} from '@nestjs/common';
|
||||
import { ApiOperation, ApiSecurity, ApiTags } from '@nestjs/swagger';
|
||||
import {
|
||||
SessionApiParam,
|
||||
SessionParam,
|
||||
} from '@waha/nestjs/params/SessionApiParam';
|
||||
import {
|
||||
Channel,
|
||||
CreateChannelRequest,
|
||||
ListChannelsQuery,
|
||||
NewsletterIdApiParam,
|
||||
NewsletterIdOrInviteCodeApiParam,
|
||||
} from '@waha/structures/channels.dto';
|
||||
|
||||
import { SessionManager } from '../core/abc/manager.abc';
|
||||
import { isNewsletter, WhatsappSession } from '../core/abc/session.abc';
|
||||
|
||||
@ApiSecurity('api_key')
|
||||
@Controller('api/:session/channels')
|
||||
@ApiTags('📢 Channels')
|
||||
export class ChannelsController {
|
||||
constructor(private manager: SessionManager) {}
|
||||
|
||||
@Get('')
|
||||
@SessionApiParam
|
||||
@ApiOperation({ summary: 'Get list of know channels' })
|
||||
async list(
|
||||
@SessionParam session: WhatsappSession,
|
||||
@Query() query: ListChannelsQuery,
|
||||
): Promise<Channel[]> {
|
||||
return session.channelsList(query);
|
||||
}
|
||||
|
||||
@Post('')
|
||||
@SessionApiParam
|
||||
@ApiOperation({ summary: 'Create a new channel.' })
|
||||
create(
|
||||
@SessionParam session: WhatsappSession,
|
||||
@Body() request: CreateChannelRequest,
|
||||
): Promise<Channel> {
|
||||
return session.channelsCreateChannel(request);
|
||||
}
|
||||
|
||||
@Delete(':id')
|
||||
@SessionApiParam
|
||||
@NewsletterIdApiParam
|
||||
@ApiOperation({ summary: 'Delete the channel.' })
|
||||
delete(@SessionParam session: WhatsappSession, @Param('id') id: string) {
|
||||
return session.channelsDeleteChannel(id);
|
||||
}
|
||||
|
||||
@Get(':id')
|
||||
@SessionApiParam
|
||||
@NewsletterIdOrInviteCodeApiParam
|
||||
@ApiOperation({
|
||||
summary: 'Get the channel info',
|
||||
description:
|
||||
'You can use either id (123@newsletter) ' +
|
||||
'OR invite code (https://www.whatsapp.com/channel/123)',
|
||||
})
|
||||
get(
|
||||
@SessionParam session: WhatsappSession,
|
||||
@Param('id') id: string,
|
||||
): Promise<Channel> {
|
||||
if (isNewsletter(id)) {
|
||||
return session.channelsGetChannel(id);
|
||||
} else {
|
||||
// https://www.whatsapp.com/channel/123 => 123
|
||||
const inviteCode = id.split('/').pop();
|
||||
return session.channelsGetChannelByInviteCode(inviteCode);
|
||||
}
|
||||
}
|
||||
|
||||
@Post('follow')
|
||||
@SessionApiParam
|
||||
@NewsletterIdApiParam
|
||||
@ApiOperation({ summary: 'Follow the channel.' })
|
||||
follow(
|
||||
@SessionParam session: WhatsappSession,
|
||||
@Param('id') id: string,
|
||||
): Promise<void> {
|
||||
return session.channelsFollowChannel(id);
|
||||
}
|
||||
|
||||
@Post('unfollow')
|
||||
@SessionApiParam
|
||||
@NewsletterIdApiParam
|
||||
@ApiOperation({ summary: 'Unfollow the channel.' })
|
||||
unfollow(
|
||||
@SessionParam session: WhatsappSession,
|
||||
@Param('id') id: string,
|
||||
): Promise<void> {
|
||||
return session.channelsUnfollowChannel(id);
|
||||
}
|
||||
|
||||
@Post('mute')
|
||||
@SessionApiParam
|
||||
@NewsletterIdApiParam
|
||||
@ApiOperation({ summary: 'Mute the channel.' })
|
||||
mute(
|
||||
@SessionParam session: WhatsappSession,
|
||||
@Param('id') id: string,
|
||||
): Promise<void> {
|
||||
return session.channelsMuteChannel(id);
|
||||
}
|
||||
|
||||
@Post('unmute')
|
||||
@SessionApiParam
|
||||
@NewsletterIdApiParam
|
||||
@ApiOperation({ summary: 'Unmute the channel.' })
|
||||
unmute(
|
||||
@SessionParam session: WhatsappSession,
|
||||
@Param('id') id: string,
|
||||
): Promise<void> {
|
||||
return session.channelsUnmuteChannel(id);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
import {
|
||||
Body,
|
||||
Controller,
|
||||
Delete,
|
||||
Get,
|
||||
Param,
|
||||
Post,
|
||||
Put,
|
||||
Query,
|
||||
UsePipes,
|
||||
ValidationPipe,
|
||||
} from '@nestjs/common';
|
||||
import { ApiOperation, ApiSecurity, ApiTags } from '@nestjs/swagger';
|
||||
import { ChatIdApiParam } from '@waha/nestjs/params/ChatIdApiParam';
|
||||
import { MessageIdApiParam } from '@waha/nestjs/params/MessageIdApiParam';
|
||||
import {
|
||||
SessionApiParam,
|
||||
SessionParam,
|
||||
} from '@waha/nestjs/params/SessionApiParam';
|
||||
|
||||
import { SessionManager } from '../core/abc/manager.abc';
|
||||
import { WhatsappSession } from '../core/abc/session.abc';
|
||||
import { parseBool } from '../helpers';
|
||||
import { GetChatMessagesQuery, GetChatsQuery } from '../structures/chats.dto';
|
||||
import { EditMessageRequest } from '../structures/chatting.dto';
|
||||
|
||||
@ApiSecurity('api_key')
|
||||
@Controller('api/:session/chats')
|
||||
@ApiTags('💬 Chats')
|
||||
@UsePipes(new ValidationPipe({ transform: true }))
|
||||
class ChatsController {
|
||||
constructor(private manager: SessionManager) {}
|
||||
|
||||
@Get('')
|
||||
@SessionApiParam
|
||||
@ApiOperation({ summary: 'Get chats' })
|
||||
getChats(
|
||||
@SessionParam session: WhatsappSession,
|
||||
@Query() query: GetChatsQuery,
|
||||
) {
|
||||
return session.getChats(query);
|
||||
}
|
||||
|
||||
@Delete(':chatId')
|
||||
@SessionApiParam
|
||||
@ApiOperation({ summary: 'Deletes the chat' })
|
||||
@ChatIdApiParam
|
||||
deleteChat(
|
||||
@SessionParam session: WhatsappSession,
|
||||
@Param('chatId') chatId: string,
|
||||
) {
|
||||
return session.deleteChat(chatId);
|
||||
}
|
||||
|
||||
@Get(':chatId/messages')
|
||||
@SessionApiParam
|
||||
@ApiOperation({ summary: 'Gets messages in the chat' })
|
||||
@ChatIdApiParam
|
||||
getChatMessages(
|
||||
@Query() query: GetChatMessagesQuery,
|
||||
@SessionParam session: WhatsappSession,
|
||||
@Param('chatId') chatId: string,
|
||||
) {
|
||||
const downloadMedia = parseBool(query.downloadMedia);
|
||||
return session.getChatMessages(chatId, query.limit, downloadMedia);
|
||||
}
|
||||
|
||||
@Delete(':chatId/messages')
|
||||
@SessionApiParam
|
||||
@ApiOperation({ summary: 'Clears all messages from the chat' })
|
||||
@ChatIdApiParam
|
||||
clearMessages(
|
||||
@SessionParam session: WhatsappSession,
|
||||
@Param('chatId') chatId: string,
|
||||
) {
|
||||
return session.clearMessages(chatId);
|
||||
}
|
||||
|
||||
@Delete(':chatId/messages/:messageId')
|
||||
@SessionApiParam
|
||||
@ChatIdApiParam
|
||||
@MessageIdApiParam
|
||||
@ApiOperation({ summary: 'Deletes a message from the chat' })
|
||||
deleteMessage(
|
||||
@SessionParam session: WhatsappSession,
|
||||
@Param('chatId') chatId: string,
|
||||
@Param('messageId') messageId: string,
|
||||
) {
|
||||
return session.deleteMessage(chatId, messageId);
|
||||
}
|
||||
|
||||
@Put(':chatId/messages/:messageId')
|
||||
@SessionApiParam
|
||||
@ChatIdApiParam
|
||||
@MessageIdApiParam
|
||||
@ApiOperation({ summary: 'Edits a message in the chat' })
|
||||
editMessage(
|
||||
@SessionParam session: WhatsappSession,
|
||||
@Param('chatId') chatId: string,
|
||||
@Param('messageId') messageId: string,
|
||||
@Body() body: EditMessageRequest,
|
||||
) {
|
||||
return session.editMessage(chatId, messageId, body);
|
||||
}
|
||||
|
||||
@Post(':chatId/archive')
|
||||
@SessionApiParam
|
||||
@ChatIdApiParam
|
||||
@ApiOperation({ summary: 'Archive the chat' })
|
||||
archiveChat(
|
||||
@SessionParam session: WhatsappSession,
|
||||
@Param('chatId') chatId: string,
|
||||
) {
|
||||
return session.chatsArchiveChat(chatId);
|
||||
}
|
||||
|
||||
@Post(':chatId/unarchive')
|
||||
@SessionApiParam
|
||||
@ChatIdApiParam
|
||||
@ApiOperation({ summary: 'Unarchive the chat' })
|
||||
unarchiveChat(
|
||||
@SessionParam session: WhatsappSession,
|
||||
@Param('chatId') chatId: string,
|
||||
) {
|
||||
return session.chatsUnarchiveChat(chatId);
|
||||
}
|
||||
}
|
||||
|
||||
export { ChatsController };
|
||||
+170
-110
@@ -1,128 +1,188 @@
|
||||
import {Body, Controller, Get, NotImplementedException, Post, Query} from '@nestjs/common';
|
||||
import {ApiOperation, ApiTags} from "@nestjs/swagger";
|
||||
import { Body, Controller, Get, Post, Put, Query } from '@nestjs/common';
|
||||
import { ApiOperation, ApiSecurity, ApiTags } from '@nestjs/swagger';
|
||||
|
||||
import { SessionManager } from '../core/abc/manager.abc';
|
||||
import {
|
||||
ChatRequest,
|
||||
CheckNumberStatusQuery,
|
||||
MessageContactVcard,
|
||||
MessageFile,
|
||||
MessageImage,
|
||||
MessageLinkPreview,
|
||||
MessageLocation,
|
||||
MessageReply,
|
||||
MessageText,
|
||||
MessageTextButtons,
|
||||
MessageTextQuery
|
||||
} from "./all.dto";
|
||||
import {ensureSuffix, WhatsappSessionManager} from "../whatsapp.service";
|
||||
ChatRequest,
|
||||
CheckNumberStatusQuery,
|
||||
GetMessageQuery,
|
||||
MessageContactVcardRequest,
|
||||
MessageFileRequest,
|
||||
MessageImageRequest,
|
||||
MessageLinkPreviewRequest,
|
||||
MessageLocationRequest,
|
||||
MessagePollRequest,
|
||||
MessageReactionRequest,
|
||||
MessageReplyRequest,
|
||||
MessageStarRequest,
|
||||
MessageTextQuery,
|
||||
MessageTextRequest,
|
||||
MessageVideoRequest,
|
||||
MessageVoiceRequest,
|
||||
SendSeenRequest,
|
||||
WANumberExistResult,
|
||||
} from '../structures/chatting.dto';
|
||||
import { WAMessage } from '../structures/responses.dto';
|
||||
|
||||
@ApiSecurity('api_key')
|
||||
@Controller('api')
|
||||
@ApiTags('chatting')
|
||||
@ApiTags('📤 Chatting')
|
||||
export class ChattingController {
|
||||
constructor(private whatsappSessionManager: WhatsappSessionManager) {
|
||||
}
|
||||
constructor(private manager: SessionManager) {}
|
||||
|
||||
@Get('/checkNumberStatus')
|
||||
@ApiOperation({summary: 'Check number status'})
|
||||
async checkNumberStatus(
|
||||
@Query() request: CheckNumberStatusQuery,
|
||||
) {
|
||||
const whatsapp = this.whatsappSessionManager.getSession(request.sessionName)
|
||||
try {
|
||||
const result = await whatsapp.checkNumberStatus(ensureSuffix(request.phone))
|
||||
return {numberExists: result['numberExists']}
|
||||
} catch (error) {
|
||||
// We need to "touch" the error in order to get unhandled rejections
|
||||
// It logs out the session and stop the app
|
||||
console.log(typeof error)
|
||||
console.log(error)
|
||||
return {numberExists: false}
|
||||
}
|
||||
}
|
||||
@Post('/sendText')
|
||||
@ApiOperation({ summary: 'Send a text message' })
|
||||
sendText(@Body() request: MessageTextRequest): Promise<WAMessage> {
|
||||
const whatsapp = this.manager.getSession(request.session);
|
||||
return whatsapp.sendText(request);
|
||||
}
|
||||
|
||||
@Post('/sendContactVcard')
|
||||
sendContactVcard(@Body() message: MessageContactVcard) {
|
||||
const whatsapp = this.whatsappSessionManager.getSession(message.sessionName)
|
||||
return whatsapp.sendContactVcard(message.chatId, message.contactsId, message.name)
|
||||
}
|
||||
@Post('/sendImage')
|
||||
@ApiOperation({
|
||||
summary: 'Send an image',
|
||||
description:
|
||||
'Either from an URL or base64 data - look at the request schemas for details.',
|
||||
})
|
||||
sendImage(@Body() request: MessageImageRequest) {
|
||||
const whatsapp = this.manager.getSession(request.session);
|
||||
return whatsapp.sendImage(request);
|
||||
}
|
||||
|
||||
@Get('/sendText')
|
||||
@ApiOperation({summary: 'Send a text message'})
|
||||
sendTextGet(
|
||||
@Query() message: MessageTextQuery,
|
||||
) {
|
||||
const whatsapp = this.whatsappSessionManager.getSession(message.sessionName)
|
||||
return whatsapp.sendText(ensureSuffix(message.phone), message.text)
|
||||
}
|
||||
@Post('/sendFile')
|
||||
@ApiOperation({
|
||||
summary: 'Send a file',
|
||||
description:
|
||||
'Either from an URL or base64 data - look at the request schemas for details.',
|
||||
})
|
||||
sendFile(@Body() request: MessageFileRequest) {
|
||||
const whatsapp = this.manager.getSession(request.session);
|
||||
return whatsapp.sendFile(request);
|
||||
}
|
||||
|
||||
@Post('/sendText')
|
||||
@ApiOperation({summary: 'Send a text message'})
|
||||
sendText(@Body() message: MessageText) {
|
||||
const whatsapp = this.whatsappSessionManager.getSession(message.sessionName)
|
||||
return whatsapp.sendText(ensureSuffix(message.chatId), message.text)
|
||||
}
|
||||
@Post('/sendVoice')
|
||||
@ApiOperation({
|
||||
summary: 'Send an voice message',
|
||||
description:
|
||||
'Either from an URL or base64 data - look at the request schemas for details.',
|
||||
})
|
||||
sendVoice(@Body() request: MessageVoiceRequest) {
|
||||
const whatsapp = this.manager.getSession(request.session);
|
||||
return whatsapp.sendVoice(request);
|
||||
}
|
||||
|
||||
@Post('/sendTextButtons')
|
||||
@ApiOperation({summary: 'Send a text message with buttons'})
|
||||
sendTextButtons(@Body() message: MessageTextButtons) {
|
||||
const whatsapp = this.whatsappSessionManager.getSession(message.sessionName)
|
||||
return whatsapp.sendButtons(ensureSuffix(message.chatId), message.title, message.buttons, message.text)
|
||||
}
|
||||
@Post('/sendVideo')
|
||||
@ApiOperation({
|
||||
summary: 'Send a video',
|
||||
description:
|
||||
'Either from an URL or base64 data - look at the request schemas for details.',
|
||||
})
|
||||
sendVideo(@Body() request: MessageVideoRequest) {
|
||||
const whatsapp = this.manager.getSession(request.session);
|
||||
return whatsapp.sendVideo(request);
|
||||
}
|
||||
|
||||
@Post('/sendLocation')
|
||||
sendLocation(@Body() message: MessageLocation) {
|
||||
const whatsapp = this.whatsappSessionManager.getSession(message.sessionName)
|
||||
return whatsapp.sendLocation(message.chatId, message.latitude, message.longitude, message.title)
|
||||
}
|
||||
@Post('/sendSeen')
|
||||
sendSeen(@Body() chat: SendSeenRequest) {
|
||||
const whatsapp = this.manager.getSession(chat.session);
|
||||
return whatsapp.sendSeen(chat);
|
||||
}
|
||||
|
||||
@Post('/sendLinkPreview')
|
||||
sendLinkPreview(@Body() message: MessageLinkPreview) {
|
||||
const whatsapp = this.whatsappSessionManager.getSession(message.sessionName)
|
||||
return whatsapp.sendLinkPreview(message.chatId, message.url, message.title)
|
||||
}
|
||||
@Post('/startTyping')
|
||||
async startTyping(@Body() chat: ChatRequest) {
|
||||
// It's infinitive action
|
||||
const whatsapp = this.manager.getSession(chat.session);
|
||||
await whatsapp.startTyping(chat);
|
||||
return { result: true };
|
||||
}
|
||||
|
||||
@Post('/sendImage')
|
||||
@ApiOperation({summary: 'NOT IMPLEMENTED YET'})
|
||||
sendImage(@Body() message: MessageImage) {
|
||||
throw new NotImplementedException();
|
||||
// TODO: Accept image URL, download it and then send with path
|
||||
const whatsapp = this.whatsappSessionManager.getSession(message.sessionName)
|
||||
return whatsapp.sendImage(message.chatId, message.path, message.filename, message.caption)
|
||||
}
|
||||
@Post('/stopTyping')
|
||||
async stopTyping(@Body() chat: ChatRequest) {
|
||||
const whatsapp = this.manager.getSession(chat.session);
|
||||
await whatsapp.stopTyping(chat);
|
||||
return { result: true };
|
||||
}
|
||||
|
||||
@Post('/sendFile')
|
||||
@ApiOperation({summary: 'NOT IMPLEMENTED YET'})
|
||||
sendFile(@Body() message: MessageFile) {
|
||||
throw new NotImplementedException();
|
||||
// TODO: Accept File URL, download it and then send with path
|
||||
const whatsapp = this.whatsappSessionManager.getSession(message.sessionName)
|
||||
return whatsapp.sendFile(message.chatId, message.path, message.filename, message.caption)
|
||||
}
|
||||
@Put('/reaction')
|
||||
@ApiOperation({ summary: 'React to a message with an emoji' })
|
||||
setReaction(@Body() request: MessageReactionRequest) {
|
||||
const whatsapp = this.manager.getSession(request.session);
|
||||
return whatsapp.setReaction(request);
|
||||
}
|
||||
|
||||
@Post('/reply')
|
||||
@ApiOperation({summary: 'Reply to a text message'})
|
||||
reply(@Body() message: MessageReply) {
|
||||
const whatsapp = this.whatsappSessionManager.getSession(message.sessionName)
|
||||
return whatsapp.reply(message.chatId, message.text, message.reply_to)
|
||||
}
|
||||
@Put('/star')
|
||||
@ApiOperation({ summary: 'Star or unstar a message' })
|
||||
async setStar(@Body() request: MessageStarRequest) {
|
||||
const whatsapp = this.manager.getSession(request.session);
|
||||
await whatsapp.setStar(request);
|
||||
return;
|
||||
}
|
||||
|
||||
@Post('/sendSeen')
|
||||
sendSeen(@Body() chat: ChatRequest) {
|
||||
const whatsapp = this.whatsappSessionManager.getSession(chat.sessionName)
|
||||
return whatsapp.sendSeen(chat.chatId)
|
||||
}
|
||||
@Post('/sendPoll')
|
||||
@ApiOperation({
|
||||
summary: 'Send a poll with options',
|
||||
description: 'You can use it as buttons or list replacement',
|
||||
})
|
||||
sendPoll(@Body() request: MessagePollRequest) {
|
||||
const whatsapp = this.manager.getSession(request.session);
|
||||
return whatsapp.sendPoll(request);
|
||||
}
|
||||
|
||||
@Post('/startTyping')
|
||||
startTyping(@Body() chat: ChatRequest) {
|
||||
// It's infinitive action
|
||||
const whatsapp = this.whatsappSessionManager.getSession(chat.sessionName)
|
||||
whatsapp.startTyping(chat.chatId)
|
||||
return true
|
||||
}
|
||||
@Post('/sendLocation')
|
||||
sendLocation(@Body() request: MessageLocationRequest) {
|
||||
const whatsapp = this.manager.getSession(request.session);
|
||||
return whatsapp.sendLocation(request);
|
||||
}
|
||||
|
||||
@Post('/stopTyping')
|
||||
stopTyping(@Body() chat: ChatRequest) {
|
||||
const whatsapp = this.whatsappSessionManager.getSession(chat.sessionName)
|
||||
whatsapp.stopTyping(chat.chatId)
|
||||
return true
|
||||
}
|
||||
@Post('/sendLinkPreview')
|
||||
sendLinkPreview(@Body() request: MessageLinkPreviewRequest) {
|
||||
const whatsapp = this.manager.getSession(request.session);
|
||||
return whatsapp.sendLinkPreview(request);
|
||||
}
|
||||
|
||||
@Get('/messages')
|
||||
@ApiOperation({ summary: 'Get messages in a chat' })
|
||||
getMessages(@Query() query: GetMessageQuery) {
|
||||
const whatsapp = this.manager.getSession(query.session);
|
||||
return whatsapp.getMessages(query);
|
||||
}
|
||||
|
||||
@Get('/sendText')
|
||||
@ApiOperation({ summary: 'Send a text message', deprecated: true })
|
||||
sendTextGet(@Query() query: MessageTextQuery) {
|
||||
const whatsapp = this.manager.getSession(query.session);
|
||||
const msg = new MessageTextRequest();
|
||||
msg.chatId = query.phone;
|
||||
msg.text = query.text;
|
||||
return whatsapp.sendText(msg);
|
||||
}
|
||||
|
||||
@Post('/sendContactVcard')
|
||||
sendContactVcard(@Body() request: MessageContactVcardRequest) {
|
||||
const whatsapp = this.manager.getSession(request.session);
|
||||
return whatsapp.sendContactVCard(request);
|
||||
}
|
||||
|
||||
@Get('/checkNumberStatus')
|
||||
@ApiOperation({
|
||||
summary: 'Check number status',
|
||||
description: 'DEPRECATED. Use "POST /contacts/check-exists" instead',
|
||||
deprecated: true,
|
||||
})
|
||||
async DEPRECATED_checkNumberStatus(
|
||||
@Query() request: CheckNumberStatusQuery,
|
||||
): Promise<WANumberExistResult> {
|
||||
const whatsapp = this.manager.getSession(request.session);
|
||||
return whatsapp.checkNumberStatus(request);
|
||||
}
|
||||
|
||||
@Post('/reply')
|
||||
@ApiOperation({
|
||||
summary:
|
||||
'DEPRECATED - you can set "reply_to" field when sending text, image, etc',
|
||||
deprecated: true,
|
||||
})
|
||||
reply(@Body() request: MessageReplyRequest) {
|
||||
const whatsapp = this.manager.getSession(request.session);
|
||||
return whatsapp.reply(request);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
import { Body, Controller, Get, Post, Query } from '@nestjs/common';
|
||||
import { ApiOperation, ApiSecurity, ApiTags } from '@nestjs/swagger';
|
||||
|
||||
import { SessionManager } from '../core/abc/manager.abc';
|
||||
import { SessionQuery } from '../structures/base.dto';
|
||||
import {
|
||||
CheckNumberStatusQuery,
|
||||
WANumberExistResult,
|
||||
} from '../structures/chatting.dto';
|
||||
import { ContactQuery, ContactRequest } from '../structures/contacts.dto';
|
||||
|
||||
@ApiSecurity('api_key')
|
||||
@Controller('api/contacts')
|
||||
@ApiTags('👤 Contacts')
|
||||
export class ContactsController {
|
||||
constructor(private manager: SessionManager) {}
|
||||
|
||||
@Get('/')
|
||||
@ApiOperation({
|
||||
summary: 'Get contact basic info',
|
||||
description:
|
||||
'The method always return result, even if the phone number is not registered in WhatsApp. For that - use /contacts/check-exists endpoint below.',
|
||||
})
|
||||
get(@Query() query: ContactQuery) {
|
||||
const whatsapp = this.manager.getSession(query.session);
|
||||
return whatsapp.getContact(query);
|
||||
}
|
||||
|
||||
@Get('/all')
|
||||
@ApiOperation({ summary: 'Get all contacts' })
|
||||
getAll(@Query() query: SessionQuery) {
|
||||
const whatsapp = this.manager.getSession(query.session);
|
||||
return whatsapp.getContacts();
|
||||
}
|
||||
|
||||
@Get('/check-exists')
|
||||
@ApiOperation({ summary: 'Check phone number is registered in WhatsApp.' })
|
||||
async checkExists(
|
||||
@Query() request: CheckNumberStatusQuery,
|
||||
): Promise<WANumberExistResult> {
|
||||
const whatsapp = this.manager.getSession(request.session);
|
||||
return whatsapp.checkNumberStatus(request);
|
||||
}
|
||||
|
||||
@Get('/about')
|
||||
@ApiOperation({
|
||||
summary: 'Gets the Contact\'s "about" info',
|
||||
description:
|
||||
'Returns null if you do not have permission to read their status.',
|
||||
})
|
||||
getAbout(@Query() query: ContactQuery) {
|
||||
const whatsapp = this.manager.getSession(query.session);
|
||||
return whatsapp.getContactAbout(query);
|
||||
}
|
||||
|
||||
@Get('/profile-picture')
|
||||
@ApiOperation({
|
||||
summary: "Get contact's profile picture URL",
|
||||
description:
|
||||
'If privacy settings do not allow to get the picture, the method will return null.',
|
||||
})
|
||||
getProfilePicture(@Query() query: ContactQuery) {
|
||||
const whatsapp = this.manager.getSession(query.session);
|
||||
return whatsapp.getContactProfilePicture(query);
|
||||
}
|
||||
|
||||
@Post('/block')
|
||||
@ApiOperation({ summary: 'Block contact' })
|
||||
block(@Body() request: ContactRequest) {
|
||||
const whatsapp = this.manager.getSession(request.session);
|
||||
return whatsapp.blockContact(request);
|
||||
}
|
||||
|
||||
@Post('/unblock')
|
||||
@ApiOperation({ summary: 'Unblock contact' })
|
||||
unblock(@Body() request: ContactRequest) {
|
||||
const whatsapp = this.manager.getSession(request.session);
|
||||
return whatsapp.unblockContact(request);
|
||||
}
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
import {Body, Controller, Get, Post} from '@nestjs/common';
|
||||
import {ApiTags} from "@nestjs/swagger";
|
||||
import {SessionRequest} from "./all.dto";
|
||||
import {WhatsappSessionManager} from "../whatsapp.service";
|
||||
|
||||
|
||||
@Controller('api')
|
||||
@ApiTags('device')
|
||||
export class DeviceController {
|
||||
|
||||
constructor(private whatsappSessionManager: WhatsappSessionManager) {
|
||||
}
|
||||
|
||||
|
||||
@Post('/killServiceWorker')
|
||||
killServiceWorker(@Body() request: SessionRequest) {
|
||||
const whatsapp = this.whatsappSessionManager.getSession(request.sessionName)
|
||||
return whatsapp.killServiceWorker()
|
||||
}
|
||||
|
||||
@Post('/restartService')
|
||||
restartService(@Body() request: SessionRequest) {
|
||||
const whatsapp = this.whatsappSessionManager.getSession(request.sessionName)
|
||||
return whatsapp.restartService()
|
||||
}
|
||||
|
||||
@Get('/getHostDevice')
|
||||
getHostDevice(@Body() request: SessionRequest) {
|
||||
const whatsapp = this.whatsappSessionManager.getSession(request.sessionName)
|
||||
return whatsapp.getHostDevice()
|
||||
}
|
||||
|
||||
@Get('/getConnectionState')
|
||||
getConnectionState(@Body() request: SessionRequest) {
|
||||
const whatsapp = this.whatsappSessionManager.getSession(request.sessionName)
|
||||
return whatsapp.getConnectionState()
|
||||
}
|
||||
|
||||
@Get('/getBatteryLevel')
|
||||
getBatteryLevel(@Body() request: SessionRequest) {
|
||||
const whatsapp = this.whatsappSessionManager.getSession(request.sessionName)
|
||||
return whatsapp.getBatteryLevel()
|
||||
}
|
||||
|
||||
@Get('/isConnected')
|
||||
isConnected(@Body() request: SessionRequest) {
|
||||
const whatsapp = this.whatsappSessionManager.getSession(request.sessionName)
|
||||
return whatsapp.isConnected()
|
||||
}
|
||||
|
||||
@Get('/getWAVersion')
|
||||
getWAVersion(@Body() request: SessionRequest) {
|
||||
const whatsapp = this.whatsappSessionManager.getSession(request.sessionName)
|
||||
return whatsapp.getWAVersion()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
import {ArgumentsHost, Catch, ExceptionFilter, HttpException, HttpStatus,} from '@nestjs/common';
|
||||
import {Request, Response} from 'express';
|
||||
|
||||
@Catch()
|
||||
export class AllExceptionsFilter implements ExceptionFilter {
|
||||
catch(exception: HttpException | Error, host: ArgumentsHost): void {
|
||||
const ctx = host.switchToHttp();
|
||||
const response = ctx.getResponse<Response>();
|
||||
const request = ctx.getRequest<Request>();
|
||||
|
||||
if (exception instanceof HttpException) {
|
||||
response
|
||||
.status(exception.getStatus())
|
||||
.json(exception.getResponse());
|
||||
return
|
||||
}
|
||||
|
||||
const httpStatus = HttpStatus.INTERNAL_SERVER_ERROR
|
||||
response
|
||||
.status(httpStatus)
|
||||
.json({
|
||||
statusCode: httpStatus,
|
||||
timestamp: new Date().toISOString(),
|
||||
path: request.url,
|
||||
error: exception.message,
|
||||
stack: exception.stack,
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,252 @@
|
||||
import {
|
||||
Body,
|
||||
Controller,
|
||||
Delete,
|
||||
Get,
|
||||
Param,
|
||||
Post,
|
||||
Put,
|
||||
} from '@nestjs/common';
|
||||
import { ApiOperation, ApiSecurity, ApiTags } from '@nestjs/swagger';
|
||||
import { GroupIdApiParam } from '@waha/nestjs/params/ChatIdApiParam';
|
||||
import {
|
||||
SessionApiParam,
|
||||
SessionParam,
|
||||
} from '@waha/nestjs/params/SessionApiParam';
|
||||
|
||||
import { SessionManager } from '../core/abc/manager.abc';
|
||||
import { WhatsappSession } from '../core/abc/session.abc';
|
||||
import {
|
||||
CreateGroupRequest,
|
||||
DescriptionRequest,
|
||||
ParticipantsRequest,
|
||||
SettingsSecurityChangeInfo,
|
||||
SubjectRequest,
|
||||
} from '../structures/groups.dto';
|
||||
|
||||
@ApiSecurity('api_key')
|
||||
@Controller('api/:session/groups')
|
||||
@ApiTags('👥 Groups')
|
||||
export class GroupsController {
|
||||
constructor(private manager: SessionManager) {}
|
||||
|
||||
@Post('')
|
||||
@SessionApiParam
|
||||
@ApiOperation({ summary: 'Create a new group.' })
|
||||
createGroup(
|
||||
@SessionParam session: WhatsappSession,
|
||||
@Body() request: CreateGroupRequest,
|
||||
) {
|
||||
return session.createGroup(request);
|
||||
}
|
||||
|
||||
@Get('')
|
||||
@SessionApiParam
|
||||
@ApiOperation({ summary: 'Get all groups.' })
|
||||
getGroups(@SessionParam session: WhatsappSession) {
|
||||
return session.getGroups();
|
||||
}
|
||||
|
||||
@Get(':id')
|
||||
@GroupIdApiParam
|
||||
@SessionApiParam
|
||||
@ApiOperation({ summary: 'Get the group.' })
|
||||
getGroup(@SessionParam session: WhatsappSession, @Param('id') id: string) {
|
||||
return session.getGroup(id);
|
||||
}
|
||||
|
||||
@Put(':id/settings/security/info-admin-only')
|
||||
@SessionApiParam
|
||||
@GroupIdApiParam
|
||||
@ApiOperation({
|
||||
summary: 'Updates the group "info admin only" settings.',
|
||||
description:
|
||||
'You can allow only admins to edit group info (title, description, photo).',
|
||||
})
|
||||
setInfoAdminOnly(
|
||||
@SessionParam session: WhatsappSession,
|
||||
@Param('id') id: string,
|
||||
@Body() request: SettingsSecurityChangeInfo,
|
||||
) {
|
||||
return session.setInfoAdminsOnly(id, request.adminsOnly);
|
||||
}
|
||||
|
||||
@Get(':id/settings/security/info-admin-only')
|
||||
@SessionApiParam
|
||||
@GroupIdApiParam
|
||||
@ApiOperation({
|
||||
summary: "Get the group's 'info admin only' settings.",
|
||||
description:
|
||||
'You can allow only admins to edit group info (title, description, photo).',
|
||||
})
|
||||
getInfoAdminOnly(
|
||||
@SessionParam session: WhatsappSession,
|
||||
@Param('id') id: string,
|
||||
): Promise<SettingsSecurityChangeInfo> {
|
||||
return session.getInfoAdminsOnly(id);
|
||||
}
|
||||
|
||||
@Put(':id/settings/security/messages-admin-only')
|
||||
@SessionApiParam
|
||||
@GroupIdApiParam
|
||||
@ApiOperation({
|
||||
summary: 'Update settings - who can send messages',
|
||||
description:
|
||||
'Updates the group settings to only allow admins to send messages.',
|
||||
})
|
||||
setMessagesAdminOnly(
|
||||
@SessionParam session: WhatsappSession,
|
||||
@Param('id') id: string,
|
||||
@Body() request: SettingsSecurityChangeInfo,
|
||||
) {
|
||||
return session.setMessagesAdminsOnly(id, request.adminsOnly);
|
||||
}
|
||||
|
||||
@Get(':id/settings/security/messages-admin-only')
|
||||
@SessionApiParam
|
||||
@GroupIdApiParam
|
||||
@ApiOperation({
|
||||
summary: 'Get settings - who can send messages',
|
||||
description: 'The group settings to only allow admins to send messages.',
|
||||
})
|
||||
getMessagesAdminOnly(
|
||||
@SessionParam session: WhatsappSession,
|
||||
@Param('id') id: string,
|
||||
): Promise<SettingsSecurityChangeInfo> {
|
||||
return session.getMessagesAdminsOnly(id);
|
||||
}
|
||||
|
||||
@Delete(':id')
|
||||
@SessionApiParam
|
||||
@GroupIdApiParam
|
||||
@ApiOperation({ summary: 'Delete the group.' })
|
||||
deleteGroup(@SessionParam session: WhatsappSession, @Param('id') id: string) {
|
||||
return session.deleteGroup(id);
|
||||
}
|
||||
|
||||
@Post(':id/leave')
|
||||
@SessionApiParam
|
||||
@GroupIdApiParam
|
||||
@ApiOperation({ summary: 'Leave the group.' })
|
||||
leaveGroup(@SessionParam session: WhatsappSession, @Param('id') id: string) {
|
||||
return session.leaveGroup(id);
|
||||
}
|
||||
|
||||
@Put(':id/description')
|
||||
@ApiOperation({
|
||||
summary: 'Updates the group description.',
|
||||
description:
|
||||
'Returns "true" if the subject was properly updated. This can return "false" if the user does not have the necessary permissions.',
|
||||
})
|
||||
@SessionApiParam
|
||||
@GroupIdApiParam
|
||||
setDescription(
|
||||
@SessionParam session: WhatsappSession,
|
||||
@Param('id') id: string,
|
||||
@Body() request: DescriptionRequest,
|
||||
) {
|
||||
return session.setDescription(id, request.description);
|
||||
}
|
||||
|
||||
@Put(':id/subject')
|
||||
@SessionApiParam
|
||||
@GroupIdApiParam
|
||||
@ApiOperation({
|
||||
summary: 'Updates the group subject',
|
||||
description:
|
||||
'Returns "true" if the subject was properly updated. This can return "false" if the user does not have the necessary permissions.',
|
||||
})
|
||||
setSubject(
|
||||
@SessionParam session: WhatsappSession,
|
||||
@Param('id') id: string,
|
||||
@Body() request: SubjectRequest,
|
||||
) {
|
||||
return session.setSubject(id, request.subject);
|
||||
}
|
||||
|
||||
@Get(':id/invite-code')
|
||||
@SessionApiParam
|
||||
@GroupIdApiParam
|
||||
@ApiOperation({ summary: 'Gets the invite code for the group.' })
|
||||
getInviteCode(
|
||||
@SessionParam session: WhatsappSession,
|
||||
@Param('id') id: string,
|
||||
): Promise<string> {
|
||||
return session.getInviteCode(id);
|
||||
}
|
||||
|
||||
@Post(':id/invite-code/revoke')
|
||||
@SessionApiParam
|
||||
@GroupIdApiParam
|
||||
@ApiOperation({
|
||||
summary:
|
||||
'Invalidates the current group invite code and generates a new one.',
|
||||
})
|
||||
revokeInviteCode(
|
||||
@SessionParam session: WhatsappSession,
|
||||
@Param('id') id: string,
|
||||
): Promise<string> {
|
||||
return session.revokeInviteCode(id);
|
||||
}
|
||||
|
||||
@Get(':id/participants/')
|
||||
@SessionApiParam
|
||||
@GroupIdApiParam
|
||||
@ApiOperation({ summary: 'Get participants' })
|
||||
getParticipants(
|
||||
@SessionParam session: WhatsappSession,
|
||||
@Param('id') id: string,
|
||||
) {
|
||||
return session.getParticipants(id);
|
||||
}
|
||||
|
||||
@Post(':id/participants/add')
|
||||
@SessionApiParam
|
||||
@GroupIdApiParam
|
||||
@ApiOperation({ summary: 'Add participants' })
|
||||
addParticipants(
|
||||
@SessionParam session: WhatsappSession,
|
||||
@Param('id') id: string,
|
||||
@Body() request: ParticipantsRequest,
|
||||
) {
|
||||
return session.addParticipants(id, request);
|
||||
}
|
||||
|
||||
@Post(':id/participants/remove')
|
||||
@SessionApiParam
|
||||
@GroupIdApiParam
|
||||
@ApiOperation({
|
||||
summary: 'Remove participants',
|
||||
})
|
||||
removeParticipants(
|
||||
@SessionParam session: WhatsappSession,
|
||||
@Param('id') id: string,
|
||||
@Body() request: ParticipantsRequest,
|
||||
) {
|
||||
return session.removeParticipants(id, request);
|
||||
}
|
||||
|
||||
@Post(':id/admin/promote')
|
||||
@SessionApiParam
|
||||
@GroupIdApiParam
|
||||
@ApiOperation({ summary: 'Promote participants to admin users.' })
|
||||
promoteToAdmin(
|
||||
@SessionParam session: WhatsappSession,
|
||||
@Param('id') id: string,
|
||||
@Body() request: ParticipantsRequest,
|
||||
) {
|
||||
return session.promoteParticipantsToAdmin(id, request);
|
||||
}
|
||||
|
||||
@Post(':id/admin/demote')
|
||||
@SessionApiParam
|
||||
@GroupIdApiParam
|
||||
@ApiOperation({ summary: 'Demotes participants to regular users.' })
|
||||
demoteToAdmin(
|
||||
@SessionParam session: WhatsappSession,
|
||||
@Param('id') id: string,
|
||||
@Body() request: ParticipantsRequest,
|
||||
) {
|
||||
return session.demoteParticipantsToUser(id, request);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import { Controller, Get } from '@nestjs/common';
|
||||
import { ApiOperation, ApiSecurity, ApiTags } from '@nestjs/swagger';
|
||||
import { HealthCheck } from '@nestjs/terminus';
|
||||
|
||||
import { WAHAHealthCheckService } from '../core/abc/WAHAHealthCheckService';
|
||||
|
||||
@ApiSecurity('api_key')
|
||||
@Controller('health')
|
||||
@ApiTags('🔍 Observability')
|
||||
export class HealthController {
|
||||
constructor(private wahaHealth: WAHAHealthCheckService) {}
|
||||
|
||||
@Get()
|
||||
@HealthCheck()
|
||||
@ApiOperation({
|
||||
summary: 'Check the health of the server',
|
||||
description:
|
||||
"Perform all health checks and return the server's health status.",
|
||||
})
|
||||
async check() {
|
||||
return this.wahaHealth.check();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
import { Body, Controller, Get, Param, Put } from '@nestjs/common';
|
||||
import { ApiOperation, ApiSecurity, ApiTags } from '@nestjs/swagger';
|
||||
import { WhatsappSession } from '@waha/core/abc/session.abc';
|
||||
import { ChatIdApiParam } from '@waha/nestjs/params/ChatIdApiParam';
|
||||
import {
|
||||
SessionApiParam,
|
||||
SessionParam,
|
||||
} from '@waha/nestjs/params/SessionApiParam';
|
||||
import { Label, SetLabelsRequest } from '@waha/structures/labels.dto';
|
||||
|
||||
import { SessionManager } from '../core/abc/manager.abc';
|
||||
|
||||
@ApiSecurity('api_key')
|
||||
@Controller('api/:session/labels')
|
||||
@ApiTags('🏷️ Labels')
|
||||
export class LabelsController {
|
||||
constructor(private manager: SessionManager) {}
|
||||
|
||||
@Get('/')
|
||||
@SessionApiParam
|
||||
@ApiOperation({ summary: 'Get all labels' })
|
||||
getAll(@SessionParam session: WhatsappSession): Promise<Label[]> {
|
||||
return session.getLabels();
|
||||
}
|
||||
|
||||
@Get('/chats/:chatId')
|
||||
@SessionApiParam
|
||||
@ChatIdApiParam
|
||||
@ApiOperation({ summary: 'Get labels for the chat' })
|
||||
getChatLabels(
|
||||
@SessionParam session: WhatsappSession,
|
||||
@Param('chatId') chatId: string,
|
||||
): Promise<Label[]> {
|
||||
return session.getChatLabels(chatId);
|
||||
}
|
||||
|
||||
@Put('/chats/:chatId')
|
||||
@SessionApiParam
|
||||
@ChatIdApiParam
|
||||
@ApiOperation({ summary: 'Save labels for the chat' })
|
||||
putChatLabels(
|
||||
@SessionParam session: WhatsappSession,
|
||||
@Param('chatId') chatId: string,
|
||||
@Body() request: SetLabelsRequest,
|
||||
) {
|
||||
return session.putLabelsToChat(chatId, request.labels);
|
||||
}
|
||||
|
||||
@Get('/:labelId/chats')
|
||||
@SessionApiParam
|
||||
@ApiOperation({ summary: 'Get chats by label' })
|
||||
getChatsByLabel(
|
||||
@SessionParam session: WhatsappSession,
|
||||
@Param('labelId') labelId: string,
|
||||
) {
|
||||
return session.getChatsByLabelId(labelId);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
import { Controller, Get } from '@nestjs/common';
|
||||
import { ApiOperation, ApiSecurity, ApiTags } from '@nestjs/swagger';
|
||||
import { PingResponse } from '@waha/structures/ping.dto';
|
||||
|
||||
@ApiSecurity('api_key')
|
||||
@Controller('ping')
|
||||
@ApiTags('🔍 Observability')
|
||||
export class PingController {
|
||||
@Get()
|
||||
@ApiOperation({
|
||||
summary: 'Ping the server',
|
||||
description: 'Check if the server is alive and responding to requests.',
|
||||
})
|
||||
ping(): PingResponse {
|
||||
return { message: 'pong' };
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
import {
|
||||
BadRequestException,
|
||||
Body,
|
||||
Controller,
|
||||
Get,
|
||||
Param,
|
||||
Post,
|
||||
} from '@nestjs/common';
|
||||
import { ApiOperation, ApiSecurity, ApiTags } from '@nestjs/swagger';
|
||||
import { ChatIdApiParam } from '@waha/nestjs/params/ChatIdApiParam';
|
||||
import {
|
||||
SessionApiParam,
|
||||
SessionParam,
|
||||
} from '@waha/nestjs/params/SessionApiParam';
|
||||
|
||||
import { SessionManager } from '../core/abc/manager.abc';
|
||||
import { WhatsappSession } from '../core/abc/session.abc';
|
||||
import { WAHAPresenceStatus } from '../structures/enums.dto';
|
||||
import {
|
||||
WAHAChatPresences,
|
||||
WAHASessionPresence,
|
||||
} from '../structures/presence.dto';
|
||||
|
||||
@ApiSecurity('api_key')
|
||||
@Controller('api/:session/presence')
|
||||
@ApiTags('✅ Presence')
|
||||
export class PresenceController {
|
||||
constructor(private manager: SessionManager) {}
|
||||
|
||||
@Post('')
|
||||
@SessionApiParam
|
||||
@ApiOperation({ summary: 'Set session presence' })
|
||||
setPresence(
|
||||
@SessionParam session: WhatsappSession,
|
||||
@Body() request: WAHASessionPresence,
|
||||
) {
|
||||
// Validate request
|
||||
const presencesWithoutChatId = [
|
||||
WAHAPresenceStatus.ONLINE,
|
||||
WAHAPresenceStatus.OFFLINE,
|
||||
];
|
||||
const requiresNoChatId = presencesWithoutChatId.includes(request.presence);
|
||||
const requiresChatId = !requiresNoChatId;
|
||||
|
||||
if (requiresNoChatId && request.chatId) {
|
||||
const msg = {
|
||||
detail: `'${request.presence}' presence works on the global scope and doesn't require 'chatId' field.`,
|
||||
};
|
||||
throw new BadRequestException(msg);
|
||||
} else if (requiresChatId && !request.chatId) {
|
||||
const msg = {
|
||||
detail: `'${request.presence}' presence requires 'chatId' field.`,
|
||||
};
|
||||
throw new BadRequestException(msg);
|
||||
}
|
||||
|
||||
return session.setPresence(request.presence, request.chatId);
|
||||
}
|
||||
|
||||
@Get('')
|
||||
@SessionApiParam
|
||||
@ApiOperation({ summary: 'Get all subscribed presence information.' })
|
||||
getPresenceAll(
|
||||
@SessionParam session: WhatsappSession,
|
||||
): Promise<WAHAChatPresences[]> {
|
||||
return session.getPresences();
|
||||
}
|
||||
|
||||
@Get(':chatId')
|
||||
@SessionApiParam
|
||||
@ChatIdApiParam
|
||||
@ApiOperation({
|
||||
summary:
|
||||
"Get the presence for the chat id. If it hasn't been subscribed - it also subscribes to it.",
|
||||
})
|
||||
getPresence(
|
||||
@SessionParam session: WhatsappSession,
|
||||
@Param('chatId') chatId: string,
|
||||
): Promise<WAHAChatPresences> {
|
||||
return session.getPresence(chatId);
|
||||
}
|
||||
|
||||
@Post(':chatId/subscribe')
|
||||
@SessionApiParam
|
||||
@ChatIdApiParam
|
||||
@ApiOperation({
|
||||
summary: 'Subscribe to presence events for the chat.',
|
||||
})
|
||||
subscribe(
|
||||
@SessionParam session: WhatsappSession,
|
||||
@Param('chatId') chatId: string,
|
||||
): Promise<void> {
|
||||
return session.subscribePresence(chatId);
|
||||
}
|
||||
}
|
||||
@@ -1,33 +1,33 @@
|
||||
import {Controller, Get, Query, Res} from '@nestjs/common';
|
||||
import {ApiTags} from "@nestjs/swagger";
|
||||
import {Readable} from "stream";
|
||||
import {Response} from 'express';
|
||||
import {WhatsappSessionManager} from "../whatsapp.service";
|
||||
import {SessionQuery} from "./all.dto";
|
||||
import {
|
||||
Controller,
|
||||
Get,
|
||||
Query,
|
||||
Res,
|
||||
StreamableFile,
|
||||
UseInterceptors,
|
||||
} from '@nestjs/common';
|
||||
import { ApiSecurity, ApiTags } from '@nestjs/swagger';
|
||||
import { ApiFileAcceptHeader } from '@waha/nestjs/ApiFileAcceptHeader';
|
||||
import { Response } from 'express';
|
||||
|
||||
import { SessionManager } from '../core/abc/manager.abc';
|
||||
import { BufferResponseInterceptor } from '../nestjs/BufferResponseInterceptor';
|
||||
import { SessionQuery } from '../structures/base.dto';
|
||||
|
||||
@ApiSecurity('api_key')
|
||||
@Controller('api')
|
||||
@ApiTags('screenshot')
|
||||
@ApiTags('🖼️ Screenshot')
|
||||
export class ScreenshotController {
|
||||
constructor(private whatsappSessionManager: WhatsappSessionManager) {
|
||||
}
|
||||
constructor(private manager: SessionManager) {}
|
||||
|
||||
@Get('/screenshot')
|
||||
async screenshot(
|
||||
@Res() res: Response,
|
||||
@Query() sessionQuery: SessionQuery,
|
||||
) {
|
||||
const whatsappService = this.whatsappSessionManager.getService(sessionQuery.sessionName)
|
||||
const buffer = await whatsappService.getScreenshotOrQRCode();
|
||||
const stream = new Readable();
|
||||
stream.push(buffer);
|
||||
stream.push(null);
|
||||
|
||||
res.set({
|
||||
'Content-Type': 'image/png',
|
||||
'Content-Length': buffer.length,
|
||||
});
|
||||
stream.pipe(res)
|
||||
}
|
||||
@Get('/screenshot')
|
||||
@UseInterceptors(new BufferResponseInterceptor())
|
||||
@ApiFileAcceptHeader()
|
||||
async screenshot(
|
||||
@Res({ passthrough: true }) res: Response,
|
||||
@Query() sessionQuery: SessionQuery,
|
||||
) {
|
||||
const whatsappService = this.manager.getSession(sessionQuery.session);
|
||||
return await whatsappService.getScreenshot();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,144 @@
|
||||
import * as process from 'node:process';
|
||||
import * as v8 from 'node:v8';
|
||||
|
||||
import {
|
||||
Body,
|
||||
Controller,
|
||||
Get,
|
||||
Logger,
|
||||
NotFoundException,
|
||||
Post,
|
||||
Query,
|
||||
Res,
|
||||
StreamableFile,
|
||||
UseInterceptors,
|
||||
UsePipes,
|
||||
} from '@nestjs/common';
|
||||
import { ApiOperation, ApiSecurity, ApiTags } from '@nestjs/swagger';
|
||||
import { WhatsappConfigService } from '@waha/config.service';
|
||||
import { getApp } from '@waha/main';
|
||||
import { WAHAValidationPipe } from '@waha/nestjs/pipes/WAHAValidationPipe';
|
||||
import { WAHAEnvironment } from '@waha/structures/environment.dto';
|
||||
import {
|
||||
EnvironmentQuery,
|
||||
ServerStatusResponse,
|
||||
StopRequest,
|
||||
} from '@waha/structures/server.dto';
|
||||
import { VERSION } from '@waha/version';
|
||||
import * as lodash from 'lodash';
|
||||
|
||||
@ApiSecurity('api_key')
|
||||
@Controller('api/server')
|
||||
@ApiTags('🔍 Observability')
|
||||
export class ServerController {
|
||||
private logger: Logger;
|
||||
|
||||
constructor() {
|
||||
this.logger = new Logger('ServerController');
|
||||
}
|
||||
|
||||
@Get('version')
|
||||
@ApiOperation({ summary: 'Get the version of the server' })
|
||||
get(): WAHAEnvironment {
|
||||
return VERSION;
|
||||
}
|
||||
|
||||
@Get('environment')
|
||||
@ApiOperation({ summary: 'Get the server environment' })
|
||||
environment(
|
||||
@Query(new WAHAValidationPipe()) query: EnvironmentQuery,
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||
): object {
|
||||
let result = process.env;
|
||||
if (!query.all) {
|
||||
result = lodash.pickBy(result, (value, key) => {
|
||||
return (
|
||||
key.startsWith('WAHA_') ||
|
||||
key.startsWith('WHATSAPP_') ||
|
||||
key === 'DEBUG'
|
||||
);
|
||||
});
|
||||
}
|
||||
const map = new Map<string, string>();
|
||||
// sort and set
|
||||
Object.keys(result)
|
||||
.sort()
|
||||
.forEach((key) => {
|
||||
map.set(key, result[key]);
|
||||
});
|
||||
return Object.fromEntries(map);
|
||||
}
|
||||
|
||||
@Get('status')
|
||||
@ApiOperation({ summary: 'Get the server status' })
|
||||
async status(): Promise<ServerStatusResponse> {
|
||||
const now = Date.now();
|
||||
const uptime = Math.floor(process.uptime() * 1000);
|
||||
const startTimestamp = now - uptime;
|
||||
return {
|
||||
startTimestamp: startTimestamp,
|
||||
uptime: uptime,
|
||||
};
|
||||
}
|
||||
|
||||
@Post('stop')
|
||||
@ApiOperation({
|
||||
summary: 'Stop (and restart) the server',
|
||||
description:
|
||||
"If you're using docker, after calling this endpoint Docker will start a new container, " +
|
||||
'so you can use this endpoint to restart the server',
|
||||
})
|
||||
@UsePipes(new WAHAValidationPipe())
|
||||
async stop(@Body() request: StopRequest) {
|
||||
const timeout = 1_000;
|
||||
if (request.force) {
|
||||
this.logger.log(`Force stopping the server in ${timeout}ms`);
|
||||
setTimeout(() => {
|
||||
this.logger.log('Force stopping the server');
|
||||
process.kill(process.pid, 'SIGKILL');
|
||||
}, timeout);
|
||||
} else {
|
||||
this.logger.log(`Gracefully stopping the server in ${timeout}ms`);
|
||||
setTimeout(async () => {
|
||||
this.logger.log('Gracefully closing the application...');
|
||||
const app = getApp();
|
||||
if (app) {
|
||||
await app.close();
|
||||
}
|
||||
this.logger.log('Application closed');
|
||||
process.exit(0);
|
||||
}, timeout);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ApiSecurity('api_key')
|
||||
@Controller('api/server/debug')
|
||||
@ApiTags('🔍 Observability')
|
||||
export class ServerDebugController {
|
||||
private logger: Logger;
|
||||
private readonly enabled: boolean;
|
||||
|
||||
constructor(protected config: WhatsappConfigService) {
|
||||
this.logger = new Logger('ServerDebugController');
|
||||
this.enabled = this.config.debugModeEnabled;
|
||||
}
|
||||
|
||||
@Get('heapsnapshot')
|
||||
@ApiOperation({
|
||||
summary: 'Return a heapsnapshot',
|
||||
description: "Return a heapsnapshot of the server's memory",
|
||||
})
|
||||
async heapsnapshot() {
|
||||
if (!this.enabled) {
|
||||
throw new NotFoundException('WAHA_DEBUG_MODE is disabled');
|
||||
}
|
||||
this.logger.log('Creating a heap snapshot...');
|
||||
const heap = v8.getHeapSnapshot();
|
||||
const fileName = `${Date.now()}.heapsnapshot`;
|
||||
return new StreamableFile(heap, {
|
||||
type: 'application/octet-stream',
|
||||
disposition: `attachment; filename=${fileName}`,
|
||||
});
|
||||
}
|
||||
}
|
||||
+291
-20
@@ -1,30 +1,301 @@
|
||||
import {Body, Controller, Get, Post} from '@nestjs/common';
|
||||
import {ApiTags} from "@nestjs/swagger";
|
||||
import {WhatsappSessionManager} from "../whatsapp.service";
|
||||
import {SessionRequest} from "./all.dto";
|
||||
import {
|
||||
Body,
|
||||
Controller,
|
||||
Delete,
|
||||
Get,
|
||||
NotFoundException,
|
||||
Param,
|
||||
Post,
|
||||
Put,
|
||||
Query,
|
||||
UsePipes,
|
||||
} from '@nestjs/common';
|
||||
import { UnprocessableEntityException } from '@nestjs/common/exceptions/unprocessable-entity.exception';
|
||||
import { ApiOperation, ApiSecurity, ApiTags } from '@nestjs/swagger';
|
||||
import {
|
||||
SessionApiParam,
|
||||
SessionParam,
|
||||
} from '@waha/nestjs/params/SessionApiParam';
|
||||
import { WAHAValidationPipe } from '@waha/nestjs/pipes/WAHAValidationPipe';
|
||||
import { WAHASessionStatus } from '@waha/structures/enums.dto';
|
||||
import {
|
||||
SessionLogoutDeprecatedRequest,
|
||||
SessionStartDeprecatedRequest,
|
||||
SessionStopDeprecatedRequest,
|
||||
} from '@waha/structures/sessions.deprecated.dto';
|
||||
import { generatePrefixedId } from '@waha/utils/ids';
|
||||
import { sleep } from '@waha/utils/promiseTimeout';
|
||||
|
||||
import { SessionManager } from '../core/abc/manager.abc';
|
||||
import { WhatsappSession } from '../core/abc/session.abc';
|
||||
import {
|
||||
ListSessionsQuery,
|
||||
MeInfo,
|
||||
SessionCreateRequest,
|
||||
SessionDTO,
|
||||
SessionInfo,
|
||||
SessionUpdateRequest,
|
||||
} from '../structures/sessions.dto';
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const AsyncLock = require('async-lock');
|
||||
|
||||
@ApiSecurity('api_key')
|
||||
@Controller('api/sessions')
|
||||
@ApiTags('sessions')
|
||||
export class SessionsController {
|
||||
constructor(private whatsappSessionManager: WhatsappSessionManager) {
|
||||
@ApiTags('🖥️ Sessions')
|
||||
class SessionsController {
|
||||
private lock: any;
|
||||
|
||||
constructor(private manager: SessionManager) {
|
||||
this.lock = new AsyncLock({ maxPending: Infinity });
|
||||
}
|
||||
|
||||
private withLock(name: string, fn: () => any) {
|
||||
return this.lock.acquire(name, fn);
|
||||
}
|
||||
|
||||
@Get('/')
|
||||
@ApiOperation({ summary: 'List all sessions' })
|
||||
list(
|
||||
@Query(new WAHAValidationPipe()) query: ListSessionsQuery,
|
||||
): Promise<SessionInfo[]> {
|
||||
return this.manager.getSessions(query.all);
|
||||
}
|
||||
|
||||
@Get('/:session')
|
||||
@ApiOperation({ summary: 'Get session information' })
|
||||
@SessionApiParam
|
||||
@UsePipes(new WAHAValidationPipe())
|
||||
async get(@Param('session') name: string): Promise<SessionInfo> {
|
||||
const session = this.manager.getSessionInfo(name);
|
||||
if (session === null) {
|
||||
throw new NotFoundException('Session not found');
|
||||
}
|
||||
return session;
|
||||
}
|
||||
|
||||
@Get(':session/me')
|
||||
@SessionApiParam
|
||||
@ApiOperation({ summary: 'Get information about the authenticated account' })
|
||||
getMe(@SessionParam session: WhatsappSession): MeInfo | null {
|
||||
return session.getSessionMeInfo();
|
||||
}
|
||||
|
||||
@Post('')
|
||||
@ApiOperation({
|
||||
summary: 'Create a session',
|
||||
description:
|
||||
'Create session a new session (and start it at the same time if required).',
|
||||
})
|
||||
@UsePipes(new WAHAValidationPipe())
|
||||
async create(@Body() request: SessionCreateRequest): Promise<SessionDTO> {
|
||||
const name = request.name || generatePrefixedId('session');
|
||||
await this.withLock(name, async () => {
|
||||
if (await this.manager.exists(name)) {
|
||||
const msg = `Session '${name}' already exists. Use PUT to update it.`;
|
||||
throw new UnprocessableEntityException(msg);
|
||||
}
|
||||
|
||||
const config = request.config;
|
||||
const start = request.start || false;
|
||||
await this.manager.upsert(name, config);
|
||||
if (start) {
|
||||
await this.manager.start(name);
|
||||
}
|
||||
});
|
||||
return await this.manager.getSessionInfo(name);
|
||||
}
|
||||
|
||||
@Put(':session')
|
||||
@ApiOperation({
|
||||
summary: 'Update a session',
|
||||
description: '',
|
||||
})
|
||||
@SessionApiParam
|
||||
@UsePipes(new WAHAValidationPipe())
|
||||
async update(
|
||||
@Param('session') name: string,
|
||||
@Body() request: SessionUpdateRequest,
|
||||
): Promise<SessionDTO> {
|
||||
await this.withLock(name, async () => {
|
||||
if (!(await this.manager.exists(name))) {
|
||||
throw new NotFoundException('Session not found');
|
||||
}
|
||||
const config = request.config;
|
||||
const isRunning = this.manager.isRunning(name);
|
||||
await this.manager.stop(name, true);
|
||||
await this.manager.upsert(name, config);
|
||||
if (isRunning) {
|
||||
await this.manager.start(name);
|
||||
}
|
||||
});
|
||||
return await this.manager.getSessionInfo(name);
|
||||
}
|
||||
|
||||
@Delete(':session')
|
||||
@SessionApiParam
|
||||
@ApiOperation({
|
||||
summary: 'Delete the session',
|
||||
description:
|
||||
'Delete the session with the given name. Stop and logout as well. Idempotent operation.',
|
||||
})
|
||||
@UsePipes(new WAHAValidationPipe())
|
||||
async delete(@Param('session') name: string): Promise<void> {
|
||||
await this.withLock(name, async () => {
|
||||
await this.manager.stop(name, true);
|
||||
await this.manager.logout(name);
|
||||
await this.manager.delete(name);
|
||||
});
|
||||
}
|
||||
|
||||
@Post(':session/start')
|
||||
@SessionApiParam
|
||||
@ApiOperation({
|
||||
summary: 'Start the session',
|
||||
description:
|
||||
'Start the session with the given name. The session must exist. Identity operation.',
|
||||
})
|
||||
@UsePipes(new WAHAValidationPipe())
|
||||
async start(@Param('session') name: string): Promise<SessionDTO> {
|
||||
await this.withLock(name, async () => {
|
||||
const exists = await this.manager.exists(name);
|
||||
if (!exists) {
|
||||
throw new NotFoundException('Session not found');
|
||||
}
|
||||
const isRunning = this.manager.isRunning(name);
|
||||
if (!isRunning) {
|
||||
await this.manager.start(name);
|
||||
}
|
||||
});
|
||||
return await this.manager.getSessionInfo(name);
|
||||
}
|
||||
|
||||
@Post(':session/stop')
|
||||
@SessionApiParam
|
||||
@ApiOperation({
|
||||
summary: 'Stop the session',
|
||||
description: 'Stop the session with the given name. Idempotent operation.',
|
||||
})
|
||||
@UsePipes(new WAHAValidationPipe())
|
||||
async stop(@Param('session') name: string): Promise<SessionDTO> {
|
||||
await this.withLock(name, async () => {
|
||||
const exists = await this.manager.exists(name);
|
||||
if (!exists) {
|
||||
throw new NotFoundException('Session not found');
|
||||
}
|
||||
await this.manager.stop(name, false);
|
||||
});
|
||||
return await this.manager.getSessionInfo(name);
|
||||
}
|
||||
|
||||
@Post(':session/logout')
|
||||
@SessionApiParam
|
||||
@ApiOperation({
|
||||
summary: 'Logout from the session',
|
||||
description: 'Logout the session, restart a session if it was not STOPPED',
|
||||
})
|
||||
@UsePipes(new WAHAValidationPipe())
|
||||
async logout(@Param('session') name: string): Promise<SessionDTO> {
|
||||
await this.withLock(name, async () => {
|
||||
const exists = await this.manager.exists(name);
|
||||
if (!exists) {
|
||||
throw new NotFoundException('Session not found');
|
||||
}
|
||||
const isRunning = this.manager.isRunning(name);
|
||||
await this.manager.stop(name, true);
|
||||
await this.manager.logout(name);
|
||||
if (isRunning) {
|
||||
await this.manager.start(name);
|
||||
}
|
||||
});
|
||||
return await this.manager.getSessionInfo(name);
|
||||
}
|
||||
|
||||
@Post(':session/restart')
|
||||
@SessionApiParam
|
||||
@ApiOperation({
|
||||
summary: 'Restart the session',
|
||||
description: 'Restart the session with the given name.',
|
||||
})
|
||||
@UsePipes(new WAHAValidationPipe())
|
||||
async restart(@Param('session') name: string): Promise<SessionDTO> {
|
||||
await this.withLock(name, async () => {
|
||||
const exists = await this.manager.exists(name);
|
||||
if (!exists) {
|
||||
throw new NotFoundException('Session not found');
|
||||
}
|
||||
await this.manager.stop(name, true);
|
||||
await this.manager.start(name);
|
||||
});
|
||||
|
||||
return await this.manager.getSessionInfo(name);
|
||||
}
|
||||
|
||||
@Post('/start/')
|
||||
@ApiOperation({
|
||||
summary: 'Upsert and Start session',
|
||||
description:
|
||||
'Create session (if not exists) or update a config (if exists) and start it.',
|
||||
deprecated: true,
|
||||
})
|
||||
async DEPRACATED_start(
|
||||
@Body() request: SessionStartDeprecatedRequest,
|
||||
): Promise<SessionDTO> {
|
||||
const name = request.name;
|
||||
if (this.manager.isRunning(name)) {
|
||||
const msg = `Session '${name}' is already started.`;
|
||||
throw new UnprocessableEntityException(msg);
|
||||
}
|
||||
|
||||
return await this.withLock(name, async () => {
|
||||
const config = request.config;
|
||||
await this.manager.upsert(name, config);
|
||||
return await this.manager.start(name);
|
||||
});
|
||||
}
|
||||
|
||||
@Post('/start/')
|
||||
async start(@Body() request: SessionRequest) {
|
||||
// Do not wait it, because we get venom instance only after scanning QR code
|
||||
this.whatsappSessionManager.startSession(request.sessionName)
|
||||
@Post('/stop/')
|
||||
@ApiOperation({
|
||||
summary: 'Stop (and Logout if asked) session',
|
||||
description: 'Stop session and Logout by default.',
|
||||
deprecated: true,
|
||||
})
|
||||
async DEPRECATED_stop(
|
||||
@Body() request: SessionStopDeprecatedRequest,
|
||||
): Promise<void> {
|
||||
const name = request.name;
|
||||
if (request.logout) {
|
||||
// Old API did remove the session complete
|
||||
await this.withLock(name, async () => {
|
||||
await this.manager.stop(name, true);
|
||||
await this.manager.logout(name);
|
||||
await this.manager.delete(name);
|
||||
});
|
||||
} else {
|
||||
await this.withLock(name, async () => {
|
||||
await this.manager.stop(name, false);
|
||||
});
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@Post('/stop/')
|
||||
async stop(@Body() request: SessionRequest) {
|
||||
await this.whatsappSessionManager.stopSession(request.sessionName)
|
||||
}
|
||||
|
||||
@Get('/')
|
||||
async list() {
|
||||
return this.whatsappSessionManager.getAllSessions()
|
||||
}
|
||||
@Post('/logout/')
|
||||
@ApiOperation({
|
||||
summary: 'Logout and Delete session.',
|
||||
description: 'Stop, Logout and Delete session.',
|
||||
deprecated: true,
|
||||
})
|
||||
async DEPRECATED_logout(
|
||||
@Body() request: SessionLogoutDeprecatedRequest,
|
||||
): Promise<void> {
|
||||
const name = request.name;
|
||||
await this.withLock(name, async () => {
|
||||
await this.manager.stop(name, true);
|
||||
await this.manager.logout(name);
|
||||
await this.manager.delete(name);
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
export { SessionsController };
|
||||
@@ -0,0 +1,75 @@
|
||||
import { Body, Controller, Post } from '@nestjs/common';
|
||||
import { ApiOperation, ApiSecurity, ApiTags } from '@nestjs/swagger';
|
||||
import {
|
||||
SessionApiParam,
|
||||
SessionParam,
|
||||
} from '@waha/nestjs/params/SessionApiParam';
|
||||
|
||||
import { SessionManager } from '../core/abc/manager.abc';
|
||||
import { WhatsappSession } from '../core/abc/session.abc';
|
||||
import {
|
||||
DeleteStatusRequest,
|
||||
ImageStatus,
|
||||
TextStatus,
|
||||
VideoStatus,
|
||||
VoiceStatus,
|
||||
} from '../structures/status.dto';
|
||||
|
||||
@ApiSecurity('api_key')
|
||||
@Controller('api/:session/status')
|
||||
@ApiTags('🟢 Status')
|
||||
class StatusController {
|
||||
constructor(private manager: SessionManager) {}
|
||||
|
||||
@Post('text')
|
||||
@SessionApiParam
|
||||
@ApiOperation({ summary: 'Send text status' })
|
||||
sendTextStatus(
|
||||
@SessionParam session: WhatsappSession,
|
||||
@Body() status: TextStatus,
|
||||
) {
|
||||
return session.sendTextStatus(status);
|
||||
}
|
||||
|
||||
@Post('image')
|
||||
@SessionApiParam
|
||||
@ApiOperation({ summary: 'Send image status' })
|
||||
sendImageStatus(
|
||||
@SessionParam session: WhatsappSession,
|
||||
@Body() status: ImageStatus,
|
||||
) {
|
||||
return session.sendImageStatus(status);
|
||||
}
|
||||
|
||||
@Post('voice')
|
||||
@SessionApiParam
|
||||
@ApiOperation({ summary: 'Send voice status' })
|
||||
sendVoiceStatus(
|
||||
@SessionParam session: WhatsappSession,
|
||||
@Body() status: VoiceStatus,
|
||||
) {
|
||||
return session.sendVoiceStatus(status);
|
||||
}
|
||||
|
||||
@Post('video')
|
||||
@SessionApiParam
|
||||
@ApiOperation({ summary: 'Send video status' })
|
||||
sendVideoStatus(
|
||||
@SessionParam session: WhatsappSession,
|
||||
@Body() status: VideoStatus,
|
||||
) {
|
||||
return session.sendVideoStatus(status);
|
||||
}
|
||||
|
||||
@Post('delete')
|
||||
@SessionApiParam
|
||||
@ApiOperation({ summary: 'DELETE sent status' })
|
||||
deleteStatus(
|
||||
@SessionParam session: WhatsappSession,
|
||||
@Body() status: DeleteStatusRequest,
|
||||
) {
|
||||
return session.deleteStatus(status);
|
||||
}
|
||||
}
|
||||
|
||||
export { StatusController };
|
||||
@@ -0,0 +1,25 @@
|
||||
import { Controller, Get } from '@nestjs/common';
|
||||
import {
|
||||
ApiExtraModels,
|
||||
ApiOperation,
|
||||
ApiSecurity,
|
||||
ApiTags,
|
||||
} from '@nestjs/swagger';
|
||||
|
||||
import { WAHAEnvironment } from '../structures/environment.dto';
|
||||
import { VERSION } from '../version';
|
||||
|
||||
@ApiSecurity('api_key')
|
||||
@Controller('api/version')
|
||||
@ApiTags('🔍 Observability')
|
||||
export class VersionController {
|
||||
@Get('')
|
||||
@ApiOperation({
|
||||
summary: 'Get the server version ',
|
||||
deprecated: true,
|
||||
description: "Use 'GET /api/server/version' instead ",
|
||||
})
|
||||
get(): WAHAEnvironment {
|
||||
return VERSION;
|
||||
}
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
import {ConsoleLogger, Module} from '@nestjs/common';
|
||||
import {WhatsappSessionManager} from "./whatsapp.service";
|
||||
import {ScreenshotController} from "./api/screenshot.controller";
|
||||
import {ConfigModule} from "@nestjs/config";
|
||||
import {WhatsappConfigService} from "./config.service";
|
||||
import {ServeStaticModule} from "@nestjs/serve-static";
|
||||
import {SessionsController} from "./api/sessions.controller";
|
||||
import {ChattingController} from "./api/chatting.controller";
|
||||
import {DeviceController} from "./api/device.controller";
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
ConfigModule.forRoot({
|
||||
isGlobal: true,
|
||||
}),
|
||||
ServeStaticModule.forRootAsync({
|
||||
imports: [],
|
||||
extraProviders: [WhatsappConfigService],
|
||||
inject: [WhatsappConfigService],
|
||||
useFactory: (config: WhatsappConfigService) => {
|
||||
return [{
|
||||
rootPath: config.filesFolder,
|
||||
serveRoot: config.files_uri,
|
||||
}]
|
||||
},
|
||||
}),
|
||||
],
|
||||
controllers: [
|
||||
SessionsController,
|
||||
ChattingController,
|
||||
DeviceController,
|
||||
ScreenshotController,
|
||||
],
|
||||
providers: [WhatsappSessionManager, ConsoleLogger, WhatsappConfigService],
|
||||
})
|
||||
export class AppModule {
|
||||
}
|
||||
+128
-40
@@ -1,56 +1,144 @@
|
||||
import {Injectable} from "@nestjs/common";
|
||||
import {ConfigService} from "@nestjs/config";
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import { WAHAEvents } from '@waha/structures/enums.dto';
|
||||
|
||||
import { parseBool } from './helpers';
|
||||
import { WebhookConfig } from './structures/webhooks.config.dto';
|
||||
|
||||
@Injectable()
|
||||
export class WhatsappConfigService {
|
||||
public files_uri = '/api/files'
|
||||
public schema = "http"
|
||||
constructor(private configService: ConfigService) {}
|
||||
|
||||
constructor(private configService: ConfigService) {
|
||||
}
|
||||
get schema() {
|
||||
return this.configService.get('WHATSAPP_API_SCHEMA', 'http');
|
||||
}
|
||||
|
||||
get filesURL(): string {
|
||||
return `${this.schema}://${this.hostname}:${this.port}${this.files_uri}/`
|
||||
}
|
||||
get hostname(): string {
|
||||
return this.configService.get('WHATSAPP_API_HOSTNAME', 'localhost');
|
||||
}
|
||||
|
||||
get hostname(): string {
|
||||
return this.configService.get(
|
||||
'WHATSAPP_API_HOSTNAME',
|
||||
'localhost',
|
||||
)
|
||||
}
|
||||
get port(): string {
|
||||
return this.configService.get('WHATSAPP_API_PORT', '3000');
|
||||
}
|
||||
|
||||
get port(): string {
|
||||
return this.configService.get(
|
||||
'WHATSAPP_API_PORT',
|
||||
'3000',
|
||||
)
|
||||
get mimetypes(): string[] {
|
||||
if (!this.shouldDownloadMedia) {
|
||||
return ['mimetype/ignore-all-media'];
|
||||
}
|
||||
const types = this.configService.get('WHATSAPP_FILES_MIMETYPES', '');
|
||||
return types ? types.split(',') : [];
|
||||
}
|
||||
|
||||
get filesFolder(): string {
|
||||
return this.configService.get(
|
||||
'WHATSAPP_FILES_FOLDER',
|
||||
'/tmp/whatsapp-files',
|
||||
)
|
||||
}
|
||||
get shouldDownloadMedia(): boolean {
|
||||
const value = this.configService.get('WHATSAPP_DOWNLOAD_MEDIA', 'true');
|
||||
return parseBool(value);
|
||||
}
|
||||
|
||||
get filesLifetime(): number {
|
||||
return this.configService.get<number>(
|
||||
'WHATSAPP_FILES_LIFETIME',
|
||||
180,
|
||||
)
|
||||
get startSessions(): string[] {
|
||||
const value: string = this.configService.get('WHATSAPP_START_SESSION', '');
|
||||
if (!value) {
|
||||
return [];
|
||||
}
|
||||
return value.split(',');
|
||||
}
|
||||
|
||||
get mimetypes(): string[] | null {
|
||||
const types = this.configService.get('WHATSAPP_FILES_MIMETYPES', "")
|
||||
return types ? types.split(',') : null
|
||||
}
|
||||
get shouldRestartAllSessions(): boolean {
|
||||
const value: string = this.configService.get(
|
||||
'WHATSAPP_RESTART_ALL_SESSIONS',
|
||||
'false',
|
||||
);
|
||||
return parseBool(value);
|
||||
}
|
||||
|
||||
get startSession(): string | undefined {
|
||||
return this.configService.get('WHATSAPP_START_SESSION', undefined)
|
||||
}
|
||||
get proxyServer(): string[] | string | undefined {
|
||||
const single = this.configService.get<string>(
|
||||
'WHATSAPP_PROXY_SERVER',
|
||||
undefined,
|
||||
);
|
||||
const multipleValues = this.configService.get<string>(
|
||||
'WHATSAPP_PROXY_SERVER_LIST',
|
||||
undefined,
|
||||
);
|
||||
const multiple = multipleValues ? multipleValues.split(',') : undefined;
|
||||
return single ? single : multiple;
|
||||
}
|
||||
|
||||
get(name: string): any {
|
||||
return this.configService.get(name)
|
||||
get proxyServerIndexPrefix(): string | undefined {
|
||||
return this.configService.get(
|
||||
'WHATSAPP_PROXY_SERVER_INDEX_PREFIX',
|
||||
undefined,
|
||||
);
|
||||
}
|
||||
|
||||
get proxyServerUsername(): string | undefined {
|
||||
return this.configService.get('WHATSAPP_PROXY_SERVER_USERNAME', undefined);
|
||||
}
|
||||
|
||||
get proxyServerPassword(): string | undefined {
|
||||
return this.configService.get('WHATSAPP_PROXY_SERVER_PASSWORD', undefined);
|
||||
}
|
||||
|
||||
getWebhookConfig(): WebhookConfig | undefined {
|
||||
const url = this.getWebhookUrl();
|
||||
const events = this.getWebhookEvents();
|
||||
if (!url || events.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
return { url: url, events: events };
|
||||
}
|
||||
|
||||
private getWebhookUrl(): string | undefined {
|
||||
return this.get('WHATSAPP_HOOK_URL');
|
||||
}
|
||||
|
||||
private getWebhookEvents(): WAHAEvents[] {
|
||||
const value = this.get('WHATSAPP_HOOK_EVENTS', '');
|
||||
return value ? value.split(',') : [];
|
||||
}
|
||||
|
||||
getSessionMongoUrl(): string | undefined {
|
||||
return this.configService.get('WHATSAPP_SESSIONS_MONGO_URL', undefined);
|
||||
}
|
||||
|
||||
get(name: string, defaultValue: any = undefined): any {
|
||||
return this.configService.get(name, defaultValue);
|
||||
}
|
||||
|
||||
getApiKey(): string | undefined {
|
||||
return this.configService.get('WHATSAPP_API_KEY', '');
|
||||
}
|
||||
|
||||
getExcludedPaths(): string[] {
|
||||
const value = this.configService.get('WHATSAPP_API_KEY_EXCLUDE_PATH', '');
|
||||
if (!value) {
|
||||
return [];
|
||||
}
|
||||
return value.split(',');
|
||||
}
|
||||
|
||||
getHealthMediaFilesThreshold(): number {
|
||||
return this.configService.get<number>(
|
||||
'WHATSAPP_HEALTH_MEDIA_FILES_THRESHOLD_MB',
|
||||
100,
|
||||
);
|
||||
}
|
||||
|
||||
getHealthSessionFilesThreshold(): number {
|
||||
return this.configService.get<number>(
|
||||
'WHATSAPP_HEALTH_SESSION_FILES_THRESHOLD_MB',
|
||||
100,
|
||||
);
|
||||
}
|
||||
|
||||
getHealthMongoTimeout(): number {
|
||||
return this.configService.get<number>(
|
||||
'WHATSAPP_HEALTH_MONGO_TIMEOUT_MS',
|
||||
3000,
|
||||
);
|
||||
}
|
||||
|
||||
get debugModeEnabled(): boolean {
|
||||
const value = this.configService.get('WAHA_DEBUG_MODE', 'false');
|
||||
return parseBool(value);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
export class QR {
|
||||
private base64: string;
|
||||
public raw?: string;
|
||||
|
||||
save(base64: string, raw?: string) {
|
||||
this.base64 = base64.replace(/^data:image\/png;base64,/, '');
|
||||
this.raw = raw;
|
||||
}
|
||||
|
||||
get(): Buffer {
|
||||
return Buffer.from(this.base64, 'base64');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,166 @@
|
||||
import { INestApplication } from '@nestjs/common';
|
||||
import { DocumentBuilder, OpenAPIObject, SwaggerModule } from '@nestjs/swagger';
|
||||
import { DECORATORS } from '@nestjs/swagger/dist/constants';
|
||||
import { Logger } from 'nestjs-pino';
|
||||
|
||||
import { WhatsappConfigService } from '../config.service';
|
||||
import { VERSION } from '../version';
|
||||
import { SwaggerConfigServiceCore } from './config/SwaggerConfigServiceCore';
|
||||
|
||||
export class SwaggerConfiguratorCore {
|
||||
protected logger: any;
|
||||
|
||||
constructor(protected app: INestApplication) {
|
||||
this.logger = app.get(Logger);
|
||||
}
|
||||
|
||||
get title() {
|
||||
return 'WAHA - WhatsApp HTTP API';
|
||||
}
|
||||
|
||||
get description() {
|
||||
return (
|
||||
'WhatsApp HTTP API that you can run in a click!<br/>' +
|
||||
'<br/>' +
|
||||
'Learn more:' +
|
||||
'<ul>' +
|
||||
'<li><a href="https://waha.devlike.pro/" target="_blank">Documentation</a></li>' +
|
||||
'<li><a href="https://waha.devlike.pro/docs/how-to/engines/#features" target="_blank">Supported features in engines</a></li>' +
|
||||
'<li><a href="https://github.com/devlikeapro/waha" target="_blank">GitHub - WAHA Core</a></li>' +
|
||||
'<li><a href="https://github.com/devlikeapro/waha-plu' + // Separate line to pass pre-commit check
|
||||
's" target="_blank">GitHub - WAHA Plus</a></li>' +
|
||||
'<li><a href="/dashboard">WAHA Dashboard</a></li>' +
|
||||
'<li><a href="https://waha.devlike.pro/docs/how-to/waha-dashboard">WAHA Dashboard Documentation</a></li>' +
|
||||
'</ul>' +
|
||||
'<p>Support the project and get WAHA Plus version!</p>' +
|
||||
'<ul>' +
|
||||
'<li><a href="https://waha.devlike.pro/docs/how-to/plu' + // Separate line to pass pre-commit check
|
||||
's-version/" target="_blank">WAHA Plus</a></li>' +
|
||||
'<li><a href="https://patreon.com/wa_http_api/" target="_blank">Patreon</a></li>' +
|
||||
'<li><a href="https://boosty.to/wa-http-api/" target="_blank">Boosty</a></li>' +
|
||||
'<li><a href="https://portal.devlike.pro/" target="_blank">Patron Portal</a></li>' +
|
||||
'</ul>'
|
||||
);
|
||||
}
|
||||
|
||||
get externalDocUrl() {
|
||||
return 'https://waha.devlike.pro/';
|
||||
}
|
||||
|
||||
configure(webhooks: any[]) {
|
||||
const app = this.app;
|
||||
const builder = new DocumentBuilder();
|
||||
|
||||
builder
|
||||
.setTitle(this.title)
|
||||
.setDescription(this.description)
|
||||
.setExternalDoc(this.title, this.externalDocUrl)
|
||||
.setVersion(VERSION.version)
|
||||
.addTag('🖥️ Sessions', 'Control WhatsApp sessions (accounts)')
|
||||
.addTag('🔑 Auth', 'Authentication')
|
||||
.addTag('🖼️ Screenshot', 'Get screenshot of WhatsApp and show QR code')
|
||||
.addTag('📤 Chatting', 'Chatting methods')
|
||||
.addTag('📢 Channels', 'Channels (newsletters) methods')
|
||||
.addTag(
|
||||
'🟢 Status',
|
||||
'Status (aka stories) methods. <b>NOWEB</b> engine only!',
|
||||
)
|
||||
.addTag('💬 Chats', `Chats methods`)
|
||||
.addTag(
|
||||
'👤 Contacts',
|
||||
`Contacts methods.<br>
|
||||
Use phone number (without +) or phone number and \`@c.us\` at the end as \`contactId\`.<br>
|
||||
'E.g: \`12312312310\` OR \`12312312310@c.us\`<br>`,
|
||||
)
|
||||
.addTag('👥 Groups', `Groups methods.<br>`)
|
||||
.addTag('✅ Presence', `Presence information`)
|
||||
.addTag(
|
||||
'🏷️ Labels',
|
||||
'Labels - available only for WhatsApp Business accounts',
|
||||
)
|
||||
.addTag('🔍 Observability', 'Other methods')
|
||||
.addTag('🗄️ Storage', 'Storage methods')
|
||||
.addApiKey({
|
||||
type: 'apiKey',
|
||||
description: 'Your secret api key',
|
||||
name: 'X-Api-Key',
|
||||
});
|
||||
|
||||
const config = app.get(WhatsappConfigService);
|
||||
const swaggerConfig = app.get(SwaggerConfigServiceCore);
|
||||
if (swaggerConfig.advancedConfigEnabled) {
|
||||
builder.addServer('{protocol}://{host}:{port}/{baseUrl}', '', {
|
||||
protocol: {
|
||||
default: 'http',
|
||||
enum: ['http', 'https'],
|
||||
description: 'The protocol used to access the server.',
|
||||
},
|
||||
host: {
|
||||
default: config.hostname,
|
||||
description: 'The hostname or IP address of the server.',
|
||||
},
|
||||
port: {
|
||||
default: config.port,
|
||||
description:
|
||||
'The port number on which the server is listening for requests',
|
||||
},
|
||||
baseUrl: {
|
||||
default: '',
|
||||
description:
|
||||
'The base URL path for all API endpoints. This can be used to group related endpoints together under a common path.',
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
const swaggerDocumentConfig = builder.build();
|
||||
const swaggerDocumentOptions = {
|
||||
extraModels: webhooks,
|
||||
};
|
||||
let document = SwaggerModule.createDocument(
|
||||
app,
|
||||
swaggerDocumentConfig,
|
||||
swaggerDocumentOptions,
|
||||
);
|
||||
document = this.configureWebhooks(document, webhooks);
|
||||
SwaggerModule.setup('', app, document, {
|
||||
customSiteTitle: this.title,
|
||||
});
|
||||
}
|
||||
|
||||
private configureWebhooks(document: OpenAPIObject, supportedWebhooks) {
|
||||
document.openapi = '3.1.0';
|
||||
const webhooks = {};
|
||||
for (const webhook of supportedWebhooks) {
|
||||
const eventMetadata = Reflect.getMetadata(
|
||||
DECORATORS.API_MODEL_PROPERTIES,
|
||||
webhook.prototype,
|
||||
'event',
|
||||
);
|
||||
const event = new webhook().event;
|
||||
const schemaName = webhook.name;
|
||||
webhooks[event] = {
|
||||
post: {
|
||||
summary: eventMetadata.description,
|
||||
requestBody: {
|
||||
content: {
|
||||
'application/json': {
|
||||
schema: {
|
||||
$ref: `#/components/schemas/${schemaName}`,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
responses: {
|
||||
'200': {
|
||||
description:
|
||||
'Return a 200 status to indicate that the data was received successfully',
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
// @ts-ignore
|
||||
document.webhooks = webhooks;
|
||||
return document;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export abstract class DataStore {
|
||||
abstract init(sessionName?: string): Promise<void>;
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
import { Injectable, Logger, LoggerService } from '@nestjs/common';
|
||||
import { HealthCheckService } from '@nestjs/terminus';
|
||||
import type { HealthCheckResult } from '@nestjs/terminus/dist/health-check/health-check-result.interface';
|
||||
|
||||
import { WhatsappConfigService } from '../../config.service';
|
||||
import { SessionManager } from './manager.abc';
|
||||
|
||||
@Injectable()
|
||||
export abstract class WAHAHealthCheckService {
|
||||
protected logger: LoggerService;
|
||||
constructor(
|
||||
protected sessionManager: SessionManager,
|
||||
protected health: HealthCheckService,
|
||||
protected config: WhatsappConfigService,
|
||||
) {
|
||||
this.logger = new Logger('WAHAHealthCheckService');
|
||||
}
|
||||
|
||||
abstract check(): Promise<HealthCheckResult>;
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
import { BeforeApplicationShutdown } from '@nestjs/common';
|
||||
import { WAHAWebhook } from '@waha/structures/webhooks.dto';
|
||||
import { VERSION } from '@waha/version';
|
||||
import { EventEmitter } from 'events';
|
||||
|
||||
import { WAHAEngine, WAHAEvents } from '../../structures/enums.dto';
|
||||
import {
|
||||
SessionConfig,
|
||||
SessionDTO,
|
||||
SessionInfo,
|
||||
} from '../../structures/sessions.dto';
|
||||
import { ISessionAuthRepository } from '../storage/ISessionAuthRepository';
|
||||
import { ISessionConfigRepository } from '../storage/ISessionConfigRepository';
|
||||
import { WhatsappSession } from './session.abc';
|
||||
import { WebhookConductor } from './webhooks.abc';
|
||||
|
||||
export abstract class SessionManager implements BeforeApplicationShutdown {
|
||||
public store: any;
|
||||
public sessionAuthRepository: ISessionAuthRepository;
|
||||
public sessionConfigRepository: ISessionConfigRepository;
|
||||
public events: EventEmitter;
|
||||
|
||||
protected abstract getEngine(engine: WAHAEngine): typeof WhatsappSession;
|
||||
|
||||
protected abstract get EngineClass(): typeof WhatsappSession;
|
||||
|
||||
protected abstract get WebhookConductorClass(): typeof WebhookConductor;
|
||||
|
||||
abstract beforeApplicationShutdown(signal?: string);
|
||||
|
||||
//
|
||||
// API Methods
|
||||
//
|
||||
/**
|
||||
* Either create or update
|
||||
*/
|
||||
abstract exists(name: string): Promise<boolean>;
|
||||
|
||||
abstract isRunning(name: string): boolean;
|
||||
|
||||
abstract upsert(name: string, config?: SessionConfig): Promise<void>;
|
||||
|
||||
abstract delete(name: string): Promise<void>;
|
||||
|
||||
abstract start(name: string): Promise<SessionDTO>;
|
||||
|
||||
abstract stop(name: string, silent: boolean): Promise<void>;
|
||||
|
||||
abstract logout(name: string): Promise<void>;
|
||||
|
||||
abstract getSession(name: string): WhatsappSession;
|
||||
|
||||
abstract getSessionInfo(name: string): Promise<SessionInfo | null>;
|
||||
|
||||
abstract getSessions(all: boolean): Promise<SessionInfo[]>;
|
||||
|
||||
handleSessionEvent(event: WAHAEvents, session: WhatsappSession) {
|
||||
return (payload: any) => {
|
||||
const me = session.getSessionMeInfo();
|
||||
const data: WAHAWebhook = {
|
||||
event: event,
|
||||
session: session.name,
|
||||
metadata: session.sessionConfig?.metadata,
|
||||
me: me,
|
||||
payload: payload,
|
||||
engine: session.engine,
|
||||
environment: VERSION,
|
||||
};
|
||||
this.events.emit(event, data);
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,604 @@
|
||||
import {
|
||||
Channel,
|
||||
CreateChannelRequest,
|
||||
ListChannelsQuery,
|
||||
} from '@waha/structures/channels.dto';
|
||||
import { GetChatsQuery } from '@waha/structures/chats.dto';
|
||||
import { Label, LabelID } from '@waha/structures/labels.dto';
|
||||
import { LoggerBuilder } from '@waha/utils/logging';
|
||||
import { EventEmitter } from 'events';
|
||||
import * as fs from 'fs';
|
||||
import { Logger } from 'pino';
|
||||
import { MessageId } from 'whatsapp-web.js';
|
||||
|
||||
import {
|
||||
ChatRequest,
|
||||
CheckNumberStatusQuery,
|
||||
EditMessageRequest,
|
||||
GetMessageQuery,
|
||||
MessageContactVcardRequest,
|
||||
MessageFileRequest,
|
||||
MessageImageRequest,
|
||||
MessageLinkPreviewRequest,
|
||||
MessageLocationRequest,
|
||||
MessagePollRequest,
|
||||
MessageReactionRequest,
|
||||
MessageReplyRequest,
|
||||
MessageStarRequest,
|
||||
MessageTextRequest,
|
||||
MessageVideoRequest,
|
||||
MessageVoiceRequest,
|
||||
} from '../../structures/chatting.dto';
|
||||
import { ContactQuery, ContactRequest } from '../../structures/contacts.dto';
|
||||
import {
|
||||
WAHAEngine,
|
||||
WAHAEvents,
|
||||
WAHAPresenceStatus,
|
||||
WAHASessionStatus,
|
||||
} from '../../structures/enums.dto';
|
||||
import {
|
||||
CreateGroupRequest,
|
||||
ParticipantsRequest,
|
||||
SettingsSecurityChangeInfo,
|
||||
} from '../../structures/groups.dto';
|
||||
import { WAHAChatPresences } from '../../structures/presence.dto';
|
||||
import {
|
||||
MeInfo,
|
||||
ProxyConfig,
|
||||
SessionConfig,
|
||||
} from '../../structures/sessions.dto';
|
||||
import {
|
||||
DeleteStatusRequest,
|
||||
ImageStatus,
|
||||
TextStatus,
|
||||
VideoStatus,
|
||||
VoiceStatus,
|
||||
} from '../../structures/status.dto';
|
||||
import { WASessionStatusBody } from '../../structures/webhooks.dto';
|
||||
import { NotImplementedByEngineError } from '../exceptions';
|
||||
import { IMediaManager } from '../media/IMediaManager';
|
||||
import { QR } from '../QR';
|
||||
import { DataStore } from './DataStore';
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const qrcode = require('qrcode-terminal');
|
||||
|
||||
const CHROME_PATH = '/usr/bin/google-chrome-stable';
|
||||
const CHROMIUM_PATH = '/usr/bin/chromium';
|
||||
|
||||
export function getBrowserExecutablePath() {
|
||||
if (fs.existsSync(CHROME_PATH)) {
|
||||
return CHROME_PATH;
|
||||
}
|
||||
return CHROMIUM_PATH;
|
||||
}
|
||||
|
||||
export function ensureSuffix(phone) {
|
||||
const suffix = '@c.us';
|
||||
if (phone.includes('@')) {
|
||||
return phone;
|
||||
}
|
||||
return phone + suffix;
|
||||
}
|
||||
|
||||
export enum WAHAInternalEvent {
|
||||
ENGINE_START = 'engine.start',
|
||||
}
|
||||
|
||||
export interface SessionParams {
|
||||
name: string;
|
||||
printQR: boolean;
|
||||
mediaManager: IMediaManager;
|
||||
loggerBuilder: LoggerBuilder;
|
||||
sessionStore: DataStore;
|
||||
proxyConfig?: ProxyConfig;
|
||||
sessionConfig?: SessionConfig;
|
||||
engineConfig?: any;
|
||||
}
|
||||
|
||||
export abstract class WhatsappSession {
|
||||
public events: EventEmitter;
|
||||
public engine: WAHAEngine;
|
||||
|
||||
public name: string;
|
||||
protected mediaManager: IMediaManager;
|
||||
public loggerBuilder: LoggerBuilder;
|
||||
protected logger: Logger;
|
||||
protected sessionStore: DataStore;
|
||||
protected proxyConfig?: ProxyConfig;
|
||||
public sessionConfig?: SessionConfig;
|
||||
protected engineConfig?: any;
|
||||
|
||||
private _status: WAHASessionStatus;
|
||||
private shouldPrintQR: boolean;
|
||||
|
||||
public constructor({
|
||||
name,
|
||||
printQR,
|
||||
loggerBuilder,
|
||||
sessionStore,
|
||||
proxyConfig,
|
||||
mediaManager,
|
||||
sessionConfig,
|
||||
engineConfig,
|
||||
}: SessionParams) {
|
||||
this.events = new EventEmitter();
|
||||
this.name = name;
|
||||
this.proxyConfig = proxyConfig;
|
||||
this.loggerBuilder = loggerBuilder;
|
||||
this.logger = loggerBuilder.child({ name: 'WhatsappSession' });
|
||||
this.sessionStore = sessionStore;
|
||||
this.mediaManager = mediaManager;
|
||||
this.sessionConfig = sessionConfig;
|
||||
this.engineConfig = engineConfig;
|
||||
this.shouldPrintQR = printQR;
|
||||
}
|
||||
|
||||
protected set status(value: WAHASessionStatus) {
|
||||
this._status = value;
|
||||
const body: WASessionStatusBody = { name: this.name, status: value };
|
||||
this.events.emit(WAHAEvents.SESSION_STATUS, body);
|
||||
}
|
||||
|
||||
public get status() {
|
||||
return this._status;
|
||||
}
|
||||
|
||||
getBrowserExecutablePath() {
|
||||
return getBrowserExecutablePath();
|
||||
}
|
||||
|
||||
getBrowserArgsForPuppeteer() {
|
||||
// Run optimized version of Chrome
|
||||
// References:
|
||||
// https://github.com/pedroslopez/whatsapp-web.js/issues/1420
|
||||
// https://github.com/wppconnect-team/wppconnect/issues/1326
|
||||
// https://www.bannerbear.com/blog/ways-to-speed-up-puppeteer-screenshots/
|
||||
return [
|
||||
'--aggressive-cache-discard',
|
||||
'--disable-accelerated-2d-canvas',
|
||||
'--disable-application-cache',
|
||||
'--disable-background-networking',
|
||||
'--disable-cache',
|
||||
'--disable-client-side-phishing-detection',
|
||||
'--disable-component-update',
|
||||
'--disable-default-apps',
|
||||
'--disable-dev-shm-usage',
|
||||
'--disable-extensions',
|
||||
'--disable-gpu',
|
||||
'--disable-offer-store-unmasked-wallet-cards',
|
||||
'--disable-offline-load-stale-cache',
|
||||
'--disable-popup-blocking',
|
||||
'--disable-setuid-sandbox',
|
||||
'--disable-site-isolation-trials', // https://superuser.com/questions/654565/how-to-run-google-chrome-in-a-single-process
|
||||
'--disable-speech-api',
|
||||
'--disable-sync',
|
||||
'--disable-translate',
|
||||
'--disable-web-security',
|
||||
'--disk-cache-size=0',
|
||||
'--hide-scrollbars',
|
||||
'--ignore-certificate-errors',
|
||||
'--ignore-ssl-errors',
|
||||
'--in-process-gpu', // https://superuser.com/questions/654565/how-to-run-google-chrome-in-a-single-process
|
||||
'--metrics-recording-only',
|
||||
'--mute-audio',
|
||||
'--no-default-browser-check',
|
||||
'--no-first-run',
|
||||
'--no-pings',
|
||||
'--no-sandbox',
|
||||
'--no-zygote',
|
||||
'--password-store=basic',
|
||||
'--renderer-process-limit=2', // https://superuser.com/questions/654565/how-to-run-google-chrome-in-a-single-process
|
||||
'--safebrowsing-disable-auto-update',
|
||||
'--single-process',
|
||||
'--use-mock-keychain',
|
||||
];
|
||||
}
|
||||
|
||||
protected isDebugEnabled() {
|
||||
return this.logger.isLevelEnabled('debug');
|
||||
}
|
||||
|
||||
/** Start the session */
|
||||
abstract start();
|
||||
|
||||
/** Stop the session */
|
||||
abstract stop(): Promise<void>;
|
||||
|
||||
/** Subscribe the handler to specific hook */
|
||||
subscribeSessionEvent(
|
||||
hook: WAHAEvents | string,
|
||||
handler: (message) => void,
|
||||
): boolean {
|
||||
switch (hook) {
|
||||
case WAHAEvents.SESSION_STATUS:
|
||||
this.events.on(WAHAEvents.SESSION_STATUS, handler);
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
abstract subscribeEngineEvent(
|
||||
hook: WAHAEvents | string,
|
||||
handler: (message) => void,
|
||||
): boolean;
|
||||
|
||||
/**
|
||||
* START - Methods for API
|
||||
*/
|
||||
|
||||
/**
|
||||
* Auth methods
|
||||
*/
|
||||
|
||||
public getQR(): QR {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
public requestCode(phoneNumber: string, method: string, params?: any) {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
public authorizeCode(code: string) {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
abstract getScreenshot(): Promise<Buffer>;
|
||||
|
||||
public getSessionMeInfo(): MeInfo | null {
|
||||
return null;
|
||||
}
|
||||
|
||||
public getCaptcha(): Promise<QR> {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
public saveCaptcha(code: string) {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
/**
|
||||
* Other methods
|
||||
*/
|
||||
abstract checkNumberStatus(request: CheckNumberStatusQuery);
|
||||
|
||||
abstract sendText(request: MessageTextRequest);
|
||||
|
||||
sendContactVCard(request: MessageContactVcardRequest) {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
sendPoll(request: MessagePollRequest) {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
abstract sendLocation(request: MessageLocationRequest);
|
||||
|
||||
sendLinkPreview(request: MessageLinkPreviewRequest) {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
abstract sendImage(request: MessageImageRequest);
|
||||
|
||||
abstract sendFile(request: MessageFileRequest);
|
||||
|
||||
abstract sendVoice(request: MessageVoiceRequest);
|
||||
|
||||
sendVideo(request: MessageVideoRequest) {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
abstract reply(request: MessageReplyRequest);
|
||||
|
||||
abstract sendSeen(chat: ChatRequest);
|
||||
|
||||
abstract startTyping(chat: ChatRequest);
|
||||
|
||||
abstract stopTyping(chat: ChatRequest);
|
||||
|
||||
getMessages(query: GetMessageQuery) {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
abstract setReaction(request: MessageReactionRequest);
|
||||
|
||||
setStar(request: MessageStarRequest): Promise<void> {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
/**
|
||||
* Chats methods
|
||||
*/
|
||||
public getChats(query: GetChatsQuery) {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
public deleteChat(chatId) {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
public getChatMessages(
|
||||
chatId: string,
|
||||
limit: number,
|
||||
downloadMedia: boolean,
|
||||
) {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
public deleteMessage(chatId: string, messageId: string) {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
public editMessage(
|
||||
chatId: string,
|
||||
messageId: string,
|
||||
request: EditMessageRequest,
|
||||
) {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
public clearMessages(chatId) {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
public chatsArchiveChat(chatId: string): Promise<any> {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
public chatsUnarchiveChat(chatId: string): Promise<any> {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
/**
|
||||
* Labels methods
|
||||
*/
|
||||
|
||||
public getLabels(): Promise<Label[]> {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
public getChatsByLabelId(labelId: string) {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
public getChatLabels(chatId: string): Promise<Label[]> {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
public putLabelsToChat(chatId: string, labels: LabelID[]) {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
/**
|
||||
* Contacts methods
|
||||
*/
|
||||
public getContact(query: ContactQuery) {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
public getContacts() {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
public getContactAbout(query: ContactQuery) {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
public getContactProfilePicture(query: ContactQuery) {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
public blockContact(request: ContactRequest) {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
public unblockContact(request: ContactRequest) {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
/**
|
||||
* Group methods
|
||||
*/
|
||||
public createGroup(request: CreateGroupRequest) {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
public getGroups() {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
public getGroup(id) {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
public getInfoAdminsOnly(id): Promise<SettingsSecurityChangeInfo> {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
public setInfoAdminsOnly(id, value) {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
public getMessagesAdminsOnly(id): Promise<SettingsSecurityChangeInfo> {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
public setMessagesAdminsOnly(id, value) {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
public deleteGroup(id) {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
public leaveGroup(id) {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
public setDescription(id, description) {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
public setSubject(id, description) {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
public getInviteCode(id): Promise<string> {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
public revokeInviteCode(id): Promise<string> {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
public getParticipants(id) {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
public addParticipants(id, request: ParticipantsRequest) {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
public removeParticipants(id, request: ParticipantsRequest) {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
public promoteParticipantsToAdmin(id, request: ParticipantsRequest) {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
public demoteParticipantsToUser(id, request: ParticipantsRequest) {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
public setPresence(presence: WAHAPresenceStatus, chatId?: string) {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
public getPresences(): Promise<WAHAChatPresences[]> {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
public getPresence(id: string): Promise<WAHAChatPresences> {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
public subscribePresence(id: string): Promise<void> {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
/**
|
||||
* Channels methods
|
||||
*/
|
||||
public channelsList(query: ListChannelsQuery): Promise<Channel[]> {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
public channelsCreateChannel(
|
||||
request: CreateChannelRequest,
|
||||
): Promise<Channel> {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
public channelsGetChannel(id: string): Promise<Channel> {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
public channelsGetChannelByInviteCode(inviteCode: string): Promise<Channel> {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
public channelsDeleteChannel(id: string): Promise<void> {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
public channelsFollowChannel(id: string): Promise<void> {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
public channelsUnfollowChannel(id: string): Promise<void> {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
public channelsMuteChannel(id: string): Promise<void> {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
public channelsUnmuteChannel(id: string): Promise<void> {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
/**
|
||||
* Status methods
|
||||
*/
|
||||
public sendTextStatus(status: TextStatus) {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
public sendImageStatus(status: ImageStatus) {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
public sendVoiceStatus(status: VoiceStatus) {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
public sendVideoStatus(status: VideoStatus) {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
public deleteStatus(request: DeleteStatusRequest) {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
public async getEngineInfo(): Promise<any> {
|
||||
return {};
|
||||
}
|
||||
|
||||
/**
|
||||
* END - Methods for API
|
||||
*/
|
||||
|
||||
/**
|
||||
* Add WhatsApp suffix (@c.us) to the phone number if it doesn't have it yet
|
||||
* @param phone
|
||||
*/
|
||||
protected ensureSuffix(phone) {
|
||||
return ensureSuffix(phone);
|
||||
}
|
||||
|
||||
protected deserializeId(messageId: string): MessageId {
|
||||
const parts = messageId.split('_');
|
||||
return {
|
||||
fromMe: parts[0] === 'true',
|
||||
remote: parts[1],
|
||||
id: parts[2],
|
||||
_serialized: messageId,
|
||||
};
|
||||
}
|
||||
|
||||
protected printQR(qr: QR) {
|
||||
if (!this.shouldPrintQR) {
|
||||
return;
|
||||
}
|
||||
if (!qr.raw) {
|
||||
this.logger.error(
|
||||
'QR.raw is not available, can not print it in the console',
|
||||
);
|
||||
return;
|
||||
}
|
||||
this.logger.info(
|
||||
"You can disable QR in console by setting 'WAHA_PRINT_QR=false' in your environment variables.",
|
||||
);
|
||||
qrcode.generate(qr.raw, { small: true });
|
||||
}
|
||||
}
|
||||
|
||||
export function isNewsletter(jid: string) {
|
||||
return jid.endsWith('@newsletter');
|
||||
}
|
||||
|
||||
export function getChannelInviteLink(code: string) {
|
||||
return `https://whatsapp.com/channel/${code}`;
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
import { LoggerBuilder } from '@waha/utils/logging';
|
||||
import { Logger } from 'pino';
|
||||
|
||||
import { WebhookConfig } from '../../structures/webhooks.config.dto';
|
||||
import { WhatsappSession } from './session.abc';
|
||||
|
||||
export abstract class WebhookSender {
|
||||
protected url: string;
|
||||
protected logger: Logger;
|
||||
|
||||
constructor(
|
||||
loggerBuilder: LoggerBuilder,
|
||||
protected webhookConfig: WebhookConfig,
|
||||
) {
|
||||
this.url = webhookConfig.url;
|
||||
this.logger = loggerBuilder.child({ name: WebhookSender.name });
|
||||
}
|
||||
|
||||
abstract send(json);
|
||||
}
|
||||
|
||||
export abstract class WebhookConductor {
|
||||
abstract configure(session: WhatsappSession, webhooks: WebhookConfig[]);
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
import {
|
||||
BeforeApplicationShutdown,
|
||||
Logger,
|
||||
LoggerService,
|
||||
} from '@nestjs/common';
|
||||
import { sleep } from '@nestjs/terminus/dist/utils';
|
||||
import {
|
||||
OnGatewayConnection,
|
||||
OnGatewayDisconnect,
|
||||
OnGatewayInit,
|
||||
WebSocketGateway,
|
||||
WebSocketServer,
|
||||
} from '@nestjs/websockets';
|
||||
import { SessionManager } from '@waha/core/abc/manager.abc';
|
||||
import { WebsocketHeartbeatJob } from '@waha/nestjs/ws/WebsocketHeartbeatJob';
|
||||
import { WebSocket } from '@waha/nestjs/ws/ws';
|
||||
import { WAHAEvents } from '@waha/structures/enums.dto';
|
||||
import { generatePrefixedId } from '@waha/utils/ids';
|
||||
import { IncomingMessage } from 'http';
|
||||
import * as lodash from 'lodash';
|
||||
import * as url from 'url';
|
||||
import { Server } from 'ws';
|
||||
|
||||
@WebSocketGateway({
|
||||
path: '/ws',
|
||||
cors: true,
|
||||
})
|
||||
export class WebsocketGatewayCore
|
||||
implements
|
||||
OnGatewayInit,
|
||||
OnGatewayConnection,
|
||||
OnGatewayDisconnect,
|
||||
BeforeApplicationShutdown
|
||||
{
|
||||
HEARTBEAT_INTERVAL = 10_000;
|
||||
|
||||
@WebSocketServer()
|
||||
server: Server;
|
||||
|
||||
private listeners: Map<WebSocket, { session: string; events: string[] }> =
|
||||
new Map();
|
||||
|
||||
private readonly logger: LoggerService;
|
||||
private heartbeat: WebsocketHeartbeatJob;
|
||||
|
||||
constructor(private manager: SessionManager) {
|
||||
this.logger = new Logger('WebsocketGateway');
|
||||
this.heartbeat = new WebsocketHeartbeatJob(
|
||||
this.logger,
|
||||
this.HEARTBEAT_INTERVAL,
|
||||
);
|
||||
}
|
||||
|
||||
handleConnection(socket: WebSocket, request: IncomingMessage, ...args): any {
|
||||
// wsc - websocket client
|
||||
const id = generatePrefixedId('wsc');
|
||||
socket.id = id;
|
||||
this.logger.debug(`New client connected: ${request.url}`);
|
||||
const params = this.getParams(id, request, socket);
|
||||
if (!params) {
|
||||
return;
|
||||
}
|
||||
const { session, events } = params;
|
||||
this.logger.debug(
|
||||
`Client connected to session: '${session}', events: ${events}, ${id}`,
|
||||
);
|
||||
this.listeners.set(socket, { session, events });
|
||||
}
|
||||
|
||||
private getParams(id: string, request: IncomingMessage, socket: WebSocket) {
|
||||
const query = url.parse(request.url, true).query;
|
||||
const session = (query.session as string) || '*';
|
||||
if (session !== '*') {
|
||||
this.logger.warn(
|
||||
`Only connecting to all sessions is allowed for now, use session=*, ${id}`,
|
||||
);
|
||||
const error =
|
||||
'Only connecting to all sessions is allowed for now, use session=*';
|
||||
socket.close(4001, JSON.stringify({ error }));
|
||||
return null;
|
||||
}
|
||||
|
||||
const events = ((query.events as string) || '*').split(',');
|
||||
if (
|
||||
!lodash.isEqual(events, ['*']) &&
|
||||
!lodash.isEqual(events, [WAHAEvents.SESSION_STATUS])
|
||||
) {
|
||||
this.logger.warn(
|
||||
`Only \'session.status\' event is allowed for now, use events=session.status or events=*, ${id}`,
|
||||
);
|
||||
const error =
|
||||
"Only 'session.status' event is allowed for now, use events=session.status or events=*";
|
||||
socket.close(4001, JSON.stringify({ error }));
|
||||
return null;
|
||||
}
|
||||
return { session, events };
|
||||
}
|
||||
|
||||
handleDisconnect(socket: WebSocket): any {
|
||||
this.logger.debug(`Client disconnected - ${socket.id}`);
|
||||
this.listeners.delete(socket);
|
||||
}
|
||||
|
||||
async beforeApplicationShutdown(signal?: string) {
|
||||
this.logger.log('Shutting down websocket server');
|
||||
// Allow pending messages to be sent, it can be even 1ms, just to release the event loop
|
||||
await sleep(100);
|
||||
this.server?.clients.forEach((options, client) => {
|
||||
// @ts-ignore
|
||||
this.logger.debug(`Closing client connection - ${client.id}...`);
|
||||
client.close(1001, 'Server is shutting down');
|
||||
});
|
||||
// Do not turn off heartbeat service here,
|
||||
// it's responsible for terminating the connection that is not alive
|
||||
this.logger.log('Websocket server is down');
|
||||
}
|
||||
|
||||
afterInit(server: Server) {
|
||||
this.logger.debug('Websocket server initialized');
|
||||
this.manager.events.on(
|
||||
WAHAEvents.SESSION_STATUS,
|
||||
this.sendToAll.bind(this),
|
||||
);
|
||||
this.logger.debug('Subscribed to manager events');
|
||||
|
||||
this.logger.debug('Starting heartbeat service...');
|
||||
this.heartbeat.start(server);
|
||||
this.logger.debug('Heartbeat service started');
|
||||
}
|
||||
|
||||
sendToAll(data: any) {
|
||||
this.listeners.forEach((options, client) => {
|
||||
if (options.events.length === 0) {
|
||||
return;
|
||||
}
|
||||
this.logger.debug('Sending data to client', data);
|
||||
client.send(JSON.stringify(data));
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,161 @@
|
||||
import { INestApplication, Module } from '@nestjs/common';
|
||||
import { ConfigModule } from '@nestjs/config';
|
||||
import { APP_INTERCEPTOR } from '@nestjs/core';
|
||||
import { PassportModule } from '@nestjs/passport';
|
||||
import { ServeStaticModule } from '@nestjs/serve-static';
|
||||
import { TerminusModule } from '@nestjs/terminus';
|
||||
import { ChannelsController } from '@waha/api/channels.controller';
|
||||
import {
|
||||
ServerController,
|
||||
ServerDebugController,
|
||||
} from '@waha/api/server.controller';
|
||||
import { WebsocketGatewayCore } from '@waha/core/api/websocket.gateway.core';
|
||||
import { MediaLocalStorageConfig } from '@waha/core/media/local/MediaLocalStorageConfig';
|
||||
import { BufferJsonReplacerInterceptor } from '@waha/nestjs/BufferJsonReplacerInterceptor';
|
||||
import {
|
||||
getPinoHttpUseLevel,
|
||||
getPinoLogLevel,
|
||||
getPinoTransport,
|
||||
} from '@waha/utils/logging';
|
||||
import * as Joi from 'joi';
|
||||
import { LoggerModule } from 'nestjs-pino';
|
||||
import { join } from 'path';
|
||||
import { Logger } from 'pino';
|
||||
|
||||
import { AuthController } from '../api/auth.controller';
|
||||
import { ChatsController } from '../api/chats.controller';
|
||||
import { ChattingController } from '../api/chatting.controller';
|
||||
import { ContactsController } from '../api/contacts.controller';
|
||||
import { GroupsController } from '../api/groups.controller';
|
||||
import { HealthController } from '../api/health.controller';
|
||||
import { LabelsController } from '../api/labels.controller';
|
||||
import { PingController } from '../api/ping.controller';
|
||||
import { PresenceController } from '../api/presence.controller';
|
||||
import { ScreenshotController } from '../api/screenshot.controller';
|
||||
import { SessionsController } from '../api/sessions.controller';
|
||||
import { StatusController } from '../api/status.controller';
|
||||
import { VersionController } from '../api/version.controller';
|
||||
import { WhatsappConfigService } from '../config.service';
|
||||
import { SessionManager } from './abc/manager.abc';
|
||||
import { WAHAHealthCheckService } from './abc/WAHAHealthCheckService';
|
||||
import { DashboardConfigServiceCore } from './config/DashboardConfigServiceCore';
|
||||
import { EngineConfigService } from './config/EngineConfigService';
|
||||
import { SwaggerConfigServiceCore } from './config/SwaggerConfigServiceCore';
|
||||
import { WAHAHealthCheckServiceCore } from './health/WAHAHealthCheckServiceCore';
|
||||
import { SessionManagerCore } from './manager.core';
|
||||
|
||||
export const IMPORTS = [
|
||||
LoggerModule.forRoot({
|
||||
renameContext: 'name',
|
||||
pinoHttp: {
|
||||
quietReqLogger: true,
|
||||
level: getPinoLogLevel(),
|
||||
useLevel: getPinoHttpUseLevel(),
|
||||
transport: getPinoTransport(),
|
||||
autoLogging: {
|
||||
ignore: (req) => {
|
||||
return (
|
||||
req.url.startsWith('/dashboard/') ||
|
||||
req.url.startsWith('/api/files/') ||
|
||||
req.url.startsWith('/api/s3/')
|
||||
);
|
||||
},
|
||||
},
|
||||
serializers: {
|
||||
req: (req) => ({
|
||||
id: req.id,
|
||||
method: req.method,
|
||||
url: req.url,
|
||||
query: req.query,
|
||||
params: req.params,
|
||||
}),
|
||||
res: (res) => ({
|
||||
statusCode: res.statusCode,
|
||||
}),
|
||||
},
|
||||
},
|
||||
}),
|
||||
ConfigModule.forRoot({
|
||||
isGlobal: true,
|
||||
validationSchema: Joi.object({
|
||||
WHATSAPP_API_SCHEMA: Joi.string().valid('http', 'https').default('http'),
|
||||
}),
|
||||
}),
|
||||
ServeStaticModule.forRootAsync({
|
||||
imports: [],
|
||||
extraProviders: [DashboardConfigServiceCore],
|
||||
inject: [DashboardConfigServiceCore],
|
||||
useFactory: (dashboardConfig: DashboardConfigServiceCore) => {
|
||||
if (!dashboardConfig.enabled) {
|
||||
return [];
|
||||
}
|
||||
return [
|
||||
{
|
||||
rootPath: join(__dirname, '..', 'dashboard'),
|
||||
serveRoot: dashboardConfig.dashboardUri,
|
||||
},
|
||||
];
|
||||
},
|
||||
}),
|
||||
PassportModule,
|
||||
TerminusModule,
|
||||
];
|
||||
export const CONTROLLERS = [
|
||||
AuthController,
|
||||
SessionsController,
|
||||
ChattingController,
|
||||
ChatsController,
|
||||
ChannelsController,
|
||||
StatusController,
|
||||
LabelsController,
|
||||
ContactsController,
|
||||
GroupsController,
|
||||
PresenceController,
|
||||
ScreenshotController,
|
||||
PingController,
|
||||
HealthController,
|
||||
ServerController,
|
||||
ServerDebugController,
|
||||
VersionController,
|
||||
];
|
||||
const PROVIDERS = [
|
||||
{
|
||||
provide: SessionManager,
|
||||
useClass: SessionManagerCore,
|
||||
},
|
||||
{
|
||||
provide: WAHAHealthCheckService,
|
||||
useClass: WAHAHealthCheckServiceCore,
|
||||
},
|
||||
{
|
||||
provide: APP_INTERCEPTOR,
|
||||
useClass: BufferJsonReplacerInterceptor,
|
||||
},
|
||||
DashboardConfigServiceCore,
|
||||
SwaggerConfigServiceCore,
|
||||
WhatsappConfigService,
|
||||
EngineConfigService,
|
||||
WebsocketGatewayCore,
|
||||
MediaLocalStorageConfig,
|
||||
];
|
||||
|
||||
@Module({
|
||||
imports: IMPORTS,
|
||||
controllers: CONTROLLERS,
|
||||
providers: PROVIDERS,
|
||||
})
|
||||
export class AppModuleCore {
|
||||
public startTimestamp: number;
|
||||
|
||||
constructor(protected config: WhatsappConfigService) {
|
||||
this.startTimestamp = Date.now();
|
||||
}
|
||||
|
||||
static getHttpsOptions(logger: Logger) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
static appReady(app: INestApplication, logger: Logger) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
import { Injectable, Logger } from '@nestjs/common';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import { InjectPinoLogger, PinoLogger } from 'nestjs-pino';
|
||||
|
||||
import { parseBool } from '../../helpers';
|
||||
|
||||
@Injectable()
|
||||
export class DashboardConfigServiceCore {
|
||||
public dashboardUri = '/dashboard';
|
||||
|
||||
constructor(
|
||||
protected configService: ConfigService,
|
||||
@InjectPinoLogger('DashboardConfigService')
|
||||
protected logger: PinoLogger,
|
||||
) {}
|
||||
|
||||
get enabled(): boolean {
|
||||
const value = this.configService.get('WAHA_DASHBOARD_ENABLED', 'true');
|
||||
return parseBool(value);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
import { Injectable, Logger } from '@nestjs/common';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
|
||||
import { parseBool } from '../../helpers';
|
||||
import { WAHAEngine } from '../../structures/enums.dto';
|
||||
import { getEngineName } from '../../version';
|
||||
|
||||
@Injectable()
|
||||
export class EngineConfigService {
|
||||
private logger: Logger;
|
||||
|
||||
constructor(protected configService: ConfigService) {
|
||||
this.logger = new Logger('EngineConfigService');
|
||||
}
|
||||
|
||||
getDefaultEngineName(): WAHAEngine {
|
||||
const value = getEngineName();
|
||||
if (value in WAHAEngine) {
|
||||
return WAHAEngine[value];
|
||||
}
|
||||
this.logger.warn(
|
||||
`Unknown WhatsApp default engine WHATSAPP_DEFAULT_ENGINE=${value}. Using WEBJS`,
|
||||
);
|
||||
return WAHAEngine.WEBJS;
|
||||
}
|
||||
|
||||
get shouldPrintQR(): boolean {
|
||||
const value = this.configService.get('WAHA_PRINT_QR', true);
|
||||
return parseBool(value);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import { InjectPinoLogger, PinoLogger } from 'nestjs-pino';
|
||||
|
||||
import { parseBool } from '../../helpers';
|
||||
|
||||
@Injectable()
|
||||
export class SwaggerConfigServiceCore {
|
||||
constructor(
|
||||
protected configService: ConfigService,
|
||||
@InjectPinoLogger('SwaggerConfigService')
|
||||
protected logger: PinoLogger,
|
||||
) {}
|
||||
|
||||
get advancedConfigEnabled(): boolean {
|
||||
const value = this.configService.get(
|
||||
'WHATSAPP_SWAGGER_CONFIG_ADVANCED',
|
||||
false,
|
||||
);
|
||||
return parseBool(value);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
import { DataStore } from '../../abc/DataStore';
|
||||
import { LocalStore } from '../../storage/LocalStore';
|
||||
import { useMultiFileAuthState } from './useMultiFileAuthState';
|
||||
|
||||
export class NowebAuthFactoryCore {
|
||||
buildAuth(store: DataStore, name: string) {
|
||||
if (store instanceof LocalStore) return this.buildLocalAuth(store, name);
|
||||
throw new Error(`Unsupported store type '${store.constructor.name}'`);
|
||||
}
|
||||
|
||||
protected async buildLocalAuth(store: LocalStore, name: string) {
|
||||
await store.init(name);
|
||||
const authFolder = store.getSessionDirectory(name);
|
||||
const { state, saveCreds } = await useMultiFileAuthState(authFolder);
|
||||
return { state, saveCreds };
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,17 @@
|
||||
import { Chat } from '@adiwajshing/baileys';
|
||||
|
||||
export interface IChatRepository {
|
||||
getAll(): Promise<Chat[]>;
|
||||
|
||||
getAllByIds(ids: string[]): Promise<Chat[]>;
|
||||
|
||||
getAllWithMessages(limit?: number, offset?: number): Promise<Chat[]>;
|
||||
|
||||
getById(id: string): Promise<Chat | null>;
|
||||
|
||||
deleteAll(): Promise<void>;
|
||||
|
||||
deleteById(id: string): Promise<void>;
|
||||
|
||||
save(chat: Chat): Promise<void>;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import { Contact } from '@adiwajshing/baileys';
|
||||
|
||||
export interface IContactRepository {
|
||||
getAll(): Promise<Contact[]>;
|
||||
|
||||
getById(id: string): Promise<Contact | null>;
|
||||
|
||||
deleteAll(): Promise<void>;
|
||||
|
||||
deleteById(id: string): Promise<void>;
|
||||
|
||||
save(contact: Contact): Promise<void>;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
import {
|
||||
LabelAssociation,
|
||||
LabelAssociationType,
|
||||
} from '@adiwajshing/baileys/lib/Types/LabelAssociation';
|
||||
|
||||
export interface ILabelAssociationRepository {
|
||||
deleteOne(association: LabelAssociation): Promise<void>;
|
||||
|
||||
save(association: LabelAssociation): Promise<void>;
|
||||
|
||||
deleteByLabelId(labelId: string): Promise<void>;
|
||||
|
||||
getAssociationsByLabelId(
|
||||
labelId: string,
|
||||
type: LabelAssociationType,
|
||||
): Promise<LabelAssociation[]>;
|
||||
|
||||
getAssociationsByChatId(chatId: string): Promise<LabelAssociation[]>;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
import { Contact } from '@adiwajshing/baileys';
|
||||
import { Label } from '@adiwajshing/baileys/lib/Types/Label';
|
||||
|
||||
export interface ILabelsRepository {
|
||||
getById(id: string): Promise<Label | null>;
|
||||
|
||||
getAll(): Promise<Label[]>;
|
||||
|
||||
getAllByIds(ids: string[]): Promise<Label[]>;
|
||||
|
||||
deleteById(id: string): Promise<void>;
|
||||
|
||||
save(label: Label): Promise<void>;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
export interface IMessagesRepository {
|
||||
deleteAll(): Promise<void>;
|
||||
|
||||
upsert(messages: any[]): Promise<void>;
|
||||
|
||||
upsertOne(message: any): Promise<void>;
|
||||
|
||||
getAllByJid(jid: string, limit: number): Promise<any[]>;
|
||||
|
||||
getByJidById(jid: string, id: string): Promise<any | null>;
|
||||
|
||||
updateByJidAndId(jid: string, id: string, update: any): Promise<boolean>;
|
||||
|
||||
deleteByJidByIds(jid: string, ids: string[]): Promise<void>;
|
||||
|
||||
deleteAllByJid(jid: string): Promise<void>;
|
||||
}
|
||||
Loaded 100 of 191 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user