mirror of
https://github.com/EvolutionAPI/evolution-api.git
synced 2025-12-19 03:42:23 -06:00
Compare commits
837 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5975117636 | ||
|
|
51b285f665 | ||
|
|
b3958d4735 | ||
|
|
128119d494 | ||
|
|
324dc01699 | ||
|
|
f11d468e31 | ||
|
|
aa5ed13752 | ||
|
|
10ce7da18d | ||
|
|
73bd55dfac | ||
|
|
db10f81d53 | ||
|
|
717aac4438 | ||
|
|
077a464481 | ||
|
|
9bf18592fc | ||
|
|
677e196c96 | ||
|
|
19e6896f0d | ||
|
|
ba537baab2 | ||
|
|
2b8b6b086b | ||
|
|
cf3ec2b601 | ||
|
|
35eaa7852c | ||
|
|
b87558d301 | ||
|
|
9f1003e94e | ||
|
|
73c003907b | ||
|
|
b65d292af4 | ||
|
|
43bdbf1018 | ||
|
|
ec7986420d | ||
|
|
0b23153316 | ||
|
|
ad4f5e5e65 | ||
|
|
1d48532146 | ||
|
|
950803b2aa | ||
|
|
a41d92f4da | ||
|
|
70d4eb393f | ||
|
|
9f162127d0 | ||
|
|
4cd30dabc4 | ||
|
|
8f78728863 | ||
|
|
e7c5d33d50 | ||
|
|
5400f31acb | ||
|
|
e58f1d778e | ||
|
|
5f5db2011e | ||
|
|
aea4d89f62 | ||
|
|
ccda17d704 | ||
|
|
4ab6c438cf | ||
|
|
219e63c226 | ||
|
|
ee1e4623a3 | ||
|
|
5982212903 | ||
|
|
32e6ecb12d | ||
|
|
3b774558f4 | ||
|
|
072171da18 | ||
|
|
8292221790 | ||
|
|
901954de33 | ||
|
|
286fe03500 | ||
|
|
9c4f02634b | ||
|
|
434f39de1f | ||
|
|
e72b543f38 | ||
|
|
826e818802 | ||
|
|
cbf846d32f | ||
|
|
55811a39a5 | ||
|
|
a81f5953fc | ||
|
|
bd09328ec2 | ||
|
|
17ecc88f80 | ||
|
|
7bc4c7b36e | ||
|
|
79189ac5d7 | ||
|
|
060c4e6e72 | ||
|
|
8f7c518487 | ||
|
|
12761cbfce | ||
|
|
2b30c273a4 | ||
|
|
03684a893d | ||
|
|
fef27111b9 | ||
|
|
b04ed66686 | ||
|
|
f5df8bca20 | ||
|
|
060af66c09 | ||
|
|
a6adbd61db | ||
|
|
bab58054f7 | ||
|
|
e27e990cd6 | ||
|
|
196c2e0ed8 | ||
|
|
499bd4328a | ||
|
|
e389047aaf | ||
|
|
da04ff284b | ||
|
|
10b48aed97 | ||
|
|
ac6e9ae994 | ||
|
|
7dd589fb6c | ||
|
|
6b930058d1 | ||
|
|
aef3495a79 | ||
|
|
da341a95c6 | ||
|
|
bca5ec6482 | ||
|
|
f0a0cb7269 | ||
|
|
238b7618b4 | ||
|
|
4f206f67c9 | ||
|
|
fa513bf784 | ||
|
|
a68b0b3878 | ||
|
|
249489e697 | ||
|
|
e2c67d7dae | ||
|
|
703bc310a7 | ||
|
|
4caecfa680 | ||
|
|
fd4fde2543 | ||
|
|
763c5de03f | ||
|
|
4e160a46dd | ||
|
|
56aaf18663 | ||
|
|
df841aed27 | ||
|
|
d4372a0332 | ||
|
|
1595da789d | ||
|
|
7e65cb1d19 | ||
|
|
a931ee7afb | ||
|
|
bc6944736e | ||
|
|
16c2e28e9c | ||
|
|
ab89ef8db3 | ||
|
|
9ef14d11f8 | ||
|
|
e753990da3 | ||
|
|
f469c2e65d | ||
|
|
0525501b87 | ||
|
|
3a37fd9d32 | ||
|
|
b095c9dfb9 | ||
|
|
cd00effcfe | ||
|
|
1cd0334ccd | ||
|
|
3b1a16844e | ||
|
|
6995e8a451 | ||
|
|
1dd0f319fc | ||
|
|
5ce96369cf | ||
|
|
0791d78e28 | ||
|
|
cecbb7c34e | ||
|
|
525daff5fe | ||
|
|
b2c51b4b8c | ||
|
|
ffddb05ba3 | ||
|
|
4bb81b9a41 | ||
|
|
3df4e8d2ba | ||
|
|
3edef873bc | ||
|
|
c04b5cb851 | ||
|
|
fabd717d92 | ||
|
|
945bcf5fad | ||
|
|
d35d755379 | ||
|
|
a2622cb38e | ||
|
|
b58fd78450 | ||
|
|
e8d32066b4 | ||
|
|
6d3779eb83 | ||
|
|
c7bed04c80 | ||
|
|
b0e956cfa9 | ||
|
|
8608b7cded | ||
|
|
23f1b4ac03 | ||
|
|
27900c214f | ||
|
|
704701e251 | ||
|
|
34c53d352a | ||
|
|
32026d1fd4 | ||
|
|
35cdce0d52 | ||
|
|
e59098cf61 | ||
|
|
d8ca480b19 | ||
|
|
803b123ade | ||
|
|
cffb673fba | ||
|
|
1f6535d61b | ||
|
|
8a5ebe83a3 | ||
|
|
cc17d61016 | ||
|
|
0547ff719c | ||
|
|
c130846fe8 | ||
|
|
b3adde3a7a | ||
|
|
54603002a6 | ||
|
|
b995cdfc32 | ||
|
|
b09546577a | ||
|
|
6e84c1dc4c | ||
|
|
f41f3aaba8 | ||
|
|
8fa61e4632 | ||
|
|
7dacd752d3 | ||
|
|
7439d2401d | ||
|
|
dfb1ee0c56 | ||
|
|
3da73b821d | ||
|
|
66b82ac10a | ||
|
|
058acc5042 | ||
|
|
cdf822291f | ||
|
|
3f9e872e1c | ||
|
|
94aa3067f6 | ||
|
|
bff8064597 | ||
|
|
6b2d4e2585 | ||
|
|
56dfc2ae82 | ||
|
|
535d5ee47f | ||
|
|
838905f3dd | ||
|
|
59f5208c5c | ||
|
|
32a1a715ea | ||
|
|
3755d3870e | ||
|
|
0edc8a9284 | ||
|
|
5449d63602 | ||
|
|
dfa72fd6af | ||
|
|
0e50da324b | ||
|
|
679f8118f6 | ||
|
|
804d177782 | ||
|
|
eb96c9fece | ||
|
|
ed6c50621c | ||
|
|
7f74de07ed | ||
|
|
9e5bf93580 | ||
|
|
9d685da12d | ||
|
|
6bb40eb502 | ||
|
|
1ceee572cf | ||
|
|
391ffea4ab | ||
|
|
5292e569d9 | ||
|
|
82e111f1be | ||
|
|
1e9b3a1e42 | ||
|
|
35520d85a2 | ||
|
|
e19e37eef4 | ||
|
|
814795f566 | ||
|
|
1a633dcf10 | ||
|
|
c9b0e66641 | ||
|
|
440ff2f3ea | ||
|
|
96be63f50b | ||
|
|
69a323691f | ||
|
|
4357fcf7ef | ||
|
|
5f79513617 | ||
|
|
ba3ccbbc9a | ||
|
|
f182436673 | ||
|
|
793b907fe6 | ||
|
|
c75dfcd499 | ||
|
|
29a48f7914 | ||
|
|
095a8aa9dd | ||
|
|
2d8b5f04e9 | ||
|
|
ba9f97bc3e | ||
|
|
ed51f44ee8 | ||
|
|
933d787108 | ||
|
|
0bc12733a3 | ||
|
|
8cfd9c44ab | ||
|
|
3ddff84be0 | ||
|
|
afb5361a1b | ||
|
|
f376047632 | ||
|
|
7373eea842 | ||
|
|
a446df4620 | ||
|
|
306c6dd21d | ||
|
|
380ba8860c | ||
|
|
6c8c8ddcfc | ||
|
|
0c09c5e140 | ||
|
|
b761fba8cb | ||
|
|
dfceda3942 | ||
|
|
85d1825236 | ||
|
|
8f8c7e26c7 | ||
|
|
181768d91f | ||
|
|
2dcd4d8fd3 | ||
|
|
d909550134 | ||
|
|
c564ec41e2 | ||
|
|
af94a0e174 | ||
|
|
fcd8815fca | ||
|
|
5bd3f28117 | ||
|
|
384bde333e | ||
|
|
d06ec604b9 | ||
|
|
d8ce23f2a0 | ||
|
|
3ccb983377 | ||
|
|
19fb9fcd31 | ||
|
|
5f4a1b96ce | ||
|
|
6983f385fc | ||
|
|
2aadd1cac5 | ||
|
|
bfa7d429bd | ||
|
|
3238150b92 | ||
|
|
3603571967 | ||
|
|
7c2a8c0abb | ||
|
|
8b5f73badd | ||
|
|
ff57fd3d23 | ||
|
|
49aa1ea17c | ||
|
|
7430897085 | ||
|
|
82894a1c4f | ||
|
|
3bb4217a45 | ||
|
|
dfc8330035 | ||
|
|
45e03d87c7 | ||
|
|
3e358e5d26 | ||
|
|
dc5dae04eb | ||
|
|
9128b1f47d | ||
|
|
16a8226ba7 | ||
|
|
9ecaf3199d | ||
|
|
a44cd0373e | ||
|
|
2c0b629302 | ||
|
|
89a37a1771 | ||
|
|
cadc038966 | ||
|
|
07e8449379 | ||
|
|
71e908ad1a | ||
|
|
035c85b775 | ||
|
|
a87b753151 | ||
|
|
edaa4aff7e | ||
|
|
be02610349 | ||
|
|
1f65731165 | ||
|
|
fb61fb7849 | ||
|
|
060a945aea | ||
|
|
2797250f34 | ||
|
|
97b3f9b3c7 | ||
|
|
9363301d2a | ||
|
|
d93a826e28 | ||
|
|
244fe0835e | ||
|
|
5f1a5d6589 | ||
|
|
4e27c22292 | ||
|
|
53ee270096 | ||
|
|
a00ad20c08 | ||
|
|
e8764dd1c6 | ||
|
|
a869d38499 | ||
|
|
7bf7c96587 | ||
|
|
72b857a92f | ||
|
|
380d6a43a5 | ||
|
|
076f2b492e | ||
|
|
547f981c47 | ||
|
|
8bfc62a3b2 | ||
|
|
d39776a314 | ||
|
|
35641d0543 | ||
|
|
038cd6f149 | ||
|
|
38978dd447 | ||
|
|
fb24b7eaa7 | ||
|
|
b4ce45bc4b | ||
|
|
8d04198309 | ||
|
|
da796347c4 | ||
|
|
2d6a29664a | ||
|
|
4ba5cfceaf | ||
|
|
7cc324e1c0 | ||
|
|
cf89601269 | ||
|
|
c07e23bf8d | ||
|
|
5aa89d85f3 | ||
|
|
4ed1edf53d | ||
|
|
1be1326b52 | ||
|
|
42ae7d1568 | ||
|
|
b781c83545 | ||
|
|
e48cea18e7 | ||
|
|
ff82987144 | ||
|
|
f612a45550 | ||
|
|
182dce4840 | ||
|
|
4e41e072d6 | ||
|
|
a369c16db8 | ||
|
|
1fc820787a | ||
|
|
d3a83ba89e | ||
|
|
20fb66e2f7 | ||
|
|
a44646161b | ||
|
|
87027ea2d0 | ||
|
|
c9757cbb4b | ||
|
|
9a8f4aefe0 | ||
|
|
3545b80050 | ||
|
|
379855714e | ||
|
|
ff06cd7643 | ||
|
|
ade3952016 | ||
|
|
f246516a6e | ||
|
|
c296bf4178 | ||
|
|
1568554a1c | ||
|
|
ae66be197e | ||
|
|
3e904aa160 | ||
|
|
64c1440c46 | ||
|
|
f069a41390 | ||
|
|
d4a33e2290 | ||
|
|
7ee5bcecff | ||
|
|
48f6ee8846 | ||
|
|
7a24f52782 | ||
|
|
324d46120b | ||
|
|
87baec5ff8 | ||
|
|
b2e144f35c | ||
|
|
87a8e25662 | ||
|
|
8e88f00fb2 | ||
|
|
d14505d59a | ||
|
|
1e6d4347fa | ||
|
|
b8d9a8c072 | ||
|
|
fd15ae5e8c | ||
|
|
fb6377414b | ||
|
|
9a5dbe055e | ||
|
|
42dd280aca | ||
|
|
41b2946cdc | ||
|
|
a90f0f2c59 | ||
|
|
4222c0e53b | ||
|
|
ee0f0f0be0 | ||
|
|
f8d874453c | ||
|
|
aa891489f0 | ||
|
|
4c69b059d4 | ||
|
|
359bd9f762 | ||
|
|
2de0b61726 | ||
|
|
d75163aa57 | ||
|
|
e49f30641e | ||
|
|
1631c2c342 | ||
|
|
cf7de369b2 | ||
|
|
78c03d8f2f | ||
|
|
876320b849 | ||
|
|
a1d13f8ff3 | ||
|
|
3c19bdfaa9 | ||
|
|
4fa895086e | ||
|
|
9945d8debb | ||
|
|
4362de2198 | ||
|
|
a5c5879e4f | ||
|
|
1a57f4f33d | ||
|
|
a99e173168 | ||
|
|
e02a28f61e | ||
|
|
26d3ff97ce | ||
|
|
57fb3c9785 | ||
|
|
edeb970a82 | ||
|
|
e17baddf01 | ||
|
|
a277d36696 | ||
|
|
6c9e86e17a | ||
|
|
e75ef21eb6 | ||
|
|
04e5443b82 | ||
|
|
8b4cdf3b9b | ||
|
|
37f1620f7c | ||
|
|
b0a0e805cf | ||
|
|
c619e253a2 | ||
|
|
2bd111f1e2 | ||
|
|
40174b50eb | ||
|
|
e0fe28717f | ||
|
|
ac5fc22043 | ||
|
|
e30f196dad | ||
|
|
9e4e1ce8ec | ||
|
|
f710898844 | ||
|
|
94633484ca | ||
|
|
0817c2589f | ||
|
|
8a99386b33 | ||
|
|
52d6a563d6 | ||
|
|
d0a5ae1da4 | ||
|
|
783c00a1d9 | ||
|
|
bc70ec8b07 | ||
|
|
50e1efe5d7 | ||
|
|
d8629e53f1 | ||
|
|
cc9df1dabb | ||
|
|
cd6cb8182e | ||
|
|
5b0e90e5b9 | ||
|
|
3c3bbc84b3 | ||
|
|
23615cff4f | ||
|
|
daadc6cb68 | ||
|
|
e157a2a36b | ||
|
|
d8d7debfee | ||
|
|
a82b206fe6 | ||
|
|
a4416214c8 | ||
|
|
8fe75cd210 | ||
|
|
303effebbc | ||
|
|
f32a34190d | ||
|
|
8588ef1d8a | ||
|
|
51ec4821f3 | ||
|
|
957033a7bb | ||
|
|
62c74deac3 | ||
|
|
29fd448998 | ||
|
|
e55cb08a6a | ||
|
|
047359e8dc | ||
|
|
eb814e181a | ||
|
|
857031ff5a | ||
|
|
d5eeb68714 | ||
|
|
966b287026 | ||
|
|
a348729109 | ||
|
|
1f29b7733e | ||
|
|
e26ae30f6f | ||
|
|
547943a05c | ||
|
|
46aa229531 | ||
|
|
28bd796289 | ||
|
|
6a3f82ed7e | ||
|
|
523f3301c0 | ||
|
|
f085343a99 | ||
|
|
f76a924700 | ||
|
|
f8e3b76a4a | ||
|
|
b8f1e8a7ef | ||
|
|
d007fc49d8 | ||
|
|
f6d8ebd8d3 | ||
|
|
6ff9c4578a | ||
|
|
33acfe1464 | ||
|
|
f5eeb16bb1 | ||
|
|
c35c5faaa4 | ||
|
|
8425ebc13f | ||
|
|
6fc37a4298 | ||
|
|
99f3e77c12 | ||
|
|
a9c087c45f | ||
|
|
3f4333087f | ||
|
|
e1ac29683d | ||
|
|
5c74cbfe19 | ||
|
|
3fdb3fa673 | ||
|
|
ba584974cb | ||
|
|
bddd6408ac | ||
|
|
413ad66a07 | ||
|
|
0ef5d884cc | ||
|
|
f6b6d23e93 | ||
|
|
50be69f3d3 | ||
|
|
5a75e4d5e6 | ||
|
|
ec463df9d6 | ||
|
|
b648334323 | ||
|
|
916972aeb1 | ||
|
|
3893e01af9 | ||
|
|
7835f32c8b | ||
|
|
37302244ee | ||
|
|
f1be7ddb83 | ||
|
|
7447a65a83 | ||
|
|
129009d602 | ||
|
|
c656bd6f4b | ||
|
|
36528d7484 | ||
|
|
f9c85b6a67 | ||
|
|
4dfe6bdbe8 | ||
|
|
ee343f2fa1 | ||
|
|
1a1f5f85b2 | ||
|
|
bd64b0c884 | ||
|
|
e1c8928ed9 | ||
|
|
1f98940445 | ||
|
|
707aa22a6d | ||
|
|
32da15fa8a | ||
|
|
97cd6e289a | ||
|
|
def6576fb9 | ||
|
|
196c10b253 | ||
|
|
cfdca38d59 | ||
|
|
ccd90a69ee | ||
|
|
c16f962d2b | ||
|
|
8fccf69ceb | ||
|
|
adc8833670 | ||
|
|
ecbf90ded8 | ||
|
|
c5d2d7782a | ||
|
|
72dae22ef4 | ||
|
|
9a9cd41009 | ||
|
|
0c4c8162c2 | ||
|
|
d47cc5d5f4 | ||
|
|
cb942e512d | ||
|
|
bd0a479645 | ||
|
|
cd0da914f4 | ||
|
|
7a7e72897a | ||
|
|
c1542054c9 | ||
|
|
250e67e7ae | ||
|
|
cdbe839b35 | ||
|
|
240a77dcce | ||
|
|
1dc5bb8bd1 | ||
|
|
23534da27d | ||
|
|
8652d4031c | ||
|
|
384e311c7a | ||
|
|
2791f88b4c | ||
|
|
402d5af19a | ||
|
|
b554d8c19c | ||
|
|
7d9dd64303 | ||
|
|
41bea8931f | ||
|
|
f83d8de476 | ||
|
|
af2a652098 | ||
|
|
a3c911263d | ||
|
|
836c677837 | ||
|
|
7e4dbfdd7e | ||
|
|
6eda556242 | ||
|
|
3ea454c7ed | ||
|
|
9123d7014d | ||
|
|
3ea3abe81a | ||
|
|
7289c3d7f9 | ||
|
|
d00e1df29c | ||
|
|
16a18c4f22 | ||
|
|
30b156d92f | ||
|
|
931f9d33e4 | ||
|
|
e61fe4d092 | ||
|
|
5bc33ac654 | ||
|
|
878ba5a869 | ||
|
|
92632b2b96 | ||
|
|
d803a9e298 | ||
|
|
b9f67533dd | ||
|
|
7ade78bedf | ||
|
|
b4eca48f4d | ||
|
|
2cf8e317fa | ||
|
|
821a422ab5 | ||
|
|
91c7b4f2cd | ||
|
|
adb43ec5b3 | ||
|
|
c9721d7bc9 | ||
|
|
bbc2b8a396 | ||
|
|
54cfa67d52 | ||
|
|
9b72b3e332 | ||
|
|
c03919be2d | ||
|
|
706cc6f49c | ||
|
|
10c7e81e02 | ||
|
|
03637b2d4d | ||
|
|
b7218a05be | ||
|
|
a2cd57d9c6 | ||
|
|
001849eeaa | ||
|
|
bf09a70096 | ||
|
|
530aec92a9 | ||
|
|
deb8f2a0b7 | ||
|
|
5c247e3d2c | ||
|
|
04b9a070c4 | ||
|
|
dd2caf720c | ||
|
|
0d16a7aab0 | ||
|
|
31325d0999 | ||
|
|
6f99784224 | ||
|
|
201e6f7e7b | ||
|
|
c4d41134b8 | ||
|
|
680c92ecec | ||
|
|
deb07d2b7f | ||
|
|
3a14fc373a | ||
|
|
29e429a02e | ||
|
|
907a0ee135 | ||
|
|
22a03f77ab | ||
|
|
9761b10bf6 | ||
|
|
c364d3fdca | ||
|
|
07ad5756eb | ||
|
|
6e401eecde | ||
|
|
f32e259d2f | ||
|
|
83ed0e6454 | ||
|
|
da568e4ea5 | ||
|
|
ad819bf3ba | ||
|
|
b502ebd23a | ||
|
|
7c5d94c19e | ||
|
|
a16b5f4644 | ||
|
|
f3cb8c531b | ||
|
|
469e696ab7 | ||
|
|
d99ccd9df6 | ||
|
|
3b3118d764 | ||
|
|
84386847e2 | ||
|
|
0da3d100b3 | ||
|
|
56d621bab8 | ||
|
|
f1571b5f66 | ||
|
|
55f8e179af | ||
|
|
ab5289a136 | ||
|
|
e05c48979d | ||
|
|
24c880343b | ||
|
|
b3b4ee7a28 | ||
|
|
e26b440a66 | ||
|
|
d6194316e1 | ||
|
|
341148612f | ||
|
|
79864e97d6 | ||
|
|
ed5e66e430 | ||
|
|
074a861fb4 | ||
|
|
b88656829e | ||
|
|
aefe6a5943 | ||
|
|
3d743f8498 | ||
|
|
0186fff67d | ||
|
|
38f61cdf75 | ||
|
|
84366002db | ||
|
|
eff5bb74b5 | ||
|
|
2614088fae | ||
|
|
3699e04db9 | ||
|
|
0dca009c01 | ||
|
|
1b39eb1a23 | ||
|
|
2637aebb7f | ||
|
|
8afcfde078 | ||
|
|
9ea1eaf3ed | ||
|
|
66d06afaf7 | ||
|
|
cffcca9722 | ||
|
|
fe2b9774d8 | ||
|
|
3d02fabef4 | ||
|
|
f89c2b1f63 | ||
|
|
ee755d5a6c | ||
|
|
65e2ecf88e | ||
|
|
332ec69ee8 | ||
|
|
1ce30f8431 | ||
|
|
3ae6944307 | ||
|
|
52533d4b38 | ||
|
|
38409d9336 | ||
|
|
d344e513c4 | ||
|
|
9af7f67930 | ||
|
|
f95d938fb6 | ||
|
|
f74a7e87bd | ||
|
|
d3fce5fc89 | ||
|
|
14f3f3d2ac | ||
|
|
127d5b97c4 | ||
|
|
7ef1c097e8 | ||
|
|
80e3116cd8 | ||
|
|
46bac55bb3 | ||
|
|
2cadafd5c1 | ||
|
|
bb27dca21c | ||
|
|
86fc9fbffa | ||
|
|
9bdbfc6f4f | ||
|
|
28a7d9c62b | ||
|
|
2ca344b17c | ||
|
|
1bf2278f31 | ||
|
|
6188ab0d30 | ||
|
|
f016b53013 | ||
|
|
127cd3e843 | ||
|
|
457dbe5831 | ||
|
|
8d73fb1161 | ||
|
|
af5746bb39 | ||
|
|
3f27d018c7 | ||
|
|
68402393b5 | ||
|
|
2ad33857f4 | ||
|
|
d3c7677dc7 | ||
|
|
73e92f9ef5 | ||
|
|
fcc8748daf | ||
|
|
312ee249b6 | ||
|
|
e4548f6961 | ||
|
|
1b93aac8c5 | ||
|
|
e7ed666037 | ||
|
|
85ca0683ed | ||
|
|
78689a23e5 | ||
|
|
ba63a55883 | ||
|
|
dc3d59bae1 | ||
|
|
0a851b935e | ||
|
|
ddc75d710f | ||
|
|
5482601b41 | ||
|
|
249aecbc0d | ||
|
|
03f3020e9f | ||
|
|
e151eb85f0 | ||
|
|
348586553e | ||
|
|
fb6e58b3c4 | ||
|
|
67e98456bb | ||
|
|
3e47420534 | ||
|
|
1d3d557c43 | ||
|
|
3f41974a75 | ||
|
|
3e3c7397a5 | ||
|
|
dcb51702e7 | ||
|
|
de0c9a1eff | ||
|
|
dd0c1e20a7 | ||
|
|
4769d75dc3 | ||
|
|
6b926dc697 | ||
|
|
ecae077c6d | ||
|
|
78ab1bed35 | ||
|
|
2b6dbfde6b | ||
|
|
6bb1abd7f0 | ||
|
|
aef92240cc | ||
|
|
f0d8c2d095 | ||
|
|
14529f2c35 | ||
|
|
89f40d54d9 | ||
|
|
4c006970a2 | ||
|
|
de676041df | ||
|
|
82b1567ae5 | ||
|
|
1cd7291068 | ||
|
|
fdee1df5b3 | ||
|
|
c314d00ccd | ||
|
|
62e2a8a6e3 | ||
|
|
a12231a0aa | ||
|
|
183efd427a | ||
|
|
f95f3126c3 | ||
|
|
4d9ca4b451 | ||
|
|
c76334a68a | ||
|
|
f475391ba6 | ||
|
|
b77f22790b | ||
|
|
757a578c6e | ||
|
|
c5824767c8 | ||
|
|
84f3f07279 | ||
|
|
f8e1892eee | ||
|
|
036a8edca0 | ||
|
|
58ed6f395f | ||
|
|
ef4be6a612 | ||
|
|
3d8e6f4394 | ||
|
|
0cc1f18a7e | ||
|
|
8b6e577b8f | ||
|
|
cc91f2e5db | ||
|
|
8d1f2313ac | ||
|
|
f9abd90cc9 | ||
|
|
f7293255cf | ||
|
|
7d6a130cf9 | ||
|
|
be7bb2e39f | ||
|
|
1c30728880 | ||
|
|
8d91e7cb1d | ||
|
|
68d980795a | ||
|
|
45c11a5a8e | ||
|
|
4d00351db7 | ||
|
|
fff420b652 | ||
|
|
7103a95305 | ||
|
|
bcada5d553 | ||
|
|
c9b24ff612 | ||
|
|
854c7ed04d | ||
|
|
c0054959cd | ||
|
|
1aa837d220 | ||
|
|
95df402c4c | ||
|
|
2f3d6f7e63 | ||
|
|
ffe1523170 | ||
|
|
a73d5f4b4d | ||
|
|
f35b62ed12 | ||
|
|
20abdd2908 | ||
|
|
28c2c7285c | ||
|
|
3ca8ab12a4 | ||
|
|
fd82aa143c | ||
|
|
1fcbd4f9fd | ||
|
|
73d9cd62a5 | ||
|
|
be699d24a1 | ||
|
|
798eb90bed | ||
|
|
c252f5f8d9 | ||
|
|
76d77ad76f | ||
|
|
69f5cdd61a | ||
|
|
9f52f20660 | ||
|
|
90048afa9d | ||
|
|
1ec3ed32ee | ||
|
|
16ed5821e2 | ||
|
|
b681e33944 | ||
|
|
8f4d44a212 | ||
|
|
93a5d07f9a | ||
|
|
40c230c7db | ||
|
|
d0fa3b92f8 | ||
|
|
2a7727cf5f | ||
|
|
98722e7acf | ||
|
|
683fe4c3db | ||
|
|
e851696430 | ||
|
|
b2ccf965bb | ||
|
|
f847f38812 | ||
|
|
19039aa281 | ||
|
|
091b920a22 | ||
|
|
d7f264c1c2 | ||
|
|
897f8164b9 | ||
|
|
04a6f7c954 | ||
|
|
796287a776 | ||
|
|
763e30bd1d | ||
|
|
5121374d60 | ||
|
|
3e3a175bdc | ||
|
|
4a1aa9130b | ||
|
|
5a3f5f60b6 | ||
|
|
8c1600be55 | ||
|
|
7d3ae2347b | ||
|
|
27add47db4 | ||
|
|
836bcab036 | ||
|
|
2d20a07dfb | ||
|
|
24c5c70466 | ||
|
|
22ead22499 | ||
|
|
65e1620b43 | ||
|
|
acac09375e | ||
|
|
83cf859da3 | ||
|
|
956c391f13 | ||
|
|
7767a2607e | ||
|
|
429d1ac183 | ||
|
|
f42928f88f | ||
|
|
2d816ab92d | ||
|
|
aa75380662 | ||
|
|
3cf0ced62e | ||
|
|
41fa700fb3 | ||
|
|
a4ef9fb9b7 | ||
|
|
1db23b5277 | ||
|
|
584f0cbac0 | ||
|
|
dc432a19a3 | ||
|
|
6727b1cfca | ||
|
|
8c7b0698f3 | ||
|
|
69e1622e82 | ||
|
|
af7a5d3248 | ||
|
|
7b1a4554ad | ||
|
|
b3e213c133 | ||
|
|
a9fafec79d | ||
|
|
7cacf7bbaf | ||
|
|
44bf39f7b7 | ||
|
|
0db8f7295b | ||
|
|
c1226062b1 | ||
|
|
819ed168da | ||
|
|
19940953e2 | ||
|
|
f4af3eaf5d | ||
|
|
718563fe6a | ||
|
|
7e996ad6fa | ||
|
|
0b07fb6a49 | ||
|
|
0cb87e6ed9 | ||
|
|
71a3e75ae2 | ||
|
|
a2448ea4c1 | ||
|
|
8a14141021 | ||
|
|
69353892d9 | ||
|
|
7a2fcc3469 | ||
|
|
dfa6dd5011 | ||
|
|
654400c0cf | ||
|
|
3c15fc1b0d | ||
|
|
4b32485e3c | ||
|
|
b90736ed25 | ||
|
|
f28d5c7781 | ||
|
|
0654627478 | ||
|
|
2e91a2ecdb | ||
|
|
66a8ceb27a | ||
|
|
e6b0addb6c | ||
|
|
c00f132145 | ||
|
|
b9eb8d45b2 | ||
|
|
ec7ad70458 | ||
|
|
64e19699fb | ||
|
|
6d3e1b0cbe | ||
|
|
d3a53e1d3c | ||
|
|
23bbb3ee32 | ||
|
|
56bfcd52b7 | ||
|
|
1c19b6de1e | ||
|
|
85bed0bdf1 | ||
|
|
0102796769 | ||
|
|
50b2a72f1b | ||
|
|
04cc239756 | ||
|
|
0c20da2481 | ||
|
|
72d2a563f3 | ||
|
|
6f3f8c944d | ||
|
|
ed60fd2e82 | ||
|
|
636fef4693 |
14
.eslintrc.js
14
.eslintrc.js
@@ -3,10 +3,14 @@ module.exports = {
|
||||
parserOptions: {
|
||||
sourceType: 'CommonJS',
|
||||
},
|
||||
plugins: ['@typescript-eslint/eslint-plugin'],
|
||||
plugins: [
|
||||
'@typescript-eslint',
|
||||
'simple-import-sort',
|
||||
'import'
|
||||
],
|
||||
extends: [
|
||||
'eslint:recommended',
|
||||
'plugin:@typescript-eslint/recommended',
|
||||
'plugin:prettier/recommended',
|
||||
'plugin:prettier/recommended'
|
||||
],
|
||||
globals: {
|
||||
@@ -26,7 +30,11 @@ module.exports = {
|
||||
'@typescript-eslint/no-explicit-any': 'off',
|
||||
'@typescript-eslint/no-empty-function': 'off',
|
||||
'@typescript-eslint/no-non-null-assertion': 'off',
|
||||
'@typescript-eslint/no-unused-vars': 'off',
|
||||
'@typescript-eslint/no-unused-vars': 'error',
|
||||
'import/first': 'error',
|
||||
'import/no-duplicates': 'error',
|
||||
'simple-import-sort/imports': 'error',
|
||||
'simple-import-sort/exports': 'error',
|
||||
'@typescript-eslint/ban-types': [
|
||||
'error',
|
||||
{
|
||||
|
||||
38
.github/ISSUE_TEMPLATE/-en--bug-report.md
vendored
Normal file
38
.github/ISSUE_TEMPLATE/-en--bug-report.md
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
---
|
||||
name: "[EN] Bug report"
|
||||
about: Create a report to help us improve
|
||||
title: "[EN][BUG]"
|
||||
labels: bug
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
### Title: [Brief Description of the Bug]
|
||||
|
||||
#### Description:
|
||||
Describe in detail the problem you encountered. Include any relevant context that may help understand the origin of the bug.
|
||||
|
||||
#### Steps to Reproduce:
|
||||
1. List the steps necessary to reproduce the problem.
|
||||
2. Try to be as specific as possible.
|
||||
3. If the problem occurs in a specific scenario, describe it here.
|
||||
|
||||
#### Expected Behavior:
|
||||
Describe what you expected to happen when following the steps above.
|
||||
|
||||
#### Current Behavior:
|
||||
Explain what actually happens when you follow the steps above.
|
||||
|
||||
#### Screenshots/Videos:
|
||||
If possible, add screenshots or videos illustrating the problem. This can be extremely helpful in understanding the issue.
|
||||
|
||||
#### Environment:
|
||||
- **Server:** [e.g., Ubuntu 18.04]
|
||||
- **API Version:** [e.g., 1.5.4]
|
||||
- **Other Hardware/Software Specifications:** [e.g., CPU, GPU]
|
||||
|
||||
#### Submitting Logs:
|
||||
Please attach logs that may be related to the problem. If the logs contain sensitive information, consider sending them privately to one of the project maintainers.
|
||||
|
||||
#### Additional Notes:
|
||||
Include here any other information that you think might be useful in understanding or resolving the bug.
|
||||
28
.github/ISSUE_TEMPLATE/-en--feature-request.md
vendored
Normal file
28
.github/ISSUE_TEMPLATE/-en--feature-request.md
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
---
|
||||
name: "[EN] Feature request"
|
||||
about: Suggest an idea for the API
|
||||
title: "[EN][FEAT]"
|
||||
labels: enhancement
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
### Title: [Brief Description of Feature Request]
|
||||
|
||||
#### Detailed Description:
|
||||
Clearly and in detail, describe the functionality you wish to be implemented. Explain how this fits into the context of the project.
|
||||
|
||||
#### Rationale:
|
||||
Explain why this functionality would be useful for the project. This helps in understanding the importance and priority of the request.
|
||||
|
||||
#### Usage Examples:
|
||||
Provide specific examples of how this feature could be used. This can include scenarios or use cases where the feature would be particularly beneficial.
|
||||
|
||||
#### Possible Implementations:
|
||||
If you have ideas on how this feature might be implemented, please share them here. This is not mandatory but can be helpful for the development team.
|
||||
|
||||
#### Impact on the Project:
|
||||
Discuss how this new feature could impact other parts of the project, if applicable.
|
||||
|
||||
#### Additional Notes:
|
||||
Any other information you believe is relevant to your request.
|
||||
38
.github/ISSUE_TEMPLATE/-pt--reportar-bug.md
vendored
Normal file
38
.github/ISSUE_TEMPLATE/-pt--reportar-bug.md
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
---
|
||||
name: "[PT] Reportar bug"
|
||||
about: Reportar um problema
|
||||
title: "[PT][BUG]"
|
||||
labels: bug
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
### Título: [Breve Descrição do Bug]
|
||||
|
||||
#### Descrição:
|
||||
Descreva detalhadamente o problema que você encontrou. Inclua qualquer contexto relevante que possa ajudar a entender a origem do bug.
|
||||
|
||||
#### Passos para Reproduzir:
|
||||
1. Liste os passos necessários para reproduzir o problema.
|
||||
2. Tente ser o mais específico possível.
|
||||
3. Se o problema ocorrer em um cenário específico, descreva-o aqui.
|
||||
|
||||
#### Comportamento Esperado:
|
||||
Descreva o que você esperava que acontecesse quando seguisse os passos acima.
|
||||
|
||||
#### Comportamento Atual:
|
||||
Explique o que realmente acontece quando você segue os passos acima.
|
||||
|
||||
#### Capturas de Tela/Vídeos:
|
||||
Se possível, adicione capturas de tela ou vídeos que ilustrem o problema. Isso pode ser extremamente útil para entender o problema.
|
||||
|
||||
#### Ambiente:
|
||||
- **Servidor:** [ex: Ubuntu 18.04]
|
||||
- **Versão da API:** [ex: 1.5.4]
|
||||
- **Outras Especificações de Hardware/Software:** [ex: CPU, GPU]
|
||||
|
||||
#### Envio de Logs:
|
||||
Por favor, anexe os logs que possam estar relacionados ao problema. Se os logs contiverem informações sensíveis, considere enviá-los de forma privada para um dos mantenedores do projeto.
|
||||
|
||||
#### Notas Adicionais:
|
||||
Inclua aqui qualquer outra informação que você ache que possa ser útil para entender ou resolver o bug.
|
||||
28
.github/ISSUE_TEMPLATE/-pt--solicitar-recurso.md
vendored
Normal file
28
.github/ISSUE_TEMPLATE/-pt--solicitar-recurso.md
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
---
|
||||
name: "[PT] Solicitar recurso"
|
||||
about: Sugira novos recursos para a API
|
||||
title: "[PT][FEAT]"
|
||||
labels: enhancement
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
### Título: [Breve Descrição da Solicitação de Recurso]
|
||||
|
||||
#### Descrição Detalhada:
|
||||
Descreva claramente e em detalhes a funcionalidade que você deseja que seja implementada. Explique como isso se encaixa no contexto do projeto.
|
||||
|
||||
#### Racional:
|
||||
Explique por que essa funcionalidade seria útil para o projeto. Isso ajuda a entender a importância e a prioridade da solicitação.
|
||||
|
||||
#### Exemplos de Uso:
|
||||
Forneça exemplos específicos de como essa funcionalidade poderia ser utilizada. Isso pode incluir cenários ou casos de uso onde a funcionalidade seria particularmente benéfica.
|
||||
|
||||
#### Possíveis Implementações:
|
||||
Se você tem ideias sobre como essa funcionalidade pode ser implementada, por favor, compartilhe-as aqui. Isso não é obrigatório, mas pode ser útil para a equipe de desenvolvimento.
|
||||
|
||||
#### Impacto no Projeto:
|
||||
Discuta como essa nova funcionalidade poderia impactar outras partes do projeto, se aplicável.
|
||||
|
||||
#### Notas Adicionais:
|
||||
Qualquer outra informação que você acredita ser relevante para a sua solicitação.
|
||||
64
.github/workflows/publish_docker_image.yml
vendored
Normal file
64
.github/workflows/publish_docker_image.yml
vendored
Normal file
@@ -0,0 +1,64 @@
|
||||
name: Build Docker image
|
||||
|
||||
on:
|
||||
push:
|
||||
tags: ['v*']
|
||||
|
||||
jobs:
|
||||
build-amd:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out the repo
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Extract existing image metadata
|
||||
id: image-meta
|
||||
uses: docker/metadata-action@v4
|
||||
with:
|
||||
images: atendai/evolution-api
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
|
||||
- name: Log in to Docker Hub
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
|
||||
- name: Build and push AMD image
|
||||
uses: docker/build-push-action@v4
|
||||
with:
|
||||
context: .
|
||||
labels: ${{ steps.image-meta.outputs.labels }}
|
||||
platforms: linux/amd64
|
||||
push: true
|
||||
|
||||
build-arm:
|
||||
runs-on: buildjet-4vcpu-ubuntu-2204-arm
|
||||
steps:
|
||||
- name: Check out the repo
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Extract existing image metadata
|
||||
id: image-meta
|
||||
uses: docker/metadata-action@v4
|
||||
with:
|
||||
images: atendai/evolution-api
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
|
||||
- name: Log in to Docker Hub
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
|
||||
- name: Build and push ARM image
|
||||
uses: docker/build-push-action@v4
|
||||
with:
|
||||
context: .
|
||||
labels: ${{ steps.image-meta.outputs.labels }}
|
||||
platforms: linux/arm64
|
||||
push: true
|
||||
12
.gitignore
vendored
12
.gitignore
vendored
@@ -4,6 +4,8 @@
|
||||
|
||||
/Docker/.env
|
||||
|
||||
.vscode
|
||||
|
||||
# Logs
|
||||
logs/**.json
|
||||
*.log
|
||||
@@ -16,16 +18,19 @@ lerna-debug.log*
|
||||
/docker-compose-data
|
||||
/docker-data
|
||||
|
||||
docker-compose.yaml
|
||||
|
||||
# Package
|
||||
/yarn.lock
|
||||
/package-lock.json
|
||||
|
||||
# IDE - VSCode
|
||||
# IDEs
|
||||
.vscode/*
|
||||
!.vscode/settings.json
|
||||
!.vscode/tasks.json
|
||||
!.vscode/launch.json
|
||||
!.vscode/extensions.json
|
||||
.nova/*
|
||||
|
||||
# Prisma
|
||||
/prisma/migrations
|
||||
@@ -36,5 +41,10 @@ lerna-debug.log*
|
||||
/test/
|
||||
/src/env.yml
|
||||
/store
|
||||
*.env
|
||||
|
||||
/temp/*
|
||||
|
||||
.DS_Store
|
||||
*.DS_Store
|
||||
.tool-versions
|
||||
|
||||
@@ -2,8 +2,11 @@ module.exports = {
|
||||
semi: true,
|
||||
trailingComma: 'all',
|
||||
singleQuote: true,
|
||||
printWidth: 90,
|
||||
printWidth: 120,
|
||||
arrowParens: 'always',
|
||||
tabWidth: 2,
|
||||
bracketSameLine: true,
|
||||
bracketSpacing: true
|
||||
useTabs: false,
|
||||
bracketSameLine: false,
|
||||
bracketSpacing: true,
|
||||
parser: 'typescript'
|
||||
}
|
||||
11
.vscode/settings.json
vendored
11
.vscode/settings.json
vendored
@@ -5,7 +5,12 @@
|
||||
"editor.smoothScrolling": true,
|
||||
"editor.tabSize": 2,
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll.eslint": true,
|
||||
"source.fixAll": true
|
||||
}
|
||||
"source.fixAll.eslint": "explicit",
|
||||
"source.fixAll": "explicit"
|
||||
},
|
||||
"prisma-smart-formatter.typescript.defaultFormatter": "esbenp.prettier-vscode",
|
||||
"prisma-smart-formatter.prisma.defaultFormatter": "Prisma.prisma",
|
||||
"i18n-ally.localesPaths": [
|
||||
"store/messages"
|
||||
]
|
||||
}
|
||||
355
CHANGELOG.md
355
CHANGELOG.md
@@ -1,4 +1,355 @@
|
||||
# 1.2.1 (homolog)
|
||||
# 1.7.1 (2024-04-03 10:19)
|
||||
|
||||
### Fixed
|
||||
|
||||
* Correction when sending files with captions on Whatsapp Business
|
||||
* Correction in receiving messages with response on WhatsApp Business
|
||||
* Correction when sending a reaction to a message on WhatsApp Business
|
||||
* Correction of receiving reactions on WhatsApp business
|
||||
* Removed mandatory description of rows from sendList
|
||||
* Feature to collect message type in typebot
|
||||
|
||||
# 1.7.0 (2024-03-11 18:23)
|
||||
|
||||
### Feature
|
||||
|
||||
* Added update message endpoint
|
||||
* Add translate capabilities to QRMessages in CW
|
||||
* Join in Group by Invite Code
|
||||
* Read messages from whatsapp in chatwoot
|
||||
* Add support to use use redis in cacheservice
|
||||
* Add support for labels
|
||||
* Command to clearcache from chatwoot inbox
|
||||
* Whatsapp Cloud API Oficial
|
||||
|
||||
### Fixed
|
||||
|
||||
* Proxy configuration improvements
|
||||
* Correction in sending lists
|
||||
* Adjust in webhook_base64
|
||||
* Correction in typebot text formatting
|
||||
* Correction in chatwoot text formatting and render list message
|
||||
* Only use a axios request to get file mimetype if necessary
|
||||
* When possible use the original file extension
|
||||
* When receiving a file from whatsapp, use the original filename in chatwoot if possible
|
||||
* Remove message ids cache in chatwoot to use chatwoot's api itself
|
||||
* Adjusts the quoted message, now has contextInfo in the message Raw
|
||||
* Collecting responses with text or numbers in Typebot
|
||||
* Added sendList endpoint to swagger documentation
|
||||
* Implemented a function to synchronize message deletions on WhatsApp, automatically reflecting in Chatwoot.
|
||||
* Improvement on numbers validation
|
||||
* Fix polls in message sending
|
||||
* Sending status message
|
||||
* Message 'connection successfully' spamming
|
||||
* Invalidate the conversation cache if reopen_conversation is false and the conversation was resolved
|
||||
* Fix looping when deleting a message in chatwoot
|
||||
* When receiving a file from whatsapp, use the original filename in chatwoot if possible
|
||||
* Correction in the sendList Function
|
||||
* Implement contact upsert in messaging-history.set
|
||||
* Improve proxy error handling
|
||||
* Refactor fetching participants for group in WhatsApp service
|
||||
* Fixed problem where the typebot final keyword did not work
|
||||
* Typebot's wait now pauses the flow and composing is defined by the delay_message parameter in set typebot
|
||||
* Composing over 20s now loops until finished
|
||||
|
||||
# 1.6.1 (2023-12-22 11:43)
|
||||
|
||||
### Fixed
|
||||
|
||||
* Fixed Lid Messages
|
||||
* Fixed sending variables to typebot
|
||||
* Fixed sending variables from typebot
|
||||
* Correction sending s3/minio media to chatwoot and typebot
|
||||
* Fixed the problem with typebot closing at the end of the flow, now this is optional with the TYPEBOT_KEEP_OPEN variable
|
||||
* Fixed chatwoot Bold, Italic and Underline formatting using Regex
|
||||
* Added the sign_delimiter property to the Chatwoot configuration, allowing you to set a different delimiter for the signature. Default when not defined \n
|
||||
* Include instance Id field in the instance configuration
|
||||
* Fixed the pairing code
|
||||
* Adjusts in typebot
|
||||
* Fix the problem when disconnecting the instance and connecting again using mongodb
|
||||
* Options to disable docs and manager
|
||||
* When deleting a message in whatsapp, delete the message in chatwoot too
|
||||
|
||||
|
||||
# 1.6.0 (2023-12-12 17:24)
|
||||
|
||||
### Feature
|
||||
|
||||
* Added AWS SQS Integration
|
||||
* Added support for new typebot API
|
||||
* Added endpoint sendPresence
|
||||
* New Instance Manager
|
||||
* Added auto_create to the chatwoot set to create the inbox automatically or not
|
||||
* Added reply, delete and message reaction in chatwoot v3.3.1
|
||||
|
||||
### Fixed
|
||||
|
||||
* Adjusts in proxy
|
||||
* Adjusts in start session for Typebot
|
||||
* Added mimetype field when sending media
|
||||
* Ajusts in validations to messages.upsert
|
||||
* Fixed messages not received: error handling when updating contact in chatwoot
|
||||
* Fix workaround to manage param data as an array in mongodb
|
||||
* Removed await from webhook when sending a message
|
||||
* Update typebot.service.ts - element.underline change ~ for *
|
||||
* Removed api restart on receiving an error
|
||||
* Fixes in mongodb and chatwoot
|
||||
* Adjusted return from queries in mongodb
|
||||
* Added restart instance when update profile picture
|
||||
* Correction of chatwoot functioning with admin flows
|
||||
* Fixed problem that did not generate qrcode with the chatwoot_conversation_pending option enabled
|
||||
* Fixed issue where CSAT opened a new ticket when reopen_conversation was disabled
|
||||
* Fixed issue sending contact to Chatwoot via iOS
|
||||
|
||||
### Integrations
|
||||
|
||||
* Chatwoot: v3.3.1
|
||||
* Typebot: v2.20.0
|
||||
|
||||
# 1.5.4 (2023-10-09 20:43)
|
||||
|
||||
### Fixed
|
||||
|
||||
* Baileys logger typing issue resolved
|
||||
* Solved problem with duplicate messages in chatwoot
|
||||
|
||||
# 1.5.3 (2023-10-06 18:55)
|
||||
|
||||
### Feature
|
||||
|
||||
* Swagger documentation
|
||||
* Added base 64 sending option via webhook
|
||||
|
||||
### Fixed
|
||||
|
||||
* Remove rabbitmq queues when delete instances
|
||||
* Improvement in restart instance to completely redo the connection
|
||||
* Update node version: v20
|
||||
* Correction of messages sent by the api and typebot not appearing in chatwoot
|
||||
* Adjustment to start typebot, added startSession parameter
|
||||
* Chatwoot now receives messages sent via api and typebot
|
||||
* Fixed problem with starting with an input in typebot
|
||||
* Added check to ensure variables are not empty before executing foreach in start typebot
|
||||
|
||||
# 1.5.2 (2023-09-28 17:56)
|
||||
|
||||
### Fixed
|
||||
|
||||
* Fix chatwootSchema in chatwoot model to store reopen_conversation and conversation_pending options
|
||||
* Problem resolved when sending files from minio to typebot
|
||||
* Improvement in the "startTypebot" method to create persistent session when triggered
|
||||
* New manager for Evo 1.5.2 - Set Typebot update
|
||||
* Resolved problems when reading/querying instances
|
||||
|
||||
# 1.5.1 (2023-09-17 13:50)
|
||||
|
||||
### Feature
|
||||
|
||||
* Added listening_from_me option in Set Typebot
|
||||
* Added variables options in Start Typebot
|
||||
* Added webhooks for typebot events
|
||||
* Added ChamaAI integration
|
||||
* Added webhook to send errors
|
||||
* Added support for messaging with ads on chatwoot
|
||||
|
||||
### Fixed
|
||||
|
||||
* Fix looping connection messages in chatwoot
|
||||
* Improved performance of fetch instances
|
||||
|
||||
# 1.5.0 (2023-08-18 12:47)
|
||||
|
||||
### Feature
|
||||
|
||||
* New instance manager in /manager route
|
||||
* Added extra files for chatwoot and appsmith
|
||||
* Added Get Last Message and Archive for Chat
|
||||
* Added env var QRCODE_COLOR
|
||||
* Added websocket to send events
|
||||
* Added rabbitmq to send events
|
||||
* Added Typebot integration
|
||||
* Added proxy endpoint
|
||||
* Added send and date_time in webhook data
|
||||
|
||||
### Fixed
|
||||
|
||||
* Solved problem when disconnecting from the instance the instance was deleted
|
||||
* Encoded spaces in chatwoot webhook
|
||||
* Adjustment in the saving of contacts, saving the information of the number and Jid
|
||||
* Update Dockerfile
|
||||
* If you pass empty events in create instance and set webhook it is understood as all
|
||||
* Fixed issue that did not output base64 averages
|
||||
* Messages sent by the api now arrive in chatwoot
|
||||
|
||||
### Integrations
|
||||
|
||||
* Chatwoot: v2.18.0 - v3.0.0
|
||||
* Typebot: v2.16.0
|
||||
* Manager Evolution API
|
||||
|
||||
# 1.4.8 (2023-07-27 10:27)
|
||||
|
||||
### Fixed
|
||||
|
||||
* Fixed error return bug
|
||||
|
||||
# 1.4.7 (2023-07-27 08:47)
|
||||
|
||||
### Fixed
|
||||
|
||||
* Fixed error return bug
|
||||
* Fixed problem of getting message when deleting message in chatwoot
|
||||
* Change in error return pattern
|
||||
|
||||
# 1.4.6 (2023-07-26 17:54)
|
||||
|
||||
### Fixed
|
||||
|
||||
* Fixed bug of creating new inbox by chatwoot
|
||||
* When conversation reopens is pending when conversation pending is true
|
||||
* Added docker-compose file with dockerhub image
|
||||
|
||||
# 1.4.5 (2023-07-26 09:32)
|
||||
|
||||
### Fixed
|
||||
|
||||
* Fixed problems in localization template in chatwoot
|
||||
* Fix mids going duplicated in chatwoot
|
||||
|
||||
# 1.4.4 (2023-07-25 15:24)
|
||||
|
||||
### Fixed
|
||||
|
||||
* Fixed chatwoot line wrap issue
|
||||
* Solved receive location in chatwoot
|
||||
* When requesting the pairing code, it also brings the qr code
|
||||
* Option reopen_conversation in chatwoot endpoint
|
||||
* Option conversation_pending in chatwoot endpoint
|
||||
|
||||
# 1.4.3 (2023-07-25 10:51)
|
||||
|
||||
### Fixed
|
||||
|
||||
* Adjusts in settings with options always_online, read_messages and read_status
|
||||
* Fixed send webhook for event CALL
|
||||
* Create instance with settings
|
||||
|
||||
# 1.4.2 (2023-07-24 20:52)
|
||||
|
||||
### Fixed
|
||||
|
||||
* Fixed validation is set settings
|
||||
* Adjusts in group validations
|
||||
* Ajusts in sticker message to chatwoot
|
||||
|
||||
# 1.4.1 (2023-07-24 18:28)
|
||||
|
||||
### Fixed
|
||||
|
||||
* Fixed reconnect with pairing code or qrcode
|
||||
* Fixed problem in createJid
|
||||
|
||||
# 1.4.0 (2023-07-24 17:03)
|
||||
|
||||
### Features
|
||||
|
||||
* Added connection functionality via pairing code
|
||||
* Added fetch profile endpoint in chat controller
|
||||
* Created settings controller
|
||||
* Added reject call and send text message when receiving a call
|
||||
* Added setting to ignore group messages
|
||||
* Added connection with pairing code in chatwoot with command /init:{NUMBER}
|
||||
* Added encoding option in endpoint sendWhatsAppAudio
|
||||
|
||||
### Fixed
|
||||
|
||||
* Added link preview option in send text message
|
||||
* Fixed problem with fileSha256 appearing when sending a sticker in chatwoot
|
||||
* Fixed issue where it was not possible to open a conversation when sent at first by me on my cell phone in chatwoot
|
||||
* Now it only updates the contact name if it is the same as the phone number in chatwoot
|
||||
* Now accepts all chatwoot inbox templates
|
||||
* Command to create new instances set to /new_instance:{NAME}:{NUMBER}
|
||||
* Fix in chatwoot set, sign msg can now be disabled
|
||||
|
||||
### Integrations
|
||||
|
||||
* Chatwoot: v2.18.0 - v3.0.0 (Beta)
|
||||
|
||||
# 1.3.2 (2023-07-21 17:19)
|
||||
|
||||
### Fixed
|
||||
|
||||
* Fix in update settings that needed to restart after updated
|
||||
* Correction in the use of the api with mongodb
|
||||
* Adjustments to search endpoint for contacts, chats, messages and Status messages
|
||||
* Now when deleting the instance, the data referring to it in mongodb is also deleted
|
||||
* It is now validated if the instance name contains uppercase and special characters
|
||||
* For compatibility reasons, container mode has been removed
|
||||
* Added docker-compose files example
|
||||
|
||||
### Integrations
|
||||
|
||||
* Chatwoot: v2.18.0
|
||||
|
||||
# 1.3.1 (2023-07-20 07:48)
|
||||
|
||||
### Fixed
|
||||
|
||||
* Adjust in create store files
|
||||
|
||||
### Integrations
|
||||
|
||||
* Chatwoot: v2.18.0
|
||||
|
||||
# 1.3.0 (2023-07-19 11:33)
|
||||
|
||||
### Features
|
||||
|
||||
* Added messages.delete event
|
||||
* Added restart instance endpoint
|
||||
* Created automation for creating instances in the chatwoot bot with the command '#inbox_whatsapp:{INSTANCE_NAME}
|
||||
* Change Baileys version to: 6.4.0
|
||||
* Send contact in chatwoot
|
||||
* Send contact array in chatwoot
|
||||
* Added apiKey in webhook and serverUrl in fetchInstance if EXPOSE_IN_FETCH_INSTANCES: true
|
||||
* Translation set to default (english) in chatwoot
|
||||
|
||||
### Fixed
|
||||
|
||||
* Fixed error to send message in large groups
|
||||
* Docker files adjusted
|
||||
* Fixed in the postman collection the webhookByEvent parameter by webhook_by_events
|
||||
* Added validations in create instance
|
||||
* Removed link preview endpoint, now it's done automatically from sending conventional text
|
||||
* Added group membership validation before sending message to groups
|
||||
* Adjusts in docker files
|
||||
* Adjusts in returns in endpoints chatwoot and webhook
|
||||
* Fixed ghost mentions in send text message
|
||||
* Fixed bug that saved contacts from groups came without number in chatwoot
|
||||
* Fixed problem to receive csat in chatwoot
|
||||
* Fixed require fileName for document only in base64 for send media message
|
||||
* Bug fix when sending mobile message change contact name to number in chatwoot
|
||||
* Bug fix when connecting whatsapp does not send confirmation message
|
||||
* Fixed quoted message with id or message directly
|
||||
* Adjust in validation for mexican and argentine numbers
|
||||
* Adjust in create store files
|
||||
|
||||
### Integrations
|
||||
|
||||
* Chatwoot: v2.18.0
|
||||
|
||||
# 1.2.2 (2023-07-15 09:36)
|
||||
|
||||
### Fixed
|
||||
|
||||
* Tweak in route "/" with version info
|
||||
* Adjusts chatwoot version
|
||||
|
||||
### Integrations
|
||||
|
||||
* Chatwoot: v2.18.0
|
||||
|
||||
# 1.2.1 (2023-07-14 19:04)
|
||||
|
||||
### Fixed
|
||||
|
||||
@@ -152,4 +503,4 @@
|
||||
* Sending the local webhook url as destination in the webhook data for webhook redirection
|
||||
* Startup modes, server or container
|
||||
* Server Mode works normally as everyone is used to
|
||||
* Container mode made to use one instance per container, when starting the application an instance is already created and the qrcode is generated and it starts sending webhook without having to call it manually, it only allows one instance at a time.
|
||||
* Container mode made to use one instance per container, when starting the application an instance is already created and the qrcode is generated and it starts sending webhook without having to call it manually, it only allows one instance at a time.
|
||||
|
||||
@@ -1,18 +1,22 @@
|
||||
SERVER_URL='<url>' # ex.: http://localhost:3333
|
||||
# Server URL - Set your application url
|
||||
SERVER_URL=http://localhost:8080
|
||||
|
||||
CORS_ORIGIN='*' # Or separate by commas - ex.: 'yourdomain1.com, yourdomain2.com'
|
||||
CORS_METHODS='POST,GET,PUT,DELETE'
|
||||
# Cors - * for all or set separate by commas - ex.: 'yourdomain1.com, yourdomain2.com'
|
||||
CORS_ORIGIN=*
|
||||
CORS_METHODS=POST,GET,PUT,DELETE
|
||||
CORS_CREDENTIALS=true
|
||||
|
||||
# Determine the logs to be displayed
|
||||
LOG_LEVEL='ERROR,WARN,DEBUG,INFO,LOG,VERBOSE,DARK,WEBHOOKS'
|
||||
LOG_LEVEL=ERROR,WARN,DEBUG,INFO,LOG,VERBOSE,DARK,WEBHOOKS
|
||||
LOG_COLOR=true
|
||||
LOG_BAILEYS=error # "fatal" | "error" | "warn" | "info" | "debug" | "trace"
|
||||
# Log Baileys - "fatal" | "error" | "warn" | "info" | "debug" | "trace"
|
||||
LOG_BAILEYS=error
|
||||
|
||||
# Determine how long the instance should be deleted from memory in case of no connection.
|
||||
# Default time: 5 minutes
|
||||
# If you don't even want an expiration, enter the value false
|
||||
DEL_INSTANCE=false
|
||||
DEL_TEMP_INSTANCES=true # Delete instances with status closed on start
|
||||
|
||||
# Temporary data storage
|
||||
STORE_MESSAGES=true
|
||||
@@ -20,7 +24,8 @@ STORE_MESSAGE_UP=true
|
||||
STORE_CONTACTS=true
|
||||
STORE_CHATS=true
|
||||
|
||||
CLEAN_STORE_CLEANING_INTERVAL=7200 # seconds === 2h
|
||||
# Set Store Interval in Seconds (7200 = 2h)
|
||||
CLEAN_STORE_CLEANING_INTERVAL=7200
|
||||
CLEAN_STORE_MESSAGES=true
|
||||
CLEAN_STORE_MESSAGE_UP=true
|
||||
CLEAN_STORE_CONTACTS=true
|
||||
@@ -29,7 +34,7 @@ CLEAN_STORE_CHATS=true
|
||||
# Permanent data storage
|
||||
DATABASE_ENABLED=false
|
||||
DATABASE_CONNECTION_URI=mongodb://root:root@mongodb:27017/?authSource=admin&readPreference=primary&ssl=false&directConnection=true
|
||||
DATABASE_CONNECTION_DB_PREFIX_NAME=evolution
|
||||
DATABASE_CONNECTION_DB_PREFIX_NAME=evdocker
|
||||
|
||||
# Choose the data you want to save in the application's database or store
|
||||
DATABASE_SAVE_DATA_INSTANCE=false
|
||||
@@ -40,12 +45,30 @@ DATABASE_SAVE_DATA_CHATS=false
|
||||
|
||||
REDIS_ENABLED=false
|
||||
REDIS_URI=redis://redis:6379
|
||||
REDIS_PREFIX_KEY=evolution
|
||||
REDIS_PREFIX_KEY=evdocker
|
||||
|
||||
RABBITMQ_ENABLED=false
|
||||
RABBITMQ_RABBITMQ_MODE=global
|
||||
RABBITMQ_EXCHANGE_NAME=evolution_exchange
|
||||
RABBITMQ_URI=amqp://guest:guest@rabbitmq:5672
|
||||
|
||||
WEBSOCKET_ENABLED=false
|
||||
|
||||
WA_BUSINESS_TOKEN_WEBHOOK=evolution
|
||||
WA_BUSINESS_URL=https://graph.facebook.com
|
||||
WA_BUSINESS_VERSION=v18.0
|
||||
WA_BUSINESS_LANGUAGE=pt_BR
|
||||
|
||||
SQS_ENABLED=false
|
||||
SQS_ACCESS_KEY_ID=
|
||||
SQS_SECRET_ACCESS_KEY=
|
||||
SQS_ACCOUNT_ID=
|
||||
SQS_REGION=
|
||||
|
||||
# Global Webhook Settings
|
||||
# Each instance's Webhook URL and events will be requested at the time it is created
|
||||
## Define a global webhook that will listen for enabled events from all instances
|
||||
WEBHOOK_GLOBAL_URL='<url>'
|
||||
WEBHOOK_GLOBAL_URL=''
|
||||
WEBHOOK_GLOBAL_ENABLED=false
|
||||
# With this option activated, you work with a url per webhook event, respecting the global url and the name of each event
|
||||
WEBHOOK_GLOBAL_WEBHOOK_BY_EVENTS=false
|
||||
@@ -55,6 +78,7 @@ WEBHOOK_EVENTS_QRCODE_UPDATED=true
|
||||
WEBHOOK_EVENTS_MESSAGES_SET=true
|
||||
WEBHOOK_EVENTS_MESSAGES_UPSERT=true
|
||||
WEBHOOK_EVENTS_MESSAGES_UPDATE=true
|
||||
WEBHOOK_EVENTS_MESSAGES_DELETE=true
|
||||
WEBHOOK_EVENTS_SEND_MESSAGE=true
|
||||
WEBHOOK_EVENTS_CONTACTS_SET=true
|
||||
WEBHOOK_EVENTS_CONTACTS_UPSERT=true
|
||||
@@ -68,33 +92,54 @@ WEBHOOK_EVENTS_GROUPS_UPSERT=true
|
||||
WEBHOOK_EVENTS_GROUPS_UPDATE=true
|
||||
WEBHOOK_EVENTS_GROUP_PARTICIPANTS_UPDATE=true
|
||||
WEBHOOK_EVENTS_CONNECTION_UPDATE=true
|
||||
WEBHOOK_EVENTS_LABELS_EDIT=true
|
||||
WEBHOOK_EVENTS_LABELS_ASSOCIATION=true
|
||||
WEBHOOK_EVENTS_CALL=true
|
||||
# This event fires every time a new token is requested via the refresh route
|
||||
WEBHOOK_EVENTS_NEW_JWT_TOKEN=false
|
||||
# This events is used with Typebot
|
||||
WEBHOOK_EVENTS_TYPEBOT_START=false
|
||||
WEBHOOK_EVENTS_TYPEBOT_CHANGE_STATUS=false
|
||||
# This event is used with Chama AI
|
||||
WEBHOOK_EVENTS_CHAMA_AI_ACTION=false
|
||||
# This event is used to send errors
|
||||
WEBHOOK_EVENTS_ERRORS=false
|
||||
WEBHOOK_EVENTS_ERRORS_WEBHOOK=
|
||||
|
||||
# Name that will be displayed on smartphone connection
|
||||
CONFIG_SESSION_PHONE_CLIENT='Evolution API'
|
||||
CONFIG_SESSION_PHONE_NAME=chrome # chrome | firefox | edge | opera | safari
|
||||
CONFIG_SESSION_PHONE_CLIENT=EvolutionAPI
|
||||
# Browser Name = Chrome | Firefox | Edge | Opera | Safari
|
||||
CONFIG_SESSION_PHONE_NAME=Chrome
|
||||
|
||||
# Set qrcode display limit
|
||||
QRCODE_LIMIT=30
|
||||
QRCODE_COLOR=#198754
|
||||
|
||||
# old | latest
|
||||
TYPEBOT_API_VERSION=latest
|
||||
TYPEBOT_KEEP_OPEN=false
|
||||
|
||||
#Chatwoot
|
||||
# If you leave this option as false, when deleting the message for everyone on WhatsApp, it will not be deleted on Chatwoot.
|
||||
CHATWOOT_MESSAGE_DELETE=false # false | true
|
||||
# If you leave this option as true, when sending a message in Chatwoot, the client's last message will be marked as read on WhatsApp.
|
||||
CHATWOOT_MESSAGE_READ=false # false | true
|
||||
# This db connection is used to import messages from whatsapp to chatwoot database
|
||||
CHATWOOT_IMPORT_DATABASE_CONNECTION_URI=postgres://user:password@hostname:port/dbname
|
||||
CHATWOOT_IMPORT_DATABASE_PLACEHOLDER_MEDIA_MESSAGE=true
|
||||
|
||||
# Defines an authentication type for the api
|
||||
# We recommend using the apikey because it will allow you to use a custom token,
|
||||
# if you use jwt, a random token will be generated and may be expired and you will have to generate a new token
|
||||
AUTHENTICATION_TYPE='apikey' # jwt or 'apikey'
|
||||
# jwt or 'apikey'
|
||||
AUTHENTICATION_TYPE=apikey
|
||||
## Define a global apikey to access all instances.
|
||||
### OBS: This key must be inserted in the request header to create an instance.
|
||||
AUTHENTICATION_API_KEY='B6D711FCDE4D4FD5936544120E713976'
|
||||
AUTHENTICATION_API_KEY=B6D711FCDE4D4FD5936544120E713976
|
||||
AUTHENTICATION_EXPOSE_IN_FETCH_INSTANCES=true
|
||||
## Set the secret key to encrypt and decrypt your token and its expiration time
|
||||
AUTHENTICATION_JWT_EXPIRIN_IN=0 # seconds - 3600s ===1h | zero (0) - never expires
|
||||
AUTHENTICATION_JWT_SECRET='L0YWtjb2w554WFqPG'
|
||||
# Set the instance name and webhook url to create an instance in init the application
|
||||
# With this option activated, you work with a url per webhook event, respecting the local url and the name of each event
|
||||
AUTHENTICATION_INSTANCE_MODE=server # container or server
|
||||
# if you are using container mode, set the container name and the webhook url to default instance
|
||||
AUTHENTICATION_INSTANCE_NAME=evolution
|
||||
AUTHENTICATION_INSTANCE_WEBHOOK_URL='<url>'
|
||||
AUTHENTICATION_INSTANCE_CHATWOOT_ACCOUNT_ID=1
|
||||
AUTHENTICATION_INSTANCE_CHATWOOT_TOKEN=123456
|
||||
AUTHENTICATION_INSTANCE_CHATWOOT_URL='<url>'
|
||||
# seconds - 3600s ===1h | zero (0) - never expires
|
||||
AUTHENTICATION_JWT_EXPIRIN_IN=0
|
||||
AUTHENTICATION_JWT_SECRET='L=0YWt]b2w[WF>#>:&E`'
|
||||
|
||||
LANGUAGE=en # pt-BR, en
|
||||
22
Docker/docker-compose.yaml
Normal file
22
Docker/docker-compose.yaml
Normal file
@@ -0,0 +1,22 @@
|
||||
version: '3.3'
|
||||
|
||||
services:
|
||||
|
||||
api:
|
||||
container_name: evolution_api
|
||||
image: atendai/evolution-api
|
||||
restart: always
|
||||
ports:
|
||||
- 8080:8080
|
||||
volumes:
|
||||
- evolution_instances:/evolution/instances
|
||||
- evolution_store:/evolution/store
|
||||
env_file:
|
||||
- .env
|
||||
command: ['node', './dist/src/main.js']
|
||||
expose:
|
||||
- 8080
|
||||
|
||||
volumes:
|
||||
evolution_instances:
|
||||
evolution_store:
|
||||
112
Docker/evolution-api-all-services/.env.example
Normal file
112
Docker/evolution-api-all-services/.env.example
Normal file
@@ -0,0 +1,112 @@
|
||||
# Server URL - Set your application url
|
||||
SERVER_URL='http://localhost:8080'
|
||||
|
||||
# Cors - * for all or set separate by commas - ex.: 'yourdomain1.com, yourdomain2.com'
|
||||
CORS_ORIGIN='*'
|
||||
CORS_METHODS='POST,GET,PUT,DELETE'
|
||||
CORS_CREDENTIALS=true
|
||||
|
||||
# Determine the logs to be displayed
|
||||
LOG_LEVEL='ERROR,WARN,DEBUG,INFO,LOG,VERBOSE,DARK,WEBHOOKS'
|
||||
LOG_COLOR=true
|
||||
# Log Baileys - "fatal" | "error" | "warn" | "info" | "debug" | "trace"
|
||||
LOG_BAILEYS=error
|
||||
|
||||
# Determine how long the instance should be deleted from memory in case of no connection.
|
||||
# Default time: 5 minutes
|
||||
# If you don't even want an expiration, enter the value false
|
||||
DEL_INSTANCE=false
|
||||
DEL_TEMP_INSTANCES=true # Delete instances with status closed on start
|
||||
|
||||
# Temporary data storage
|
||||
STORE_MESSAGES=true
|
||||
STORE_MESSAGE_UP=true
|
||||
STORE_CONTACTS=true
|
||||
STORE_CHATS=true
|
||||
|
||||
# Set Store Interval in Seconds (7200 = 2h)
|
||||
CLEAN_STORE_CLEANING_INTERVAL=7200
|
||||
CLEAN_STORE_MESSAGES=true
|
||||
CLEAN_STORE_MESSAGE_UP=true
|
||||
CLEAN_STORE_CONTACTS=true
|
||||
CLEAN_STORE_CHATS=true
|
||||
|
||||
# Permanent data storage
|
||||
DATABASE_ENABLED=true
|
||||
DATABASE_CONNECTION_URI=mongodb://root:root@mongodb:27017/?authSource=admin&readPreference=primary&ssl=false&directConnection=true
|
||||
DATABASE_CONNECTION_DB_PREFIX_NAME=evolution
|
||||
|
||||
# Choose the data you want to save in the application's database or store
|
||||
DATABASE_SAVE_DATA_INSTANCE=false
|
||||
DATABASE_SAVE_DATA_NEW_MESSAGE=false
|
||||
DATABASE_SAVE_MESSAGE_UPDATE=false
|
||||
DATABASE_SAVE_DATA_CONTACTS=false
|
||||
DATABASE_SAVE_DATA_CHATS=false
|
||||
|
||||
REDIS_ENABLED=true
|
||||
REDIS_URI=redis://redis:6379
|
||||
REDIS_PREFIX_KEY=evolution
|
||||
|
||||
# Global Webhook Settings
|
||||
# Each instance's Webhook URL and events will be requested at the time it is created
|
||||
## Define a global webhook that will listen for enabled events from all instances
|
||||
WEBHOOK_GLOBAL_URL=''
|
||||
WEBHOOK_GLOBAL_ENABLED=false
|
||||
# With this option activated, you work with a url per webhook event, respecting the global url and the name of each event
|
||||
WEBHOOK_GLOBAL_WEBHOOK_BY_EVENTS=false
|
||||
## Set the events you want to hear
|
||||
WEBHOOK_EVENTS_APPLICATION_STARTUP=false
|
||||
WEBHOOK_EVENTS_QRCODE_UPDATED=true
|
||||
WEBHOOK_EVENTS_MESSAGES_SET=true
|
||||
WEBHOOK_EVENTS_MESSAGES_UPSERT=true
|
||||
WEBHOOK_EVENTS_MESSAGES_UPDATE=true
|
||||
WEBHOOK_EVENTS_MESSAGES_DELETE=true
|
||||
WEBHOOK_EVENTS_SEND_MESSAGE=true
|
||||
WEBHOOK_EVENTS_CONTACTS_SET=true
|
||||
WEBHOOK_EVENTS_CONTACTS_UPSERT=true
|
||||
WEBHOOK_EVENTS_CONTACTS_UPDATE=true
|
||||
WEBHOOK_EVENTS_PRESENCE_UPDATE=true
|
||||
WEBHOOK_EVENTS_CHATS_SET=true
|
||||
WEBHOOK_EVENTS_CHATS_UPSERT=true
|
||||
WEBHOOK_EVENTS_CHATS_UPDATE=true
|
||||
WEBHOOK_EVENTS_CHATS_DELETE=true
|
||||
WEBHOOK_EVENTS_GROUPS_UPSERT=true
|
||||
WEBHOOK_EVENTS_GROUPS_UPDATE=true
|
||||
WEBHOOK_EVENTS_GROUP_PARTICIPANTS_UPDATE=true
|
||||
WEBHOOK_EVENTS_CONNECTION_UPDATE=true
|
||||
WEBHOOK_EVENTS_LABELS_EDIT=true
|
||||
WEBHOOK_EVENTS_LABELS_ASSOCIATION=true
|
||||
# This event fires every time a new token is requested via the refresh route
|
||||
WEBHOOK_EVENTS_NEW_JWT_TOKEN=false
|
||||
|
||||
# Name that will be displayed on smartphone connection
|
||||
CONFIG_SESSION_PHONE_CLIENT='Evolution API'
|
||||
# Browser Name = chrome | firefox | edge | opera | safari
|
||||
CONFIG_SESSION_PHONE_NAME=chrome
|
||||
|
||||
# Set qrcode display limit
|
||||
QRCODE_LIMIT=30
|
||||
|
||||
# Defines an authentication type for the api
|
||||
# We recommend using the apikey because it will allow you to use a custom token,
|
||||
# if you use jwt, a random token will be generated and may be expired and you will have to generate a new token
|
||||
# jwt or 'apikey'
|
||||
AUTHENTICATION_TYPE='apikey'
|
||||
## Define a global apikey to access all instances.
|
||||
### OBS: This key must be inserted in the request header to create an instance.
|
||||
AUTHENTICATION_API_KEY='B6D711FCDE4D4FD5936544120E713976'
|
||||
AUTHENTICATION_EXPOSE_IN_FETCH_INSTANCES=true
|
||||
## Set the secret key to encrypt and decrypt your token and its expiration time
|
||||
# seconds - 3600s ===1h | zero (0) - never expires
|
||||
AUTHENTICATION_JWT_EXPIRIN_IN=0
|
||||
AUTHENTICATION_JWT_SECRET='L0YWtjb2w554WFqPG'
|
||||
# Set the instance name and webhook url to create an instance in init the application
|
||||
# With this option activated, you work with a url per webhook event, respecting the local url and the name of each event
|
||||
# container or server
|
||||
AUTHENTICATION_INSTANCE_MODE=server
|
||||
# if you are using container mode, set the container name and the webhook url to default instance
|
||||
AUTHENTICATION_INSTANCE_NAME=evolution
|
||||
AUTHENTICATION_INSTANCE_WEBHOOK_URL=''
|
||||
AUTHENTICATION_INSTANCE_CHATWOOT_ACCOUNT_ID=1
|
||||
AUTHENTICATION_INSTANCE_CHATWOOT_TOKEN=123456
|
||||
AUTHENTICATION_INSTANCE_CHATWOOT_URL=''
|
||||
91
Docker/evolution-api-all-services/docker-compose.yaml
Normal file
91
Docker/evolution-api-all-services/docker-compose.yaml
Normal file
@@ -0,0 +1,91 @@
|
||||
version: '3.3'
|
||||
|
||||
services:
|
||||
|
||||
mongodb:
|
||||
container_name: mongodb
|
||||
image: mongo
|
||||
restart: on-failure
|
||||
ports:
|
||||
- 27017:27017
|
||||
environment:
|
||||
- MONGO_INITDB_ROOT_USERNAME=root
|
||||
- MONGO_INITDB_ROOT_PASSWORD=root
|
||||
- PUID=1000
|
||||
- PGID=1000
|
||||
volumes:
|
||||
- evolution_mongodb_data:/data/db
|
||||
- evolution_mongodb_configdb:/data/configdb
|
||||
expose:
|
||||
- 27017
|
||||
|
||||
mongo-express:
|
||||
container_name: mongodb-express
|
||||
image: mongo-express
|
||||
restart: on-failure
|
||||
ports:
|
||||
- 8081:8081
|
||||
depends_on:
|
||||
- mongodb
|
||||
environment:
|
||||
ME_CONFIG_BASICAUTH_USERNAME: root
|
||||
ME_CONFIG_BASICAUTH_PASSWORD: root
|
||||
ME_CONFIG_MONGODB_SERVER: mongodb
|
||||
ME_CONFIG_MONGODB_ADMINUSERNAME: root
|
||||
ME_CONFIG_MONGODB_ADMINPASSWORD: root
|
||||
links:
|
||||
- mongodb
|
||||
|
||||
redis:
|
||||
container_name: redis
|
||||
image: redis:latest
|
||||
restart: on-failure
|
||||
ports:
|
||||
- 6379:6379
|
||||
command: >
|
||||
redis-server
|
||||
--port 6379
|
||||
--appendonly yes
|
||||
volumes:
|
||||
- evolution_redis:/data
|
||||
|
||||
rebrow:
|
||||
container_name: rebrow
|
||||
image: marian/rebrow
|
||||
restart: on-failure
|
||||
depends_on:
|
||||
- redis
|
||||
ports:
|
||||
- 5001:5001
|
||||
links:
|
||||
- redis
|
||||
|
||||
api:
|
||||
container_name: evolution_api
|
||||
image: atendai/evolution-api
|
||||
restart: always
|
||||
depends_on:
|
||||
- mongodb
|
||||
- redis
|
||||
ports:
|
||||
- 8080:8080
|
||||
volumes:
|
||||
- evolution_instances:/evolution/instances
|
||||
- evolution_store:/evolution/store
|
||||
env_file:
|
||||
- .env
|
||||
command: ['node', './dist/src/main.js']
|
||||
expose:
|
||||
- 8080
|
||||
|
||||
volumes:
|
||||
evolution_mongodb_data:
|
||||
evolution_mongodb_configdb:
|
||||
evolution_redis:
|
||||
evolution_instances:
|
||||
evolution_store:
|
||||
|
||||
networks:
|
||||
evolution-net:
|
||||
external: true
|
||||
|
||||
@@ -15,8 +15,6 @@ services:
|
||||
volumes:
|
||||
- evolution_mongodb_data:/data/db
|
||||
- evolution_mongodb_configdb:/data/configdb
|
||||
networks:
|
||||
- evolution-net
|
||||
expose:
|
||||
- 27017
|
||||
|
||||
@@ -37,7 +35,8 @@ volumes:
|
||||
evolution_mongodb_data:
|
||||
evolution_mongodb_configdb:
|
||||
|
||||
|
||||
networks:
|
||||
default:
|
||||
evolution-net:
|
||||
name: evolution-net
|
||||
|
||||
driver: bridge
|
||||
|
||||
@@ -5,24 +5,17 @@ services:
|
||||
image: redis:latest
|
||||
container_name: redis
|
||||
command: >
|
||||
redis-server
|
||||
--port 6379
|
||||
--appendonly yes
|
||||
redis-server --port 6379 --appendonly yes
|
||||
volumes:
|
||||
- evolution_redis:/data
|
||||
ports:
|
||||
- 6379:6379
|
||||
|
||||
rebrow:
|
||||
image: marian/rebrow
|
||||
ports:
|
||||
- 5001:5001
|
||||
links:
|
||||
- redis
|
||||
|
||||
|
||||
volumes:
|
||||
evolution_redis:
|
||||
|
||||
|
||||
networks:
|
||||
default:
|
||||
evolution-net:
|
||||
name: evolution-net
|
||||
driver: bridge
|
||||
|
||||
220
Dockerfile
220
Dockerfile
@@ -1,103 +1,151 @@
|
||||
FROM node:16.18-alpine
|
||||
FROM node:20.7.0-alpine AS builder
|
||||
|
||||
LABEL version="1.1.3" description="Api to control whatsapp features through http requests."
|
||||
LABEL version="1.7.1" description="Api to control whatsapp features through http requests."
|
||||
LABEL maintainer="Davidson Gomes" git="https://github.com/DavidsonGomes"
|
||||
LABEL contact="contato@agenciadgcode.com"
|
||||
|
||||
RUN apk update && apk upgrade && \
|
||||
apk add --no-cache git
|
||||
apk add --no-cache git tzdata ffmpeg wget curl
|
||||
|
||||
WORKDIR /evolution
|
||||
|
||||
COPY ./package.json .
|
||||
|
||||
ENV DOCKER_ENV=true
|
||||
|
||||
ENV SERVER_TYPE="http"
|
||||
ENV SERVER_PORT=8080
|
||||
ENV SERVER_URL=$SERVER_URL
|
||||
|
||||
ENV CORS_ORIGIN="*"
|
||||
ENV CORS_METHODS="POST,GET,PUT,DELETE"
|
||||
ENV CORS_CREDENTIALS=true
|
||||
|
||||
ENV LOG_LEVEL=$LOG_LEVEL
|
||||
ENV LOG_COLOR=$LOG_COLOR
|
||||
|
||||
ENV DEL_INSTANCE=$DEL_INSTANCE
|
||||
|
||||
ENV STORE_MESSAGES=$STORE_MESSAGE
|
||||
ENV STORE_MESSAGE_UP=$STORE_MESSAGE_UP
|
||||
ENV STORE_CONTACTS=$STORE_CONTACTS
|
||||
ENV STORE_CHATS=$STORE_CHATS
|
||||
|
||||
ENV CLEAN_STORE_CLEANING_INTERVAL=$CLEAN_STORE_CLEANING_INTERVAL
|
||||
ENV CLEAN_STORE_MESSAGES=$CLEAN_STORE_MESSAGE
|
||||
ENV CLEAN_STORE_MESSAGE_UP=$CLEAN_STORE_MESSAGE_UP
|
||||
ENV CLEAN_STORE_CONTACTS=$CLEAN_STORE_CONTACTS
|
||||
ENV CLEAN_STORE_CHATS=$CLEAN_STORE_CHATS
|
||||
|
||||
ENV DATABASE_ENABLED=$DATABASE_ENABLED
|
||||
ENV DATABASE_CONNECTION_URI=$DATABASE_CONNECTION_URI
|
||||
ENV DATABASE_CONNECTION_DB_PREFIX_NAME=$DATABASE_CONNECTION_DB_PREFIX_NAME
|
||||
ENV DATABASE_SAVE_DATA_INSTANCE=$DATABASE_SAVE_DATA_INSTANCE
|
||||
ENV DATABASE_SAVE_DATA_NEW_MESSAGE=$DATABASE_SAVE_DATA_NEW_MESSAGE
|
||||
ENV DATABASE_SAVE_MESSAGE_UPDATE=$DATABASE_SAVE_MESSAGE_UPDATE
|
||||
ENV DATABASE_SAVE_DATA_CONTACTS=$DATABASE_SAVE_DATA_CONTACTS
|
||||
ENV DATABASE_SAVE_DATA_CHATS=$DATABASE_SAVE_DATA_CHATS
|
||||
|
||||
ENV REDIS_ENABLED=$REDIS_ENABLED
|
||||
ENV REDIS_URI=$REDIS_URI
|
||||
ENV REDIS_PREFIX_KEY=$REDIS_PREFIX_KEY
|
||||
|
||||
ENV WEBHOOK_GLOBAL_URL=$WEBHOOK_GLOBAL_URL
|
||||
ENV WEBHOOK_GLOBAL_ENABLED=$WEBHOOK_GLOBAL_ENABLED
|
||||
ENV WEBHOOK_GLOBAL_WEBHOOK_BY_EVENTS=$WEBHOOK_GLOBAL_WEBHOOK_BY_EVENTS
|
||||
|
||||
ENV WEBHOOK_EVENTS_APPLICATION_STARTUP=$WEBHOOK_EVENTS_APPLICATION_STARTUP
|
||||
ENV WEBHOOK_EVENTS_QRCODE_UPDATED=$WEBHOOK_EVENTS_QRCODE_UPDATED
|
||||
ENV WEBHOOK_EVENTS_MESSAGES_SET=$WEBHOOK_EVENTS_MESSAGES_SET
|
||||
ENV WEBHOOK_EVENTS_MESSAGES_UPSERT=$WEBHOOK_EVENTS_MESSAGES_UPSERT
|
||||
ENV WEBHOOK_EVENTS_MESSAGES_UPDATE=$WEBHOOK_EVENTS_MESSAGES_UPDATE
|
||||
ENV WEBHOOK_EVENTS_SEND_MESSAGE=$WEBHOOK_EVENTS_SEND_MESSAGE
|
||||
ENV WEBHOOK_EVENTS_CONTACTS_SET=$WEBHOOK_EVENTS_CONTACTS_SET
|
||||
ENV WEBHOOK_EVENTS_CONTACTS_UPSERT=$WEBHOOK_EVENTS_CONTACTS_UPSERT
|
||||
ENV WEBHOOK_EVENTS_CONTACTS_UPDATE=$WEBHOOK_EVENTS_CONTACTS_UPDATE
|
||||
ENV WEBHOOK_EVENTS_PRESENCE_UPDATE=$WEBHOOK_EVENTS_PRESENCE_UPDATE
|
||||
ENV WEBHOOK_EVENTS_CHATS_SET=$WEBHOOK_EVENTS_CHATS_SET
|
||||
ENV WEBHOOK_EVENTS_CHATS_UPSERT=$WEBHOOK_EVENTS_CHATS_UPSERT
|
||||
ENV WEBHOOK_EVENTS_CHATS_UPDATE=$WEBHOOK_EVENTS_CHATS_UPDATE
|
||||
ENV WEBHOOK_EVENTS_CONNECTION_UPDATE=$WEBHOOK_EVENTS_CONNECTION_UPDATE
|
||||
ENV WEBHOOK_EVENTS_GROUPS_UPSERT=$WEBHOOK_EVENTS_GROUPS_UPSERT
|
||||
ENV WEBHOOK_EVENTS_GROUPS_UPDATE=$WEBHOOK_EVENTS_GROUPS_UPDATE
|
||||
ENV WEBHOOK_EVENTS_GROUP_PARTICIPANTS_UPDATE=$WEBHOOK_EVENTS_GROUP_PARTICIPANTS_UPDATE
|
||||
|
||||
ENV WEBHOOK_EVENTS_NEW_JWT_TOKEN=$WEBHOOK_EVENTS_NEW_JWT_TOKEN
|
||||
|
||||
ENV CONFIG_SESSION_PHONE_CLIENT=$CONFIG_SESSION_PHONE_CLIENT
|
||||
ENV CONFIG_SESSION_PHONE_NAME=$CONFIG_SESSION_PHONE_NAME
|
||||
|
||||
ENV QRCODE_LIMIT=$QRCODE_LIMIT
|
||||
|
||||
ENV AUTHENTICATION_TYPE=$AUTHENTICATION_TYPE
|
||||
|
||||
ENV AUTHENTICATION_API_KEY=$AUTHENTICATION_API_KEY
|
||||
ENV AUTHENTICATION_EXPOSE_IN_FETCH_INSTANCES=$AUTHENTICATION_EXPOSE_IN_FETCH_INSTANCES
|
||||
|
||||
ENV AUTHENTICATION_JWT_EXPIRIN_IN=$AUTHENTICATION_JWT_EXPIRIN_IN
|
||||
ENV AUTHENTICATION_JWT_SECRET="L=0YWt]b2w[WF>#>:&E`"
|
||||
|
||||
ENV AUTHENTICATION_INSTANCE_NAME=$AUTHENTICATION_INSTANCE_NAME
|
||||
ENV AUTHENTICATION_INSTANCE_WEBHOOK_URL=$AUTHENTICATION_INSTANCE_WEBHOOK_URL
|
||||
ENV AUTHENTICATION_INSTANCE_CHATWOOT_ACCOUNT_ID=$AUTHENTICATION_INSTANCE_CHATWOOT_ACCOUNT_ID
|
||||
ENV AUTHENTICATION_INSTANCE_CHATWOOT_TOKEN=$AUTHENTICATION_INSTANCE_CHATWOOT_TOKEN
|
||||
ENV AUTHENTICATION_INSTANCE_CHATWOOT_URL=$AUTHENTICATION_INSTANCE_CHATWOOT_URL
|
||||
ENV AUTHENTICATION_INSTANCE_MODE=$AUTHENTICATION_INSTANCE_MODE
|
||||
|
||||
RUN npm install
|
||||
|
||||
COPY . .
|
||||
|
||||
RUN npm run build
|
||||
|
||||
FROM node:20.7.0-alpine AS final
|
||||
|
||||
ENV TZ=America/Sao_Paulo
|
||||
ENV DOCKER_ENV=true
|
||||
|
||||
ENV SERVER_TYPE=http
|
||||
ENV SERVER_PORT=8080
|
||||
ENV SERVER_URL=http://localhost:8080
|
||||
|
||||
ENV CORS_ORIGIN=*
|
||||
ENV CORS_METHODS=POST,GET,PUT,DELETE
|
||||
ENV CORS_CREDENTIALS=true
|
||||
|
||||
ENV LOG_LEVEL=ERROR,WARN,DEBUG,INFO,LOG,VERBOSE,DARK,WEBHOOKS
|
||||
ENV LOG_COLOR=true
|
||||
ENV LOG_BAILEYS=error
|
||||
|
||||
ENV DEL_INSTANCE=false
|
||||
ENV DEL_TEMP_INSTANCES=true
|
||||
|
||||
ENV STORE_MESSAGES=true
|
||||
ENV STORE_MESSAGE_UP=true
|
||||
ENV STORE_CONTACTS=true
|
||||
ENV STORE_CHATS=true
|
||||
|
||||
ENV CLEAN_STORE_CLEANING_INTERVAL=7200
|
||||
ENV CLEAN_STORE_MESSAGES=true
|
||||
ENV CLEAN_STORE_MESSAGE_UP=true
|
||||
ENV CLEAN_STORE_CONTACTS=true
|
||||
ENV CLEAN_STORE_CHATS=true
|
||||
|
||||
ENV DATABASE_ENABLED=false
|
||||
ENV DATABASE_CONNECTION_URI=mongodb://root:root@mongodb:27017/?authSource=admin&readPreference=primary&ssl=false&directConnection=true
|
||||
ENV DATABASE_CONNECTION_DB_PREFIX_NAME=evolution
|
||||
|
||||
ENV DATABASE_SAVE_DATA_INSTANCE=false
|
||||
ENV DATABASE_SAVE_DATA_NEW_MESSAGE=false
|
||||
ENV DATABASE_SAVE_MESSAGE_UPDATE=false
|
||||
ENV DATABASE_SAVE_DATA_CONTACTS=false
|
||||
ENV DATABASE_SAVE_DATA_CHATS=false
|
||||
|
||||
ENV REDIS_ENABLED=false
|
||||
ENV REDIS_URI=redis://redis:6379
|
||||
ENV REDIS_PREFIX_KEY=evolution
|
||||
|
||||
ENV RABBITMQ_ENABLED=false
|
||||
ENV RABBITMQ_MODE=global
|
||||
ENV RABBITMQ_EXCHANGE_NAME=evolution_exchange
|
||||
ENV RABBITMQ_URI=amqp://guest:guest@rabbitmq:5672
|
||||
|
||||
ENV WEBSOCKET_ENABLED=false
|
||||
|
||||
ENV WA_BUSINESS_TOKEN_WEBHOOK=evolution
|
||||
ENV WA_BUSINESS_URL=https://graph.facebook.com
|
||||
ENV WA_BUSINESS_VERSION=v18.0
|
||||
ENV WA_BUSINESS_LANGUAGE=pt_BR
|
||||
|
||||
ENV SQS_ENABLED=false
|
||||
ENV SQS_ACCESS_KEY_ID=
|
||||
ENV SQS_SECRET_ACCESS_KEY=
|
||||
ENV SQS_ACCOUNT_ID=
|
||||
ENV SQS_REGION=
|
||||
|
||||
ENV WEBHOOK_GLOBAL_URL=
|
||||
ENV WEBHOOK_GLOBAL_ENABLED=false
|
||||
|
||||
ENV WEBHOOK_GLOBAL_WEBHOOK_BY_EVENTS=false
|
||||
|
||||
ENV WEBHOOK_EVENTS_APPLICATION_STARTUP=false
|
||||
ENV WEBHOOK_EVENTS_INSTANCE_CREATE=false
|
||||
ENV WEBHOOK_EVENTS_INSTANCE_DELETE=false
|
||||
ENV WEBHOOK_EVENTS_QRCODE_UPDATED=true
|
||||
ENV WEBHOOK_EVENTS_MESSAGES_SET=true
|
||||
ENV WEBHOOK_EVENTS_MESSAGES_UPSERT=true
|
||||
ENV WEBHOOK_EVENTS_MESSAGES_UPDATE=true
|
||||
ENV WEBHOOK_EVENTS_MESSAGES_DELETE=true
|
||||
ENV WEBHOOK_EVENTS_SEND_MESSAGE=true
|
||||
ENV WEBHOOK_EVENTS_CONTACTS_SET=true
|
||||
ENV WEBHOOK_EVENTS_CONTACTS_UPSERT=true
|
||||
ENV WEBHOOK_EVENTS_CONTACTS_UPDATE=true
|
||||
ENV WEBHOOK_EVENTS_PRESENCE_UPDATE=true
|
||||
ENV WEBHOOK_EVENTS_CHATS_SET=true
|
||||
ENV WEBHOOK_EVENTS_CHATS_UPSERT=true
|
||||
ENV WEBHOOK_EVENTS_CHATS_UPDATE=true
|
||||
ENV WEBHOOK_EVENTS_CHATS_DELETE=true
|
||||
ENV WEBHOOK_EVENTS_GROUPS_UPSERT=true
|
||||
ENV WEBHOOK_EVENTS_GROUPS_UPDATE=true
|
||||
ENV WEBHOOK_EVENTS_GROUP_PARTICIPANTS_UPDATE=true
|
||||
ENV WEBHOOK_EVENTS_CONNECTION_UPDATE=true
|
||||
ENV WEBHOOK_EVENTS_LABELS_EDIT=true
|
||||
ENV WEBHOOK_EVENTS_LABELS_ASSOCIATION=true
|
||||
ENV WEBHOOK_EVENTS_CALL=true
|
||||
|
||||
ENV WEBHOOK_EVENTS_NEW_JWT_TOKEN=false
|
||||
|
||||
ENV WEBHOOK_EVENTS_TYPEBOT_START=false
|
||||
ENV WEBHOOK_EVENTS_TYPEBOT_CHANGE_STATUS=false
|
||||
|
||||
ENV WEBHOOK_EVENTS_CHAMA_AI_ACTION=false
|
||||
|
||||
ENV WEBHOOK_EVENTS_ERRORS=false
|
||||
ENV WEBHOOK_EVENTS_ERRORS_WEBHOOK=
|
||||
|
||||
ENV CONFIG_SESSION_PHONE_CLIENT=EvolutionAPI
|
||||
ENV CONFIG_SESSION_PHONE_NAME=Chrome
|
||||
|
||||
ENV QRCODE_LIMIT=30
|
||||
ENV QRCODE_COLOR=#198754
|
||||
|
||||
ENV TYPEBOT_API_VERSION=latest
|
||||
|
||||
ENV AUTHENTICATION_TYPE=apikey
|
||||
|
||||
ENV AUTHENTICATION_API_KEY=B6D711FCDE4D4FD5936544120E713976
|
||||
ENV AUTHENTICATION_EXPOSE_IN_FETCH_INSTANCES=true
|
||||
|
||||
ENV AUTHENTICATION_JWT_EXPIRIN_IN=0
|
||||
ENV AUTHENTICATION_JWT_SECRET='L=0YWt]b2w[WF>#>:&E`'
|
||||
|
||||
ENV AUTHENTICATION_INSTANCE_MODE=server
|
||||
|
||||
ENV AUTHENTICATION_INSTANCE_NAME=evolution
|
||||
ENV AUTHENTICATION_INSTANCE_WEBHOOK_URL=<url>
|
||||
ENV AUTHENTICATION_INSTANCE_CHATWOOT_ACCOUNT_ID=1
|
||||
ENV AUTHENTICATION_INSTANCE_CHATWOOT_TOKEN=123456
|
||||
ENV AUTHENTICATION_INSTANCE_CHATWOOT_URL=<url>
|
||||
|
||||
WORKDIR /evolution
|
||||
|
||||
COPY --from=builder /evolution .
|
||||
|
||||
CMD [ "node", "./dist/src/main.js" ]
|
||||
|
||||
1
Extras/appsmith/manager.json
Normal file
1
Extras/appsmith/manager.json
Normal file
File diff suppressed because one or more lines are too long
241
Extras/chatwoot/configurar_admin.json
Normal file
241
Extras/chatwoot/configurar_admin.json
Normal file
@@ -0,0 +1,241 @@
|
||||
{
|
||||
"name": "[Evolution] Configurar Admin",
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {
|
||||
"values": {
|
||||
"string": [
|
||||
{
|
||||
"name": "api_access_token",
|
||||
"value": "CHATWOOT_ADMIN_USER_TOKEN"
|
||||
},
|
||||
{
|
||||
"name": "chatwoot_url",
|
||||
"value": "https://CHATWOOT_URL"
|
||||
},
|
||||
{
|
||||
"name": "n8n_url",
|
||||
"value": "https://N8N_URL"
|
||||
},
|
||||
{
|
||||
"name": "organization",
|
||||
"value": "ORGANIZATION_NAME"
|
||||
},
|
||||
{
|
||||
"name": "logo",
|
||||
"value": "ORGANIZATION_LOGO"
|
||||
}
|
||||
]
|
||||
},
|
||||
"options": {}
|
||||
},
|
||||
"id": "7a89a538-2cae-4032-8896-09627c07bc68",
|
||||
"name": "Info Base",
|
||||
"type": "n8n-nodes-base.set",
|
||||
"typeVersion": 2,
|
||||
"position": [
|
||||
620,
|
||||
480
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"method": "POST",
|
||||
"url": "={{ $('Info Base').item.json[\"chatwoot_url\"] }}/api/v1/accounts/1/contacts/",
|
||||
"sendHeaders": true,
|
||||
"headerParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "api_access_token",
|
||||
"value": "={{ $('Info Base').item.json[\"api_access_token\"] }}"
|
||||
}
|
||||
]
|
||||
},
|
||||
"sendBody": true,
|
||||
"specifyBody": "json",
|
||||
"jsonBody": "={\n \"inbox_id\": {{ $('Cria Inbox Start').item.json[\"id\"] }},\n \"name\": \"Bot {{ $('Info Base').item.json[\"organization\"] }}\",\n \"phone_number\": \"+123456\",\n \"avatar_url\": \"{{ $('Info Base').item.json[\"logo\"] }}\"\n}",
|
||||
"options": {}
|
||||
},
|
||||
"id": "12a39df3-6b95-4f83-a0bc-50b25adaca7f",
|
||||
"name": "Cria Contato Bot",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"typeVersion": 3,
|
||||
"position": [
|
||||
1020,
|
||||
480
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"method": "POST",
|
||||
"url": "={{ $('Info Base').item.json[\"chatwoot_url\"] }}/api/v1/accounts/1/inboxes/",
|
||||
"sendHeaders": true,
|
||||
"headerParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "api_access_token",
|
||||
"value": "={{ $('Info Base').item.json[\"api_access_token\"] }}"
|
||||
}
|
||||
]
|
||||
},
|
||||
"sendBody": true,
|
||||
"specifyBody": "json",
|
||||
"jsonBody": "={\n \"name\": \"Start {{ $('Info Base').item.json[\"organization\"] }}\",\n \"channel\": {\n \"type\": \"api\",\n \"website_url\": \"\"\n }\n}",
|
||||
"options": {}
|
||||
},
|
||||
"id": "bed7c54d-e232-4fe4-9584-0515e9679868",
|
||||
"name": "Cria Inbox Start",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"typeVersion": 3,
|
||||
"position": [
|
||||
820,
|
||||
480
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "36ada769-a757-4193-989b-0cc4ea504b80",
|
||||
"name": "When clicking \"Execute Workflow\"",
|
||||
"type": "n8n-nodes-base.manualTrigger",
|
||||
"typeVersion": 1,
|
||||
"position": [
|
||||
420,
|
||||
480
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"method": "POST",
|
||||
"url": "={{ $('Info Base').item.json[\"chatwoot_url\"] }}/api/v1/accounts/1/automation_rules/",
|
||||
"sendHeaders": true,
|
||||
"headerParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "api_access_token",
|
||||
"value": "={{ $('Info Base').item.json[\"api_access_token\"] }}"
|
||||
}
|
||||
]
|
||||
},
|
||||
"sendBody": true,
|
||||
"specifyBody": "json",
|
||||
"jsonBody": "={\n \"name\": \"Create Company Chatwoot\",\n \"description\": \"Create Company Chatwoot\",\n \"event_name\": \"message_created\",\n \"active\": true,\n \"actions\": \n [\n {\n \"action_name\": \"send_webhook_event\",\n \"action_params\": [\"{{ $('Info Base').item.json[\"n8n_url\"] }}/webhook/criadorchatwoot\"]\n }\n ],\n \"conditions\": \n [\n {\n \"attribute_key\": \"content\",\n \"filter_operator\": \"contains\",\n \"query_operator\": \"and\",\n \"values\": [\"Tema Criador de Empresa:\"]\n },\n {\n \"attribute_key\": \"phone_number\",\n \"filter_operator\": \"equal_to\",\n \"values\": [\"+123456\"]\n }\n ]\n}",
|
||||
"options": {}
|
||||
},
|
||||
"id": "f5bbb285-71a8-4c58-a4d7-e56002d697f0",
|
||||
"name": "Cria Automação Empresas",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"typeVersion": 3,
|
||||
"position": [
|
||||
1220,
|
||||
480
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"method": "POST",
|
||||
"url": "={{ $('Info Base').item.json[\"chatwoot_url\"] }}/api/v1/accounts/1/automation_rules/",
|
||||
"sendHeaders": true,
|
||||
"headerParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "api_access_token",
|
||||
"value": "={{ $('Info Base').item.json[\"api_access_token\"] }}"
|
||||
}
|
||||
]
|
||||
},
|
||||
"sendBody": true,
|
||||
"specifyBody": "json",
|
||||
"jsonBody": "={\n \"name\": \"Create Inbox {{ $('Info Base').item.json[\"organization\"] }}\",\n \"description\": \"Create Inbox {{ $('Info Base').item.json[\"organization\"] }}\",\n \"event_name\": \"message_created\",\n \"active\": true,\n \"actions\": \n [\n {\n \"action_name\": \"send_webhook_event\",\n \"action_params\": [\"{{ $('Info Base').item.json[\"n8n_url\"] }}/webhook/inbox_whatsapp?utoken={{ $('Info Base').item.json[\"api_access_token\"] }}&organization={{ $('Info Base').item.json[\"organization\"] }}\"]\n }\n ],\n \"conditions\": \n [\n {\n \"attribute_key\": \"content\",\n \"filter_operator\": \"contains\",\n \"query_operator\": \"and\",\n \"values\": [\"start:\"]\n },\n \n {\n \"attribute_key\": \"phone_number\",\n \"filter_operator\": \"equal_to\",\n \"query_operator\": \"or\",\n \"values\": [\"+123456\"]\n },\n\n\n {\n \"attribute_key\": \"content\",\n \"filter_operator\": \"contains\",\n \"query_operator\": \"and\",\n \"values\": [\"new_instance:\"]\n },\n {\n \"attribute_key\": \"phone_number\",\n \"filter_operator\": \"equal_to\",\n \"values\": [\"+123456\"]\n }\n ]\n}",
|
||||
"options": {}
|
||||
},
|
||||
"id": "a36bebdc-a318-40a2-8532-c7f476f8adb7",
|
||||
"name": "Cria Automação Inboxes",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"typeVersion": 3,
|
||||
"position": [
|
||||
1420,
|
||||
480
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"content": "## Workflow Para Configurar admin\n**Aqui você prepara o Chatwoot Principal com um usuário (Superadmin) que poderá criar empresas e caixas de entrada**\n**Instruções**\n**No node Info Base, configure as variáveis de seu Chatwoot e N8N**\n**Obs: A variável api_access_token é o token do usuário que irá poder criar as empresas**",
|
||||
"width": 894.6435495898575
|
||||
},
|
||||
"id": "db66e867-e9f4-452d-b521-725eeac652c8",
|
||||
"name": "Sticky Note",
|
||||
"type": "n8n-nodes-base.stickyNote",
|
||||
"typeVersion": 1,
|
||||
"position": [
|
||||
420,
|
||||
280
|
||||
]
|
||||
}
|
||||
],
|
||||
"pinData": {},
|
||||
"connections": {
|
||||
"Info Base": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Cria Inbox Start",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Cria Contato Bot": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Cria Automação Empresas",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Cria Inbox Start": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Cria Contato Bot",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"When clicking \"Execute Workflow\"": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Info Base",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Cria Automação Empresas": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Cria Automação Inboxes",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"active": false,
|
||||
"settings": {},
|
||||
"versionId": "78f155dc-7809-4bfc-9282-63f49b07fc4d",
|
||||
"id": "BSATyGpGWLR4ZwNm",
|
||||
"meta": {
|
||||
"instanceId": "4ff16e963c7f5197d7e99e6239192860914312fea0ce2a9a7fd14d74a0a0e906"
|
||||
},
|
||||
"tags": []
|
||||
}
|
||||
456
Extras/chatwoot/criador_de_empresas.json
Normal file
456
Extras/chatwoot/criador_de_empresas.json
Normal file
@@ -0,0 +1,456 @@
|
||||
{
|
||||
"name": "[Evolution] Criador de Empresas",
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {
|
||||
"httpMethod": "POST",
|
||||
"path": "criadorchatwoot",
|
||||
"options": {}
|
||||
},
|
||||
"id": "5a47c10a-e43c-4fa5-baad-4b6cc511bfcd",
|
||||
"name": "Webhook",
|
||||
"type": "n8n-nodes-base.webhook",
|
||||
"typeVersion": 1,
|
||||
"position": [
|
||||
1420,
|
||||
860
|
||||
],
|
||||
"webhookId": "6fe428e3-1752-453c-9358-abf18b793387"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"method": "POST",
|
||||
"url": "={{ $('Info Base').item.json[\"chatwoot_url\"] }}/platform/api/v1/accounts",
|
||||
"sendHeaders": true,
|
||||
"headerParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "api_access_token",
|
||||
"value": "={{ $('Info Base').item.json[\"api_access_token\"] }}"
|
||||
}
|
||||
]
|
||||
},
|
||||
"sendBody": true,
|
||||
"bodyParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "name",
|
||||
"value": "={{ $json.name_company }}"
|
||||
},
|
||||
{
|
||||
"name": "locale",
|
||||
"value": "pt_BR"
|
||||
}
|
||||
]
|
||||
},
|
||||
"options": {}
|
||||
},
|
||||
"id": "8295c119-3a96-424e-9386-43d75f6816f5",
|
||||
"name": "Cria Conta",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"typeVersion": 3,
|
||||
"position": [
|
||||
2020,
|
||||
860
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"method": "POST",
|
||||
"url": "={{ $('Info Base').item.json[\"chatwoot_url\"] }}/platform/api/v1/users",
|
||||
"sendHeaders": true,
|
||||
"headerParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "api_access_token",
|
||||
"value": "={{ $('Info Base').item.json[\"api_access_token\"] }}"
|
||||
}
|
||||
]
|
||||
},
|
||||
"sendBody": true,
|
||||
"bodyParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "name",
|
||||
"value": "={{ $('Info Base').item.json.name_admin }}"
|
||||
},
|
||||
{
|
||||
"name": "email",
|
||||
"value": "={{ $('Info Base').item.json[\"email\"] }}"
|
||||
},
|
||||
{
|
||||
"name": "password",
|
||||
"value": "={{ $('Info Base').item.json[\"password\"] }}"
|
||||
}
|
||||
]
|
||||
},
|
||||
"options": {}
|
||||
},
|
||||
"id": "4fe5007a-3a6b-490a-a446-e45cc168189f",
|
||||
"name": "Cria Usuario",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"typeVersion": 3,
|
||||
"position": [
|
||||
2220,
|
||||
860
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"method": "POST",
|
||||
"url": "={{ $('Info Base').item.json[\"chatwoot_url\"] }}/platform/api/v1/accounts/{{ $node[\"Cria Conta\"].json[\"id\"] }}/account_users",
|
||||
"sendHeaders": true,
|
||||
"headerParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "api_access_token",
|
||||
"value": "={{ $('Info Base').item.json[\"api_access_token\"] }}"
|
||||
}
|
||||
]
|
||||
},
|
||||
"sendBody": true,
|
||||
"bodyParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "user_id",
|
||||
"value": "={{ $node[\"Cria Usuario\"].json[\"id\"] }}"
|
||||
},
|
||||
{
|
||||
"name": "role",
|
||||
"value": "administrator"
|
||||
}
|
||||
]
|
||||
},
|
||||
"options": {}
|
||||
},
|
||||
"id": "848c55e2-5678-4291-9602-c94d994da95b",
|
||||
"name": "Add Usuario a Conta",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"typeVersion": 3,
|
||||
"position": [
|
||||
2420,
|
||||
860
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"fromEmail": "={{ $('Info Base').item.json[\"from_email\"] }}",
|
||||
"toEmail": "={{ $('LimpaDados').item.json.email }}",
|
||||
"subject": "=Bem vindo à {{ $('Info Base').item.json[\"organization\"] }}",
|
||||
"text": "=Olá seja bem vindo:\n\nAbaixo segue seus dados de acesso:\n\nURL: {{ $('Info Base').item.json[\"chatwoot_url\"] }}\n\nuser: {{ $('LimpaDados').item.json[\"email\"] }}\n\nSenha: {{ $('LimpaDados').item.json[\"password\"] }}",
|
||||
"options": {}
|
||||
},
|
||||
"id": "27f3b24f-1cf2-4d0d-a354-ecba066059f6",
|
||||
"name": "Send Email",
|
||||
"type": "n8n-nodes-base.emailSend",
|
||||
"typeVersion": 2,
|
||||
"position": [
|
||||
3220,
|
||||
860
|
||||
],
|
||||
"credentials": {
|
||||
"smtp": {
|
||||
"id": "6BxluEUV8zrXKoVG",
|
||||
"name": "[Dgcode] SMTP"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"values": {
|
||||
"string": [
|
||||
{
|
||||
"name": "api_access_token",
|
||||
"value": "CHATWOOT_PLATFORM_TOKEN"
|
||||
},
|
||||
{
|
||||
"name": "chatwoot_url",
|
||||
"value": "https://CHATWOOT_URL"
|
||||
},
|
||||
{
|
||||
"name": "n8n_url",
|
||||
"value": "https://N8N_URL"
|
||||
},
|
||||
{
|
||||
"name": "organization",
|
||||
"value": "ORGANIZATION_NAME"
|
||||
},
|
||||
{
|
||||
"name": "logo",
|
||||
"value": "ORGANIZATION_LOGO"
|
||||
},
|
||||
{
|
||||
"name": "from_email",
|
||||
"value": "FROM_EMAIL"
|
||||
},
|
||||
{
|
||||
"name": "name",
|
||||
"value": "={{ $json.name_company }}"
|
||||
},
|
||||
{
|
||||
"name": "email",
|
||||
"value": "={{ $json.email }}"
|
||||
},
|
||||
{
|
||||
"name": "password",
|
||||
"value": "={{ $json.password }}"
|
||||
},
|
||||
{
|
||||
"name": "name_company",
|
||||
"value": "={{ $json.name_company }}"
|
||||
}
|
||||
]
|
||||
},
|
||||
"options": {}
|
||||
},
|
||||
"id": "38b4069d-e51e-4db7-933f-941b1be6d124",
|
||||
"name": "Info Base",
|
||||
"type": "n8n-nodes-base.set",
|
||||
"typeVersion": 2,
|
||||
"position": [
|
||||
1820,
|
||||
860
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"keepOnlySet": true,
|
||||
"values": {
|
||||
"string": [
|
||||
{
|
||||
"name": "name_admin",
|
||||
"value": "={{$node[\"Webhook\"].json[\"body\"][\"messages\"][0][\"content\"].match(/Nome Usuario Administrador: ([^\\n]+)/)[1];}}"
|
||||
},
|
||||
{
|
||||
"name": "name_company",
|
||||
"value": "={{$node[\"Webhook\"].json[\"body\"][\"messages\"][0][\"content\"].match(/Nome da Empresa: ([^\\n]+)/)[1];}}"
|
||||
},
|
||||
{
|
||||
"name": "email",
|
||||
"value": "={{$node[\"Webhook\"].json[\"body\"][\"messages\"][0][\"content\"].match(/Email: ([^\\s]+)/)[1];}}"
|
||||
},
|
||||
{
|
||||
"name": "password",
|
||||
"value": "={{$node[\"Webhook\"].json[\"body\"][\"messages\"][0][\"content\"].match(/Senha: ([^\\s]+)/)[1];}}"
|
||||
}
|
||||
]
|
||||
},
|
||||
"options": {}
|
||||
},
|
||||
"id": "28e29e73-aadc-49ca-bd6d-b57ee0160a21",
|
||||
"name": "LimpaDados",
|
||||
"type": "n8n-nodes-base.set",
|
||||
"typeVersion": 2,
|
||||
"position": [
|
||||
1620,
|
||||
860
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"method": "POST",
|
||||
"url": "={{ $('Info Base').item.json[\"chatwoot_url\"] }}/api/v1/accounts/{{ $('Add Usuario a Conta').item.json.account_id }}/contacts/",
|
||||
"sendHeaders": true,
|
||||
"headerParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "api_access_token",
|
||||
"value": "={{ $('Cria Usuario').item.json.access_token }}"
|
||||
}
|
||||
]
|
||||
},
|
||||
"sendBody": true,
|
||||
"specifyBody": "json",
|
||||
"jsonBody": "={\n \"inbox_id\": {{ $('Cria Inbox Start').item.json[\"id\"] }},\n \"name\": \"Bot {{ $('Info Base').item.json[\"organization\"] }}\",\n \"phone_number\": \"+123456\",\n \"avatar_url\": \"{{ $('Info Base').item.json[\"logo\"] }}\"\n}",
|
||||
"options": {}
|
||||
},
|
||||
"id": "bb671443-bdb4-4f56-99af-f0baef246a3e",
|
||||
"name": "Cria Contato Bot",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"typeVersion": 3,
|
||||
"position": [
|
||||
2820,
|
||||
860
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"method": "POST",
|
||||
"url": "={{ $('Info Base').item.json[\"chatwoot_url\"] }}/api/v1/accounts/{{ $('Add Usuario a Conta').item.json.account_id }}/automation_rules/",
|
||||
"sendHeaders": true,
|
||||
"headerParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "api_access_token",
|
||||
"value": "={{ $('Cria Usuario').item.json.access_token }}"
|
||||
}
|
||||
]
|
||||
},
|
||||
"sendBody": true,
|
||||
"specifyBody": "json",
|
||||
"jsonBody": "={\n \"name\": \"Create Inbox {{ $('Info Base').item.json[\"organization\"] }}\",\n \"description\": \"Create Inbox {{ $('Info Base').item.json[\"organization\"] }}\",\n \"event_name\": \"message_created\",\n \"active\": true,\n \"actions\": \n [\n {\n \"action_name\": \"send_webhook_event\",\n \"action_params\": [\"{{ $('Info Base').item.json[\"n8n_url\"] }}/webhook/inbox_whatsapp?utoken={{ $('Cria Usuario').item.json.access_token }}&organization={{ $('Info Base').item.json[\"organization\"] }}\"]\n }\n ],\n \"conditions\": \n [\n {\n \"attribute_key\": \"content\",\n \"filter_operator\": \"contains\",\n \"query_operator\": \"and\",\n \"values\": [\"start:\"]\n },\n {\n \"attribute_key\": \"phone_number\",\n \"filter_operator\": \"equal_to\",\n \"query_operator\": \"or\",\n \"values\": [\"+123456\"]\n },\n {\n \"attribute_key\": \"content\",\n \"filter_operator\": \"contains\",\n \"query_operator\": \"and\",\n \"values\": [\"new_instance:\"]\n },\n {\n \"attribute_key\": \"phone_number\",\n \"filter_operator\": \"equal_to\",\n \"values\": [\"+123456\"]\n }\n ]\n}",
|
||||
"options": {}
|
||||
},
|
||||
"id": "e016a2af-b212-4e00-a3ff-8cd03530aa06",
|
||||
"name": "Cria Automação",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"typeVersion": 3,
|
||||
"position": [
|
||||
3020,
|
||||
860
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"method": "POST",
|
||||
"url": "={{ $('Info Base').item.json[\"chatwoot_url\"] }}/api/v1/accounts/{{ $json.account_id }}/inboxes/",
|
||||
"sendHeaders": true,
|
||||
"headerParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "api_access_token",
|
||||
"value": "={{ $('Cria Usuario').item.json.access_token }}"
|
||||
}
|
||||
]
|
||||
},
|
||||
"sendBody": true,
|
||||
"specifyBody": "json",
|
||||
"jsonBody": "={\n \"name\": \"Start {{ $('Info Base').item.json[\"organization\"] }}\",\n \"channel\": {\n \"type\": \"api\",\n \"website_url\": \"\"\n }\n}",
|
||||
"options": {}
|
||||
},
|
||||
"id": "d3c42148-8920-4c98-a874-eb7113f2dd22",
|
||||
"name": "Cria Inbox Start",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"typeVersion": 3,
|
||||
"position": [
|
||||
2620,
|
||||
860
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"content": "## Workflow Criador de Empresas\n**Cria Contas (Empresas) e Usuários através de tema**\n**Instruções**\n**No node Info Base, configure as variáveis de seu Chatwoot e N8N**\n**Obs: A variável api_access_token é o token PlatformApp encontrado no acesso ao Super Admin**\n**Tema para criar novas empresa:**\n\nTema Criador de Empresa:\n\nNome Usuario Administrador: Joao Linhares\nNome da Empresa: Oficina Linhates\nEmail: machineteste24@gmail.com\nSenha: Mfcd62!!",
|
||||
"height": 304.02684563758396,
|
||||
"width": 1129.7777777777778
|
||||
},
|
||||
"id": "d07516c0-4c8e-43ab-ba86-c8d063b09be5",
|
||||
"name": "Sticky Note",
|
||||
"type": "n8n-nodes-base.stickyNote",
|
||||
"typeVersion": 1,
|
||||
"position": [
|
||||
1420,
|
||||
520
|
||||
]
|
||||
}
|
||||
],
|
||||
"pinData": {},
|
||||
"connections": {
|
||||
"Webhook": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "LimpaDados",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Cria Conta": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Cria Usuario",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Cria Usuario": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Add Usuario a Conta",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Add Usuario a Conta": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Cria Inbox Start",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Info Base": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Cria Conta",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"LimpaDados": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Info Base",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Cria Contato Bot": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Cria Automação",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Cria Automação": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Send Email",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Cria Inbox Start": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Cria Contato Bot",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"active": true,
|
||||
"settings": {},
|
||||
"versionId": "3ffd6d3f-6966-4de4-af8f-1fda464bc1b8",
|
||||
"id": "79R6qQDtfyCwgYjJ",
|
||||
"meta": {
|
||||
"instanceId": "4ff16e963c7f5197d7e99e6239192860914312fea0ce2a9a7fd14d74a0a0e906"
|
||||
},
|
||||
"tags": []
|
||||
}
|
||||
510
Extras/chatwoot/criador_de_inbox.json
Normal file
510
Extras/chatwoot/criador_de_inbox.json
Normal file
@@ -0,0 +1,510 @@
|
||||
{
|
||||
"name": "[Evolution] Criador de Inbox",
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {
|
||||
"httpMethod": "POST",
|
||||
"path": "inbox_whatsapp",
|
||||
"options": {}
|
||||
},
|
||||
"id": "8205b929-73e9-456a-9b0d-e1474991663a",
|
||||
"name": "Webhook",
|
||||
"type": "n8n-nodes-base.webhook",
|
||||
"typeVersion": 1,
|
||||
"position": [
|
||||
320,
|
||||
300
|
||||
],
|
||||
"webhookId": "cf37002d-3869-4bb1-af3a-739fdd3c1756"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"method": "POST",
|
||||
"url": "={{ $json.evolution_url }}/instance/create",
|
||||
"sendHeaders": true,
|
||||
"headerParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "Content-Type",
|
||||
"value": "application/json"
|
||||
},
|
||||
{
|
||||
"name": "apikey",
|
||||
"value": "={{ $json.global_api_key }}"
|
||||
}
|
||||
]
|
||||
},
|
||||
"sendBody": true,
|
||||
"bodyParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "instanceName",
|
||||
"value": "={{ $json.instance_name }}"
|
||||
},
|
||||
{
|
||||
"name": "qrcode",
|
||||
"value": "={{ $json.qrcode }}"
|
||||
},
|
||||
{
|
||||
"name": "chatwoot_account_id",
|
||||
"value": "={{ $json.chatwoot_account_id }}"
|
||||
},
|
||||
{
|
||||
"name": "chatwoot_token",
|
||||
"value": "={{ $json.chatwoot_token }}"
|
||||
},
|
||||
{
|
||||
"name": "chatwoot_url",
|
||||
"value": "={{ $json.chatwoot_url }}"
|
||||
},
|
||||
{
|
||||
"name": "chatwoot_sign_msg",
|
||||
"value": "={{ $json.chatwoot_sign_msg }}"
|
||||
},
|
||||
{
|
||||
"name": "chatwoot_reopen_conversation",
|
||||
"value": "={{ $json.chatwoot_reopen_conversation }}"
|
||||
},
|
||||
{
|
||||
"name": "chatwoot_conversation_pending",
|
||||
"value": "={{ $json.chatwoot_conversation_pending }}"
|
||||
},
|
||||
{
|
||||
"name": "reject_call",
|
||||
"value": "={{ $json.reject_call }}"
|
||||
},
|
||||
{
|
||||
"name": "msg_call",
|
||||
"value": "={{ $json.msg_call }}"
|
||||
},
|
||||
{
|
||||
"name": "groups_ignore",
|
||||
"value": "={{ $json.groups_ignore }}"
|
||||
},
|
||||
{
|
||||
"name": "always_online",
|
||||
"value": "={{ $json.always_online }}"
|
||||
},
|
||||
{
|
||||
"name": "read_messages",
|
||||
"value": "={{ $json.read_messages }}"
|
||||
},
|
||||
{
|
||||
"name": "read_status",
|
||||
"value": "={{ $json.read_status }}"
|
||||
}
|
||||
]
|
||||
},
|
||||
"options": {}
|
||||
},
|
||||
"id": "275aa370-2fdb-42f4-844a-2fb3051301bd",
|
||||
"name": "Cria Instancia",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"typeVersion": 4.1,
|
||||
"position": [
|
||||
760,
|
||||
300
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"url": "={{ $('Info Base').item.json[\"chatwoot_url\"] }}/api/v1/accounts/{{ $('Info Base').item.json[\"chatwoot_account_id\"] }}/inboxes/",
|
||||
"sendHeaders": true,
|
||||
"headerParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "api_access_token",
|
||||
"value": "={{ $('Info Base').item.json.chatwoot_token }}"
|
||||
}
|
||||
]
|
||||
},
|
||||
"options": {}
|
||||
},
|
||||
"id": "e4650812-ba0a-4f72-8bd8-a235eca4b2de",
|
||||
"name": "Lista Inboxes",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"typeVersion": 3,
|
||||
"position": [
|
||||
980,
|
||||
300
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"content": "## Workflow Para Criar Inbox\n**Aqui você configura a comunicação entre o chatwoot e a Evolution API para criar novas instâncias a partir do chatwoot**\n**Instruções**\n**No node Info Base, configure as variáveis de seu Chatwoot e Evolution API**",
|
||||
"width": 1129.7777777777778
|
||||
},
|
||||
"id": "aa763d9e-d973-44fc-8399-277bb24718a5",
|
||||
"name": "Sticky Note",
|
||||
"type": "n8n-nodes-base.stickyNote",
|
||||
"typeVersion": 1,
|
||||
"position": [
|
||||
320,
|
||||
80
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"keepOnlySet": true,
|
||||
"values": {
|
||||
"string": [
|
||||
{
|
||||
"name": "chatwoot_url",
|
||||
"value": "CHATWOOT_URL"
|
||||
},
|
||||
{
|
||||
"name": "evolution_url",
|
||||
"value": "EVOLUTION_URL"
|
||||
},
|
||||
{
|
||||
"name": "global_api_key",
|
||||
"value": "EVOLUTION_GLOBAL_API_KEY"
|
||||
},
|
||||
{
|
||||
"name": "organization",
|
||||
"value": "={{ $json.query.organization }}"
|
||||
},
|
||||
{
|
||||
"name": "instance_name",
|
||||
"value": "={{ $json.body.messages[0].content.split(':')[1] }}-cwId-{{ $json.body.messages[0].account_id }}"
|
||||
},
|
||||
{
|
||||
"name": "chatwoot_token",
|
||||
"value": "={{ $json.query.utoken }}"
|
||||
},
|
||||
{
|
||||
"name": "msg_call",
|
||||
"value": "Não aceitamos chamadas, por favor deixe uma mensagem!"
|
||||
}
|
||||
],
|
||||
"boolean": [
|
||||
{
|
||||
"name": "qrcode",
|
||||
"value": true
|
||||
},
|
||||
{
|
||||
"name": "chatwoot_sign_msg",
|
||||
"value": true
|
||||
},
|
||||
{
|
||||
"name": "chatwoot_reopen_conversation",
|
||||
"value": true
|
||||
},
|
||||
{
|
||||
"name": "chatwoot_conversation_pending"
|
||||
},
|
||||
{
|
||||
"name": "reject_call",
|
||||
"value": true
|
||||
},
|
||||
{
|
||||
"name": "groups_ignore"
|
||||
},
|
||||
{
|
||||
"name": "always_online",
|
||||
"value": true
|
||||
},
|
||||
{
|
||||
"name": "read_messages",
|
||||
"value": true
|
||||
},
|
||||
{
|
||||
"name": "read_status"
|
||||
}
|
||||
],
|
||||
"number": [
|
||||
{
|
||||
"name": "chatwoot_account_id",
|
||||
"value": "={{ $json.body.messages[0].account_id }}"
|
||||
}
|
||||
]
|
||||
},
|
||||
"options": {}
|
||||
},
|
||||
"id": "297df325-ecc4-4a34-817c-092d16d5753b",
|
||||
"name": "Info Base",
|
||||
"type": "n8n-nodes-base.set",
|
||||
"typeVersion": 2,
|
||||
"position": [
|
||||
540,
|
||||
300
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"conditions": {
|
||||
"string": [
|
||||
{
|
||||
"value1": "={{ $json.name }}",
|
||||
"value2": "=Start {{ $('Info Base').item.json[\"organization\"] }}"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"id": "a8d955e6-ac51-4316-aeec-09d4d65e943a",
|
||||
"name": "é Start Inbox?",
|
||||
"type": "n8n-nodes-base.if",
|
||||
"typeVersion": 1,
|
||||
"position": [
|
||||
1660,
|
||||
200
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"batchSize": 1,
|
||||
"options": {}
|
||||
},
|
||||
"id": "0d2d2194-aa4a-4241-9022-217d88bb581f",
|
||||
"name": "Split In Batches",
|
||||
"type": "n8n-nodes-base.splitInBatches",
|
||||
"typeVersion": 2,
|
||||
"position": [
|
||||
1420,
|
||||
300
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"conditions": {
|
||||
"string": [
|
||||
{
|
||||
"value1": "={{ $json.name }}",
|
||||
"value2": "={{ $('Webhook').item.json[\"body\"][\"messages\"][0][\"content\"].split(':')[1] }}"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"id": "0bfbc2cb-eff5-423c-bd3a-b266aaf6a943",
|
||||
"name": "é_pre-existente?",
|
||||
"type": "n8n-nodes-base.if",
|
||||
"typeVersion": 1,
|
||||
"position": [
|
||||
1900,
|
||||
340
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"method": "PATCH",
|
||||
"url": "={{ $('Info Base').item.json[\"chatwoot_url\"] }}/api/v1/accounts/{{ $('Info Base').item.json[\"chatwoot_account_id\"] }}/inboxes/{{ $json.id }}",
|
||||
"sendHeaders": true,
|
||||
"headerParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "api_access_token",
|
||||
"value": "={{ $('Info Base').item.json.chatwoot_token }}"
|
||||
},
|
||||
{
|
||||
"name": "Content-Type",
|
||||
"value": "application/json"
|
||||
}
|
||||
]
|
||||
},
|
||||
"sendBody": true,
|
||||
"specifyBody": "json",
|
||||
"jsonBody": "={\n\"channel\": {\n\"webhook_url\": \"{{ $('Info Base').item.json[\"evolution_url\"] }}/chatwoot/webhook/{{ encodeURIComponent($('Info Base').item.json[\"instance_name\"]) }}\"\n}\n}",
|
||||
"options": {}
|
||||
},
|
||||
"id": "fb589456-5566-4a45-96a7-75986d0aa1d5",
|
||||
"name": "Update_webhook_url",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"typeVersion": 3,
|
||||
"position": [
|
||||
2120,
|
||||
340
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"method": "DELETE",
|
||||
"url": "={{ $('Info Base').item.json[\"chatwoot_url\"] }}/api/v1/accounts/{{ $('Info Base').item.json[\"chatwoot_account_id\"] }}/inboxes/{{ $json.id }}",
|
||||
"sendHeaders": true,
|
||||
"headerParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "api_access_token",
|
||||
"value": "={{ $('Info Base').item.json.chatwoot_token }}"
|
||||
}
|
||||
]
|
||||
},
|
||||
"options": {}
|
||||
},
|
||||
"id": "e6094941-410f-496c-9c9c-7b95fd9349af",
|
||||
"name": "Deleta Inbox Start",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"typeVersion": 3,
|
||||
"position": [
|
||||
1900,
|
||||
100
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "8cf9a78f-9e8a-4288-9d7b-801790af68d5",
|
||||
"name": "No Operation, do nothing",
|
||||
"type": "n8n-nodes-base.noOp",
|
||||
"typeVersion": 1,
|
||||
"position": [
|
||||
1660,
|
||||
400
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"fieldToSplitOut": "payload",
|
||||
"options": {}
|
||||
},
|
||||
"id": "9468896a-5f86-4598-9d20-e8f495cae859",
|
||||
"name": "Ajusta lista",
|
||||
"type": "n8n-nodes-base.itemLists",
|
||||
"typeVersion": 2.2,
|
||||
"position": [
|
||||
1200,
|
||||
300
|
||||
]
|
||||
}
|
||||
],
|
||||
"pinData": {},
|
||||
"connections": {
|
||||
"Webhook": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Info Base",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Lista Inboxes": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Ajusta lista",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Cria Instancia": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Lista Inboxes",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Info Base": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Cria Instancia",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"é Start Inbox?": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Deleta Inbox Start",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"node": "é_pre-existente?",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Split In Batches": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "é Start Inbox?",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"node": "No Operation, do nothing",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"é_pre-existente?": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Update_webhook_url",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"node": "Split In Batches",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Update_webhook_url": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Split In Batches",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Deleta Inbox Start": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Split In Batches",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Ajusta lista": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Split In Batches",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"active": true,
|
||||
"settings": {},
|
||||
"versionId": "ab910349-b559-4738-9ac6-de6b06d6bbce",
|
||||
"id": "ByW2ccjR4XPrOyio",
|
||||
"meta": {
|
||||
"instanceId": "4ff16e963c7f5197d7e99e6239192860914312fea0ce2a9a7fd14d74a0a0e906"
|
||||
},
|
||||
"tags": []
|
||||
}
|
||||
1
Extras/typebot/typebot-example.json
Normal file
1
Extras/typebot/typebot-example.json
Normal file
File diff suppressed because one or more lines are too long
14
README.md
14
README.md
@@ -7,7 +7,8 @@
|
||||
[](https://evolution-api.com/postman)
|
||||
[](https://doc.evolution-api.com)
|
||||
[](./LICENSE)
|
||||
[](https://app.picpay.com/user/davidsongomes1998)
|
||||
[](https://app.picpay.com/user/davidsongomes1998)
|
||||
[](https://bmc.link/evolutionapi)
|
||||
|
||||
</div>
|
||||
|
||||
@@ -34,8 +35,17 @@ This code was produced based on the baileys library and it is still under develo
|
||||
|
||||
<div align="center">
|
||||
<a href="https://app.picpay.com/user/davidsongomes1998" target="_blank" rel="noopener noreferrer">
|
||||
<img src="./public/images/picpay-image.png" style="width: 50% !important;">
|
||||
<img src="./public/images/picpay-qr.jpeg" style="width: 50% !important;">
|
||||
</a>
|
||||
</div>
|
||||
|
||||
#### Buy me coffe - PIX
|
||||
|
||||
<div align="center">
|
||||
<a href="https://bmc.link/evolutionapi" target="_blank" rel="noopener noreferrer">
|
||||
<img src="./public/images/qrcode-pix.png" style="width: 50% !important;">
|
||||
</a>
|
||||
<p><b>CHAVE PIX (Telefone):</b> (74)99987-9409</p>
|
||||
</div>
|
||||
|
||||
</br>
|
||||
@@ -4,12 +4,15 @@ services:
|
||||
api:
|
||||
container_name: evolution_api
|
||||
image: evolution/api:local
|
||||
build: .
|
||||
restart: always
|
||||
ports:
|
||||
- 8080:8080
|
||||
volumes:
|
||||
- evolution_instances:/evolution/instances
|
||||
- evolution_store:/evolution/store
|
||||
networks:
|
||||
- evolution-net
|
||||
env_file:
|
||||
- ./Docker/.env
|
||||
command: ['node', './dist/src/main.js']
|
||||
@@ -21,6 +24,6 @@ volumes:
|
||||
evolution_store:
|
||||
|
||||
networks:
|
||||
default:
|
||||
evolution-net:
|
||||
name: evolution-net
|
||||
|
||||
driver: bridge
|
||||
@@ -1,36 +1,47 @@
|
||||
version: '3.3'
|
||||
|
||||
services:
|
||||
redis:
|
||||
image: redis:latest
|
||||
container_name: redis
|
||||
api:
|
||||
container_name: evolution_api
|
||||
image: evolution/api:local
|
||||
build: .
|
||||
restart: always
|
||||
ports:
|
||||
- 6379:6379
|
||||
|
||||
rebrow:
|
||||
image: marian/rebrow
|
||||
ports:
|
||||
- 5001:5001
|
||||
links:
|
||||
- redis
|
||||
- 8080:8080
|
||||
volumes:
|
||||
- evolution_instances:/evolution/instances
|
||||
- evolution_store:/evolution/store
|
||||
networks:
|
||||
- evolution-net
|
||||
env_file:
|
||||
- ./Docker/.env
|
||||
command: ['node', './dist/src/main.js']
|
||||
expose:
|
||||
- 8080
|
||||
|
||||
mongodb:
|
||||
container_name: mongodb
|
||||
image: mongo
|
||||
restart: always
|
||||
volumes:
|
||||
- evolution_mongodb_data:/data/db
|
||||
- evolution_mongodb_configdb:/data/configdb
|
||||
ports:
|
||||
- 27017:27017
|
||||
environment:
|
||||
MONGO_INITDB_ROOT_USERNAME: root
|
||||
MONGO_INITDB_ROOT_PASSWORD: root
|
||||
- MONGO_INITDB_ROOT_USERNAME=root
|
||||
- MONGO_INITDB_ROOT_PASSWORD=root
|
||||
- PUID=1000
|
||||
- PGID=1000
|
||||
volumes:
|
||||
- evolution_mongodb_data:/data/db
|
||||
- evolution_mongodb_configdb:/data/configdb
|
||||
networks:
|
||||
- evolution-net
|
||||
expose:
|
||||
- 27017
|
||||
|
||||
mongo-express:
|
||||
image: mongo-express
|
||||
networks:
|
||||
- evolution-net
|
||||
environment:
|
||||
ME_CONFIG_BASICAUTH_USERNAME: root
|
||||
ME_CONFIG_BASICAUTH_PASSWORD: root
|
||||
@@ -41,23 +52,20 @@ services:
|
||||
- 8081:8081
|
||||
links:
|
||||
- mongodb
|
||||
api:
|
||||
container_name: evolution_api
|
||||
image: evolution/api:local
|
||||
restart: always
|
||||
ports:
|
||||
- 8080:8080
|
||||
|
||||
redis:
|
||||
image: redis:latest
|
||||
container_name: redis
|
||||
command: >
|
||||
redis-server
|
||||
--port 6379
|
||||
--appendonly yes
|
||||
volumes:
|
||||
- evolution_instances:/evolution/instances
|
||||
- evolution_store:/evolution/store
|
||||
env_file:
|
||||
- ./Docker/.env
|
||||
command: ['node', './dist/src/main.js']
|
||||
expose:
|
||||
- 8080
|
||||
links:
|
||||
- mongodb
|
||||
- redis
|
||||
- evolution_redis:/data
|
||||
networks:
|
||||
- evolution-net
|
||||
ports:
|
||||
- 6379:6379
|
||||
|
||||
volumes:
|
||||
evolution_instances:
|
||||
@@ -67,6 +75,6 @@ volumes:
|
||||
evolution_redis:
|
||||
|
||||
networks:
|
||||
default:
|
||||
evolution-net:
|
||||
name: evolution-net
|
||||
|
||||
driver: bridge
|
||||
28
docker-compose.yaml.example.dockerhub
Normal file
28
docker-compose.yaml.example.dockerhub
Normal file
@@ -0,0 +1,28 @@
|
||||
version: '3.3'
|
||||
|
||||
services:
|
||||
api:
|
||||
container_name: evolution_api
|
||||
image: atendai/evolution-api:latest
|
||||
restart: always
|
||||
ports:
|
||||
- 8080:8080
|
||||
volumes:
|
||||
- evolution_instances:/evolution/instances
|
||||
- evolution_store:/evolution/store
|
||||
networks:
|
||||
- evolution-net
|
||||
env_file:
|
||||
- ./Docker/.env
|
||||
command: ['node', './dist/src/main.js']
|
||||
expose:
|
||||
- 8080
|
||||
|
||||
volumes:
|
||||
evolution_instances:
|
||||
evolution_store:
|
||||
|
||||
networks:
|
||||
evolution-net:
|
||||
name: evolution-net
|
||||
driver: bridge
|
||||
13
docker.sh
13
docker.sh
@@ -1,13 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
NET='evolution-net'
|
||||
IMAGE='evolution/api:local'
|
||||
|
||||
if !(docker network ls | grep ${NET} > /dev/null)
|
||||
then
|
||||
docker network create -d bridge ${NET}
|
||||
fi
|
||||
|
||||
docker build -t ${IMAGE} .
|
||||
|
||||
docker compose up -d
|
||||
48
package.json
48
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "evolution-api",
|
||||
"version": "1.2.1",
|
||||
"version": "1.7.1",
|
||||
"description": "Rest api for communication with WhatsApp",
|
||||
"main": "./dist/src/main.js",
|
||||
"scripts": {
|
||||
@@ -8,7 +8,8 @@
|
||||
"start": "ts-node --files --transpile-only ./src/main.ts",
|
||||
"start:prod": "bash start.sh",
|
||||
"dev:server": "clear && tsnd --files --transpile-only --respawn --ignore-watch node_modules ./src/main.ts",
|
||||
"test": "clear && tsnd --files --transpile-only --respawn --ignore-watch node_modules ./test/all.test.ts"
|
||||
"test": "clear && tsnd --files --transpile-only --respawn --ignore-watch node_modules ./test/all.test.ts",
|
||||
"lint": "eslint --fix --ext .ts src"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -42,36 +43,51 @@
|
||||
"dependencies": {
|
||||
"@adiwajshing/keyed-db": "^0.2.4",
|
||||
"@ffmpeg-installer/ffmpeg": "^1.1.0",
|
||||
"@figuro/chatwoot-sdk": "^1.1.16",
|
||||
"@hapi/boom": "^10.0.1",
|
||||
"@whiskeysockets/baileys": "github:vphelipe/WhiskeySockets-Baileys#master",
|
||||
"@figuro/chatwoot-sdk": "^1.1.14",
|
||||
"axios": "^1.3.5",
|
||||
"class-validator": "^0.13.2",
|
||||
"@sentry/node": "^7.59.2",
|
||||
"@whiskeysockets/baileys": "6.6.0",
|
||||
"amqplib": "^0.10.3",
|
||||
"aws-sdk": "^2.1499.0",
|
||||
"axios": "^1.6.5",
|
||||
"class-validator": "^0.14.1",
|
||||
"compression": "^1.7.4",
|
||||
"cors": "^2.8.5",
|
||||
"cross-env": "^7.0.3",
|
||||
"dayjs": "^1.11.7",
|
||||
"eventemitter2": "^6.4.9",
|
||||
"evolution-manager": "^0.4.13",
|
||||
"exiftool-vendored": "^22.0.0",
|
||||
"express": "^4.18.2",
|
||||
"express-async-errors": "^3.1.1",
|
||||
"form-data": "^4.0.0",
|
||||
"hbs": "^4.2.0",
|
||||
"https-proxy-agent": "^7.0.2",
|
||||
"i18next": "^23.7.19",
|
||||
"jimp": "^0.16.13",
|
||||
"join": "^3.0.0",
|
||||
"js-yaml": "^4.1.0",
|
||||
"jsonschema": "^1.4.1",
|
||||
"jsonwebtoken": "^8.5.1",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
"libphonenumber-js": "^1.10.39",
|
||||
"link-preview-js": "^3.0.4",
|
||||
"mongoose": "^6.10.5",
|
||||
"node-cache": "^5.1.2",
|
||||
"node-mime-types": "^1.1.0",
|
||||
"node-windows": "^1.0.0-beta.8",
|
||||
"parse-bmfont-xml": "^1.1.4",
|
||||
"pg": "^8.11.3",
|
||||
"pino": "^8.11.0",
|
||||
"proxy-agent": "^6.2.1",
|
||||
"qrcode": "^1.5.1",
|
||||
"qrcode-terminal": "^0.12.0",
|
||||
"redis": "^4.6.5",
|
||||
"sharp": "^0.30.7",
|
||||
"uuid": "^9.0.0"
|
||||
"sharp": "^0.32.2",
|
||||
"socket.io": "^4.7.1",
|
||||
"socks-proxy-agent": "^8.0.1",
|
||||
"swagger-ui-express": "^5.0.0",
|
||||
"uuid": "^9.0.0",
|
||||
"xml2js": "^0.6.2",
|
||||
"yamljs": "^0.3.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/compression": "^1.7.2",
|
||||
@@ -79,16 +95,20 @@
|
||||
"@types/express": "^4.17.17",
|
||||
"@types/js-yaml": "^4.0.5",
|
||||
"@types/jsonwebtoken": "^8.5.9",
|
||||
"@types/mime-types": "^2.1.1",
|
||||
"@types/node": "^18.15.11",
|
||||
"@types/node-windows": "^0.1.2",
|
||||
"@types/qrcode": "^1.5.0",
|
||||
"@types/qrcode-terminal": "^0.12.0",
|
||||
"@types/uuid": "^8.3.4",
|
||||
"@typescript-eslint/eslint-plugin": "^5.57.1",
|
||||
"@typescript-eslint/parser": "^5.57.1",
|
||||
"eslint": "^8.38.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.62.0",
|
||||
"@typescript-eslint/parser": "^5.62.0",
|
||||
"eslint": "^8.45.0",
|
||||
"eslint-config-prettier": "^8.8.0",
|
||||
"eslint-plugin-import": "^2.27.5",
|
||||
"eslint-plugin-prettier": "^4.2.1",
|
||||
"prettier": "^2.8.7",
|
||||
"eslint-plugin-simple-import-sort": "^10.0.0",
|
||||
"prettier": "^2.8.8",
|
||||
"ts-node-dev": "^2.0.0",
|
||||
"typescript": "^4.9.5"
|
||||
}
|
||||
|
||||
BIN
public/images/bmc_qr.png
Normal file
BIN
public/images/bmc_qr.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 52 KiB |
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
BIN
public/images/picpay-qr.jpeg
Normal file
BIN
public/images/picpay-qr.jpeg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 111 KiB |
BIN
public/images/qrcode-pix.png
Normal file
BIN
public/images/qrcode-pix.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.6 KiB |
@@ -1,10 +1,15 @@
|
||||
import { isBooleanString } from 'class-validator';
|
||||
import { readFileSync } from 'fs';
|
||||
import { load } from 'js-yaml';
|
||||
import { join } from 'path';
|
||||
import { SRC_DIR } from './path.config';
|
||||
import { isBooleanString } from 'class-validator';
|
||||
|
||||
export type HttpServer = { TYPE: 'http' | 'https'; PORT: number; URL: string };
|
||||
export type HttpServer = {
|
||||
TYPE: 'http' | 'https';
|
||||
PORT: number;
|
||||
URL: string;
|
||||
DISABLE_DOCS: boolean;
|
||||
DISABLE_MANAGER: boolean;
|
||||
};
|
||||
|
||||
export type HttpMethods = 'POST' | 'GET' | 'PUT' | 'DELETE';
|
||||
export type Cors = {
|
||||
@@ -15,15 +20,7 @@ export type Cors = {
|
||||
|
||||
export type LogBaileys = 'fatal' | 'error' | 'warn' | 'info' | 'debug' | 'trace';
|
||||
|
||||
export type LogLevel =
|
||||
| 'ERROR'
|
||||
| 'WARN'
|
||||
| 'DEBUG'
|
||||
| 'INFO'
|
||||
| 'LOG'
|
||||
| 'VERBOSE'
|
||||
| 'DARK'
|
||||
| 'WEBHOOKS';
|
||||
export type LogLevel = 'ERROR' | 'WARN' | 'DEBUG' | 'INFO' | 'LOG' | 'VERBOSE' | 'DARK' | 'WEBHOOKS';
|
||||
|
||||
export type Log = {
|
||||
LEVEL: LogLevel[];
|
||||
@@ -37,6 +34,7 @@ export type SaveData = {
|
||||
MESSAGE_UPDATE: boolean;
|
||||
CONTACTS: boolean;
|
||||
CHATS: boolean;
|
||||
LABELS: boolean;
|
||||
};
|
||||
|
||||
export type StoreConf = {
|
||||
@@ -44,6 +42,7 @@ export type StoreConf = {
|
||||
MESSAGE_UP: boolean;
|
||||
CONTACTS: boolean;
|
||||
CHATS: boolean;
|
||||
LABELS: boolean;
|
||||
};
|
||||
|
||||
export type CleanStoreConf = {
|
||||
@@ -70,12 +69,41 @@ export type Redis = {
|
||||
PREFIX_KEY: string;
|
||||
};
|
||||
|
||||
export type Rabbitmq = {
|
||||
ENABLED: boolean;
|
||||
MODE: string; // global, single, isolated
|
||||
EXCHANGE_NAME: string; // available for global and single, isolated mode will use instance name as exchange
|
||||
URI: string;
|
||||
};
|
||||
|
||||
export type Sqs = {
|
||||
ENABLED: boolean;
|
||||
ACCESS_KEY_ID: string;
|
||||
SECRET_ACCESS_KEY: string;
|
||||
ACCOUNT_ID: string;
|
||||
REGION: string;
|
||||
};
|
||||
|
||||
export type Websocket = {
|
||||
ENABLED: boolean;
|
||||
};
|
||||
|
||||
export type WaBusiness = {
|
||||
TOKEN_WEBHOOK: string;
|
||||
URL: string;
|
||||
VERSION: string;
|
||||
LANGUAGE: string;
|
||||
};
|
||||
|
||||
export type EventsWebhook = {
|
||||
APPLICATION_STARTUP: boolean;
|
||||
INSTANCE_CREATE: boolean;
|
||||
INSTANCE_DELETE: boolean;
|
||||
QRCODE_UPDATED: boolean;
|
||||
MESSAGES_SET: boolean;
|
||||
MESSAGES_UPSERT: boolean;
|
||||
MESSAGES_UPDATE: boolean;
|
||||
MESSAGES_DELETE: boolean;
|
||||
SEND_MESSAGE: boolean;
|
||||
CONTACTS_SET: boolean;
|
||||
CONTACTS_UPDATE: boolean;
|
||||
@@ -86,41 +114,68 @@ export type EventsWebhook = {
|
||||
CHATS_DELETE: boolean;
|
||||
CHATS_UPSERT: boolean;
|
||||
CONNECTION_UPDATE: boolean;
|
||||
LABELS_EDIT: boolean;
|
||||
LABELS_ASSOCIATION: boolean;
|
||||
GROUPS_UPSERT: boolean;
|
||||
GROUP_UPDATE: boolean;
|
||||
GROUP_PARTICIPANTS_UPDATE: boolean;
|
||||
CALL: boolean;
|
||||
NEW_JWT_TOKEN: boolean;
|
||||
TYPEBOT_START: boolean;
|
||||
TYPEBOT_CHANGE_STATUS: boolean;
|
||||
CHAMA_AI_ACTION: boolean;
|
||||
ERRORS: boolean;
|
||||
ERRORS_WEBHOOK: string;
|
||||
};
|
||||
|
||||
export type ApiKey = { KEY: string };
|
||||
export type Jwt = { EXPIRIN_IN: number; SECRET: string };
|
||||
export type Instance = {
|
||||
NAME: string;
|
||||
WEBHOOK_URL: string;
|
||||
MODE: string;
|
||||
CHATWOOT_ACCOUNT_ID?: string;
|
||||
CHATWOOT_TOKEN?: string;
|
||||
CHATWOOT_URL?: string;
|
||||
};
|
||||
|
||||
export type Auth = {
|
||||
API_KEY: ApiKey;
|
||||
EXPOSE_IN_FETCH_INSTANCES: boolean;
|
||||
JWT: Jwt;
|
||||
TYPE: 'jwt' | 'apikey';
|
||||
INSTANCE: Instance;
|
||||
};
|
||||
|
||||
export type DelInstance = number | boolean;
|
||||
|
||||
export type Language = string | 'en';
|
||||
|
||||
export type GlobalWebhook = {
|
||||
URL: string;
|
||||
ENABLED: boolean;
|
||||
WEBHOOK_BY_EVENTS: boolean;
|
||||
};
|
||||
export type CacheConfRedis = {
|
||||
ENABLED: boolean;
|
||||
URI: string;
|
||||
PREFIX_KEY: string;
|
||||
TTL: number;
|
||||
};
|
||||
export type CacheConfLocal = {
|
||||
ENABLED: boolean;
|
||||
TTL: number;
|
||||
};
|
||||
export type SslConf = { PRIVKEY: string; FULLCHAIN: string };
|
||||
export type Webhook = { GLOBAL?: GlobalWebhook; EVENTS: EventsWebhook };
|
||||
export type ConfigSessionPhone = { CLIENT: string; NAME: string };
|
||||
export type QrCode = { LIMIT: number };
|
||||
export type QrCode = { LIMIT: number; COLOR: string };
|
||||
export type Typebot = { API_VERSION: string; KEEP_OPEN: boolean };
|
||||
export type Chatwoot = {
|
||||
MESSAGE_DELETE: boolean;
|
||||
MESSAGE_READ: boolean;
|
||||
IMPORT: {
|
||||
DATABASE: {
|
||||
CONNECTION: {
|
||||
URI: string;
|
||||
};
|
||||
};
|
||||
PLACEHOLDER_MEDIA_MESSAGE: boolean;
|
||||
};
|
||||
};
|
||||
|
||||
export type CacheConf = { REDIS: CacheConfRedis; LOCAL: CacheConfLocal };
|
||||
export type Production = boolean;
|
||||
|
||||
export interface Env {
|
||||
@@ -131,11 +186,20 @@ export interface Env {
|
||||
CLEAN_STORE: CleanStoreConf;
|
||||
DATABASE: Database;
|
||||
REDIS: Redis;
|
||||
RABBITMQ: Rabbitmq;
|
||||
SQS: Sqs;
|
||||
WEBSOCKET: Websocket;
|
||||
WA_BUSINESS: WaBusiness;
|
||||
LOG: Log;
|
||||
DEL_INSTANCE: DelInstance;
|
||||
DEL_TEMP_INSTANCES: boolean;
|
||||
LANGUAGE: Language;
|
||||
WEBHOOK: Webhook;
|
||||
CONFIG_SESSION_PHONE: ConfigSessionPhone;
|
||||
QRCODE: QrCode;
|
||||
TYPEBOT: Typebot;
|
||||
CHATWOOT: Chatwoot;
|
||||
CACHE: CacheConf;
|
||||
AUTHENTICATION: Auth;
|
||||
PRODUCTION?: Production;
|
||||
}
|
||||
@@ -157,43 +221,44 @@ export class ConfigService {
|
||||
this.env = !(process.env?.DOCKER_ENV === 'true') ? this.envYaml() : this.envProcess();
|
||||
this.env.PRODUCTION = process.env?.NODE_ENV === 'PROD';
|
||||
if (process.env?.DOCKER_ENV === 'true') {
|
||||
this.env.SERVER.TYPE = 'http';
|
||||
this.env.SERVER.PORT = 8080;
|
||||
this.env.SERVER.TYPE = process.env.SERVER_TYPE as 'http' | 'http';
|
||||
this.env.SERVER.PORT = Number.parseInt(process.env.SERVER_PORT) || 8080;
|
||||
}
|
||||
}
|
||||
|
||||
private envYaml(): Env {
|
||||
return load(
|
||||
readFileSync(join(process.cwd(), 'src', 'env.yml'), { encoding: 'utf-8' }),
|
||||
) as Env;
|
||||
return load(readFileSync(join(process.cwd(), 'src', 'env.yml'), { encoding: 'utf-8' })) as Env;
|
||||
}
|
||||
|
||||
private envProcess(): Env {
|
||||
return {
|
||||
SERVER: {
|
||||
TYPE: process.env.SERVER_TYPE as 'http' | 'https',
|
||||
PORT: Number.parseInt(process.env.SERVER_PORT),
|
||||
TYPE: (process.env.SERVER_TYPE as 'http' | 'https') || 'http',
|
||||
PORT: Number.parseInt(process.env.SERVER_PORT) || 8080,
|
||||
URL: process.env.SERVER_URL,
|
||||
DISABLE_DOCS: process.env?.SERVER_DISABLE_DOCS === 'true',
|
||||
DISABLE_MANAGER: process.env?.SERVER_DISABLE_MANAGER === 'true',
|
||||
},
|
||||
CORS: {
|
||||
ORIGIN: process.env.CORS_ORIGIN.split(','),
|
||||
METHODS: process.env.CORS_METHODS.split(',') as HttpMethods[],
|
||||
ORIGIN: process.env.CORS_ORIGIN.split(',') || ['*'],
|
||||
METHODS: (process.env.CORS_METHODS.split(',') as HttpMethods[]) || ['POST', 'GET', 'PUT', 'DELETE'],
|
||||
CREDENTIALS: process.env?.CORS_CREDENTIALS === 'true',
|
||||
},
|
||||
SSL_CONF: {
|
||||
PRIVKEY: process.env?.SSL_CONF_PRIVKEY,
|
||||
FULLCHAIN: process.env?.SSL_CONF_FULLCHAIN,
|
||||
PRIVKEY: process.env?.SSL_CONF_PRIVKEY || '',
|
||||
FULLCHAIN: process.env?.SSL_CONF_FULLCHAIN || '',
|
||||
},
|
||||
STORE: {
|
||||
MESSAGES: process.env?.STORE_MESSAGES === 'true',
|
||||
MESSAGE_UP: process.env?.STORE_MESSAGE_UP === 'true',
|
||||
CONTACTS: process.env?.STORE_CONTACTS === 'true',
|
||||
CHATS: process.env?.STORE_CHATS === 'true',
|
||||
LABELS: process.env?.STORE_LABELS === 'true',
|
||||
},
|
||||
CLEAN_STORE: {
|
||||
CLEANING_INTERVAL: Number.isInteger(process.env?.CLEAN_STORE_CLEANING_TERMINAL)
|
||||
? Number.parseInt(process.env.CLEAN_STORE_CLEANING_TERMINAL)
|
||||
: undefined,
|
||||
: 7200,
|
||||
MESSAGES: process.env?.CLEAN_STORE_MESSAGES === 'true',
|
||||
MESSAGE_UP: process.env?.CLEAN_STORE_MESSAGE_UP === 'true',
|
||||
CONTACTS: process.env?.CLEAN_STORE_CONTACTS === 'true',
|
||||
@@ -201,8 +266,8 @@ export class ConfigService {
|
||||
},
|
||||
DATABASE: {
|
||||
CONNECTION: {
|
||||
URI: process.env.DATABASE_CONNECTION_URI,
|
||||
DB_PREFIX_NAME: process.env.DATABASE_CONNECTION_DB_PREFIX_NAME,
|
||||
URI: process.env.DATABASE_CONNECTION_URI || '',
|
||||
DB_PREFIX_NAME: process.env.DATABASE_CONNECTION_DB_PREFIX_NAME || 'evolution',
|
||||
},
|
||||
ENABLED: process.env?.DATABASE_ENABLED === 'true',
|
||||
SAVE_DATA: {
|
||||
@@ -211,33 +276,72 @@ export class ConfigService {
|
||||
MESSAGE_UPDATE: process.env?.DATABASE_SAVE_MESSAGE_UPDATE === 'true',
|
||||
CONTACTS: process.env?.DATABASE_SAVE_DATA_CONTACTS === 'true',
|
||||
CHATS: process.env?.DATABASE_SAVE_DATA_CHATS === 'true',
|
||||
LABELS: process.env?.DATABASE_SAVE_DATA_LABELS === 'true',
|
||||
},
|
||||
},
|
||||
REDIS: {
|
||||
ENABLED: process.env?.REDIS_ENABLED === 'true',
|
||||
URI: process.env.REDIS_URI,
|
||||
PREFIX_KEY: process.env.REDIS_PREFIX_KEY,
|
||||
URI: process.env.REDIS_URI || '',
|
||||
PREFIX_KEY: process.env.REDIS_PREFIX_KEY || 'evolution',
|
||||
},
|
||||
RABBITMQ: {
|
||||
ENABLED: process.env?.RABBITMQ_ENABLED === 'true',
|
||||
MODE: process.env?.RABBITMQ_MODE || 'isolated',
|
||||
EXCHANGE_NAME: process.env?.RABBITMQ_EXCHANGE_NAME || 'evolution_exchange',
|
||||
URI: process.env.RABBITMQ_URI || '',
|
||||
},
|
||||
SQS: {
|
||||
ENABLED: process.env?.SQS_ENABLED === 'true',
|
||||
ACCESS_KEY_ID: process.env.SQS_ACCESS_KEY_ID || '',
|
||||
SECRET_ACCESS_KEY: process.env.SQS_SECRET_ACCESS_KEY || '',
|
||||
ACCOUNT_ID: process.env.SQS_ACCOUNT_ID || '',
|
||||
REGION: process.env.SQS_REGION || '',
|
||||
},
|
||||
WEBSOCKET: {
|
||||
ENABLED: process.env?.WEBSOCKET_ENABLED === 'true',
|
||||
},
|
||||
WA_BUSINESS: {
|
||||
TOKEN_WEBHOOK: process.env.WA_BUSINESS_TOKEN_WEBHOOK || '',
|
||||
URL: process.env.WA_BUSINESS_URL || '',
|
||||
VERSION: process.env.WA_BUSINESS_VERSION || '',
|
||||
LANGUAGE: process.env.WA_BUSINESS_LANGUAGE || 'en',
|
||||
},
|
||||
LOG: {
|
||||
LEVEL: process.env?.LOG_LEVEL.split(',') as LogLevel[],
|
||||
LEVEL: (process.env?.LOG_LEVEL.split(',') as LogLevel[]) || [
|
||||
'ERROR',
|
||||
'WARN',
|
||||
'DEBUG',
|
||||
'INFO',
|
||||
'LOG',
|
||||
'VERBOSE',
|
||||
'DARK',
|
||||
'WEBHOOKS',
|
||||
],
|
||||
COLOR: process.env?.LOG_COLOR === 'true',
|
||||
BAILEYS: (process.env?.LOG_BAILEYS as LogBaileys) || 'error',
|
||||
},
|
||||
DEL_INSTANCE: isBooleanString(process.env?.DEL_INSTANCE)
|
||||
? process.env.DEL_INSTANCE === 'true'
|
||||
: Number.parseInt(process.env.DEL_INSTANCE),
|
||||
: Number.parseInt(process.env.DEL_INSTANCE) || false,
|
||||
DEL_TEMP_INSTANCES: isBooleanString(process.env?.DEL_TEMP_INSTANCES)
|
||||
? process.env.DEL_TEMP_INSTANCES === 'true'
|
||||
: true,
|
||||
LANGUAGE: process.env?.LANGUAGE || 'en',
|
||||
WEBHOOK: {
|
||||
GLOBAL: {
|
||||
URL: process.env?.WEBHOOK_GLOBAL_URL,
|
||||
URL: process.env?.WEBHOOK_GLOBAL_URL || '',
|
||||
ENABLED: process.env?.WEBHOOK_GLOBAL_ENABLED === 'true',
|
||||
WEBHOOK_BY_EVENTS: process.env?.WEBHOOK_GLOBAL_WEBHOOK_BY_EVENTS === 'true',
|
||||
},
|
||||
EVENTS: {
|
||||
APPLICATION_STARTUP: process.env?.WEBHOOK_EVENTS_APPLICATION_STARTUP === 'true',
|
||||
INSTANCE_CREATE: process.env?.WEBHOOK_EVENTS_INSTANCE_CREATE === 'true',
|
||||
INSTANCE_DELETE: process.env?.WEBHOOK_EVENTS_INSTANCE_DELETE === 'true',
|
||||
QRCODE_UPDATED: process.env?.WEBHOOK_EVENTS_QRCODE_UPDATED === 'true',
|
||||
MESSAGES_SET: process.env?.WEBHOOK_EVENTS_MESSAGES_SET === 'true',
|
||||
MESSAGES_UPSERT: process.env?.WEBHOOK_EVENTS_MESSAGES_UPSERT === 'true',
|
||||
MESSAGES_UPDATE: process.env?.WEBHOOK_EVENTS_MESSAGES_UPDATE === 'true',
|
||||
MESSAGES_DELETE: process.env?.WEBHOOK_EVENTS_MESSAGES_DELETE === 'true',
|
||||
SEND_MESSAGE: process.env?.WEBHOOK_EVENTS_SEND_MESSAGE === 'true',
|
||||
CONTACTS_SET: process.env?.WEBHOOK_EVENTS_CONTACTS_SET === 'true',
|
||||
CONTACTS_UPDATE: process.env?.WEBHOOK_EVENTS_CONTACTS_UPDATE === 'true',
|
||||
@@ -248,41 +352,67 @@ export class ConfigService {
|
||||
CHATS_UPSERT: process.env?.WEBHOOK_EVENTS_CHATS_UPSERT === 'true',
|
||||
CHATS_DELETE: process.env?.WEBHOOK_EVENTS_CHATS_DELETE === 'true',
|
||||
CONNECTION_UPDATE: process.env?.WEBHOOK_EVENTS_CONNECTION_UPDATE === 'true',
|
||||
LABELS_EDIT: process.env?.WEBHOOK_EVENTS_LABELS_EDIT === 'true',
|
||||
LABELS_ASSOCIATION: process.env?.WEBHOOK_EVENTS_LABELS_ASSOCIATION === 'true',
|
||||
GROUPS_UPSERT: process.env?.WEBHOOK_EVENTS_GROUPS_UPSERT === 'true',
|
||||
GROUP_UPDATE: process.env?.WEBHOOK_EVENTS_GROUPS_UPDATE === 'true',
|
||||
GROUP_PARTICIPANTS_UPDATE:
|
||||
process.env?.WEBHOOK_EVENTS_GROUP_PARTICIPANTS_UPDATE === 'true',
|
||||
GROUP_PARTICIPANTS_UPDATE: process.env?.WEBHOOK_EVENTS_GROUP_PARTICIPANTS_UPDATE === 'true',
|
||||
CALL: process.env?.WEBHOOK_EVENTS_CALL === 'true',
|
||||
NEW_JWT_TOKEN: process.env?.WEBHOOK_EVENTS_NEW_JWT_TOKEN === 'true',
|
||||
TYPEBOT_START: process.env?.WEBHOOK_EVENTS_TYPEBOT_START === 'true',
|
||||
TYPEBOT_CHANGE_STATUS: process.env?.WEBHOOK_EVENTS_TYPEBOT_CHANGE_STATUS === 'true',
|
||||
CHAMA_AI_ACTION: process.env?.WEBHOOK_EVENTS_CHAMA_AI_ACTION === 'true',
|
||||
ERRORS: process.env?.WEBHOOK_EVENTS_ERRORS === 'true',
|
||||
ERRORS_WEBHOOK: process.env?.WEBHOOK_EVENTS_ERRORS_WEBHOOK || '',
|
||||
},
|
||||
},
|
||||
CONFIG_SESSION_PHONE: {
|
||||
CLIENT: process.env?.CONFIG_SESSION_PHONE_CLIENT,
|
||||
NAME: process.env?.CONFIG_SESSION_PHONE_NAME,
|
||||
CLIENT: process.env?.CONFIG_SESSION_PHONE_CLIENT || 'Evolution API',
|
||||
NAME: process.env?.CONFIG_SESSION_PHONE_NAME || 'Chrome',
|
||||
},
|
||||
QRCODE: {
|
||||
LIMIT: Number.parseInt(process.env.QRCODE_LIMIT),
|
||||
LIMIT: Number.parseInt(process.env.QRCODE_LIMIT) || 30,
|
||||
COLOR: process.env.QRCODE_COLOR || '#198754',
|
||||
},
|
||||
TYPEBOT: {
|
||||
API_VERSION: process.env?.TYPEBOT_API_VERSION || 'old',
|
||||
KEEP_OPEN: process.env.TYPEBOT_KEEP_OPEN === 'true',
|
||||
},
|
||||
CHATWOOT: {
|
||||
MESSAGE_DELETE: process.env.CHATWOOT_MESSAGE_DELETE === 'false',
|
||||
MESSAGE_READ: process.env.CHATWOOT_MESSAGE_READ === 'false',
|
||||
IMPORT: {
|
||||
DATABASE: {
|
||||
CONNECTION: {
|
||||
URI: process.env.CHATWOOT_IMPORT_DATABASE_CONNECTION_URI || '',
|
||||
},
|
||||
},
|
||||
PLACEHOLDER_MEDIA_MESSAGE: process.env?.CHATWOOT_IMPORT_PLACEHOLDER_MEDIA_MESSAGE === 'true',
|
||||
},
|
||||
},
|
||||
CACHE: {
|
||||
REDIS: {
|
||||
ENABLED: process.env?.CACHE_REDIS_ENABLED === 'true',
|
||||
URI: process.env?.CACHE_REDIS_URI || '',
|
||||
PREFIX_KEY: process.env?.CACHE_REDIS_PREFIX_KEY || 'evolution-cache',
|
||||
TTL: Number.parseInt(process.env?.CACHE_REDIS_TTL) || 604800,
|
||||
},
|
||||
LOCAL: {
|
||||
ENABLED: process.env?.CACHE_LOCAL_ENABLED === 'true',
|
||||
TTL: Number.parseInt(process.env?.CACHE_REDIS_TTL) || 86400,
|
||||
},
|
||||
},
|
||||
AUTHENTICATION: {
|
||||
TYPE: process.env.AUTHENTICATION_TYPE as 'jwt',
|
||||
TYPE: process.env.AUTHENTICATION_TYPE as 'apikey',
|
||||
API_KEY: {
|
||||
KEY: process.env.AUTHENTICATION_API_KEY,
|
||||
KEY: process.env.AUTHENTICATION_API_KEY || 'BQYHJGJHJ',
|
||||
},
|
||||
EXPOSE_IN_FETCH_INSTANCES:
|
||||
process.env?.AUTHENTICATION_EXPOSE_IN_FETCH_INSTANCES === 'true',
|
||||
EXPOSE_IN_FETCH_INSTANCES: process.env?.AUTHENTICATION_EXPOSE_IN_FETCH_INSTANCES === 'true',
|
||||
JWT: {
|
||||
EXPIRIN_IN: Number.isInteger(process.env?.AUTHENTICATION_JWT_EXPIRIN_IN)
|
||||
? Number.parseInt(process.env.AUTHENTICATION_JWT_EXPIRIN_IN)
|
||||
: 3600,
|
||||
SECRET: process.env.AUTHENTICATION_JWT_SECRET,
|
||||
},
|
||||
INSTANCE: {
|
||||
NAME: process.env.AUTHENTICATION_INSTANCE_NAME,
|
||||
WEBHOOK_URL: process.env.AUTHENTICATION_INSTANCE_WEBHOOK_URL,
|
||||
MODE: process.env.AUTHENTICATION_INSTANCE_MODE,
|
||||
CHATWOOT_ACCOUNT_ID:
|
||||
process.env.AUTHENTICATION_INSTANCE_CHATWOOT_ACCOUNT_ID || '',
|
||||
CHATWOOT_TOKEN: process.env.AUTHENTICATION_INSTANCE_CHATWOOT_TOKEN || '',
|
||||
CHATWOOT_URL: process.env.AUTHENTICATION_INSTANCE_CHATWOOT_URL || '',
|
||||
SECRET: process.env.AUTHENTICATION_JWT_SECRET || 'L=0YWt]b2w[WF>#>:&E`',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import { configService, Log } from './env.config';
|
||||
import dayjs from 'dayjs';
|
||||
import fs from 'fs';
|
||||
|
||||
import { configService, Log } from './env.config';
|
||||
const packageJson = JSON.parse(fs.readFileSync('./package.json', 'utf8'));
|
||||
|
||||
const formatDateLog = (timestamp: number) =>
|
||||
dayjs(timestamp)
|
||||
@@ -73,6 +76,8 @@ export class Logger {
|
||||
/*Command.UNDERSCORE +*/ Command.BRIGHT + Level[type],
|
||||
'[Evolution API]',
|
||||
Command.BRIGHT + Color[type],
|
||||
`v${packageJson.version}`,
|
||||
Command.BRIGHT + Color[type],
|
||||
process.pid.toString(),
|
||||
Command.RESET,
|
||||
Command.BRIGHT + Color[type],
|
||||
|
||||
@@ -9,6 +9,8 @@ SERVER:
|
||||
TYPE: http # https
|
||||
PORT: 8080 # 443
|
||||
URL: localhost
|
||||
DISABLE_MANAGER: false
|
||||
DISABLE_DOCS: false
|
||||
|
||||
CORS:
|
||||
ORIGIN:
|
||||
@@ -39,12 +41,13 @@ LOG:
|
||||
- DARK
|
||||
- WEBHOOKS
|
||||
COLOR: true
|
||||
BAILEYS: error # "fatal" | "error" | "warn" | "info" | "debug" | "trace"
|
||||
BAILEYS: error # fatal | error | warn | info | debug | trace
|
||||
|
||||
# Determine how long the instance should be deleted from memory in case of no connection.
|
||||
# Default time: 5 minutes
|
||||
# If you don't even want an expiration, enter the value false
|
||||
DEL_INSTANCE: false # or false
|
||||
DEL_TEMP_INSTANCES: true # Delete instances with status closed on start
|
||||
|
||||
# Temporary data storage
|
||||
STORE:
|
||||
@@ -79,6 +82,28 @@ REDIS:
|
||||
URI: "redis://localhost:6379"
|
||||
PREFIX_KEY: "evolution"
|
||||
|
||||
RABBITMQ:
|
||||
ENABLED: false
|
||||
MODE: "global"
|
||||
EXCHANGE_NAME: "evolution_exchange"
|
||||
URI: "amqp://guest:guest@localhost:5672"
|
||||
|
||||
SQS:
|
||||
ENABLED: true
|
||||
ACCESS_KEY_ID: ""
|
||||
SECRET_ACCESS_KEY: ""
|
||||
ACCOUNT_ID: ""
|
||||
REGION: "us-east-1"
|
||||
|
||||
WEBSOCKET:
|
||||
ENABLED: false
|
||||
|
||||
WA_BUSINESS:
|
||||
TOKEN_WEBHOOK: evolution
|
||||
URL: https://graph.facebook.com
|
||||
VERSION: v18.0
|
||||
LANGUAGE: pt_BR
|
||||
|
||||
# Global Webhook Settings
|
||||
# Each instance's Webhook URL and events will be requested at the time it is created
|
||||
WEBHOOK:
|
||||
@@ -96,6 +121,7 @@ WEBHOOK:
|
||||
MESSAGES_SET: true
|
||||
MESSAGES_UPSERT: true
|
||||
MESSAGES_UPDATE: true
|
||||
MESSAGES_DELETE: true
|
||||
SEND_MESSAGE: true
|
||||
CONTACTS_SET: true
|
||||
CONTACTS_UPSERT: true
|
||||
@@ -109,17 +135,56 @@ WEBHOOK:
|
||||
GROUP_UPDATE: true
|
||||
GROUP_PARTICIPANTS_UPDATE: true
|
||||
CONNECTION_UPDATE: true
|
||||
LABELS_EDIT: true
|
||||
LABELS_ASSOCIATION: true
|
||||
CALL: true
|
||||
# This event fires every time a new token is requested via the refresh route
|
||||
NEW_JWT_TOKEN: false
|
||||
# This events is used with Typebot
|
||||
TYPEBOT_START: false
|
||||
TYPEBOT_CHANGE_STATUS: false
|
||||
# This event is used with Chama AI
|
||||
CHAMA_AI_ACTION: false
|
||||
# This event is used to send errors to the webhook
|
||||
ERRORS: false
|
||||
ERRORS_WEBHOOK: <url>
|
||||
|
||||
CONFIG_SESSION_PHONE:
|
||||
# Name that will be displayed on smartphone connection
|
||||
CLIENT: "Evolution API"
|
||||
NAME: chrome # chrome | firefox | edge | opera | safari
|
||||
NAME: Chrome # Chrome | Firefox | Edge | Opera | Safari
|
||||
|
||||
# Set qrcode display limit
|
||||
QRCODE:
|
||||
LIMIT: 30
|
||||
COLOR: "#198754"
|
||||
|
||||
TYPEBOT:
|
||||
API_VERSION: "old" # old | latest
|
||||
KEEP_OPEN: false
|
||||
|
||||
CHATWOOT:
|
||||
# If you leave this option as false, when deleting the message for everyone on WhatsApp, it will not be deleted on Chatwoot.
|
||||
MESSAGE_DELETE: true # false | true
|
||||
# If you leave this option as true, when sending a message in Chatwoot, the client's last message will be marked as read on WhatsApp.
|
||||
MESSAGE_READ: false # false | true
|
||||
IMPORT:
|
||||
# This db connection is used to import messages from whatsapp to chatwoot database
|
||||
DATABASE:
|
||||
CONNECTION:
|
||||
URI: "postgres://user:password@hostname:port/dbname"
|
||||
PLACEHOLDER_MEDIA_MESSAGE: true
|
||||
|
||||
# Cache to optimize application performance
|
||||
CACHE:
|
||||
REDIS:
|
||||
ENABLED: false
|
||||
URI: "redis://localhost:6379"
|
||||
PREFIX_KEY: "evolution-cache"
|
||||
TTL: 604800
|
||||
LOCAL:
|
||||
ENABLED: false
|
||||
TTL: 86400
|
||||
|
||||
# Defines an authentication type for the api
|
||||
# We recommend using the apikey because it will allow you to use a custom token,
|
||||
@@ -136,13 +201,5 @@ AUTHENTICATION:
|
||||
JWT:
|
||||
EXPIRIN_IN: 0 # seconds - 3600s === 1h | zero (0) - never expires
|
||||
SECRET: L=0YWt]b2w[WF>#>:&E`
|
||||
# Set the instance name and webhook url to create an instance in init the application
|
||||
INSTANCE:
|
||||
# With this option activated, you work with a url per webhook event, respecting the local url and the name of each event
|
||||
MODE: server # container or server
|
||||
# if you are using container mode, set the container name and the webhook url to default instance
|
||||
NAME: evolution
|
||||
WEBHOOK_URL: <url>
|
||||
CHATWOOT_ACCOUNT_ID: 1
|
||||
CHATWOOT_TOKEN: 123456
|
||||
CHATWOOT_URL: <url>
|
||||
|
||||
LANGUAGE: "pt-BR" # pt-BR, en
|
||||
|
||||
17
src/docs/swagger.conf.ts
Normal file
17
src/docs/swagger.conf.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import { Router } from 'express';
|
||||
import { join } from 'path';
|
||||
import swaggerUi from 'swagger-ui-express';
|
||||
import YAML from 'yamljs';
|
||||
|
||||
const document = YAML.load(join(process.cwd(), 'src', 'docs', 'swagger.yaml'));
|
||||
|
||||
const router = Router();
|
||||
|
||||
export const swaggerRouter = router.use('/docs', swaggerUi.serve).get(
|
||||
'/docs',
|
||||
swaggerUi.setup(document, {
|
||||
customCssUrl: '/css/dark-theme-swagger.css',
|
||||
customSiteTitle: 'Evolution API',
|
||||
customfavIcon: '/images/logo.svg',
|
||||
}),
|
||||
);
|
||||
2772
src/docs/swagger.yaml
Normal file
2772
src/docs/swagger.yaml
Normal file
File diff suppressed because it is too large
Load Diff
@@ -5,7 +5,7 @@ export class UnauthorizedException {
|
||||
throw {
|
||||
status: HttpStatus.UNAUTHORIZED,
|
||||
error: 'Unauthorized',
|
||||
message: objectError.length > 0 ? objectError : undefined,
|
||||
message: objectError.length > 0 ? objectError : 'Unauthorized',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
100
src/libs/amqp.server.ts
Normal file
100
src/libs/amqp.server.ts
Normal file
@@ -0,0 +1,100 @@
|
||||
import * as amqp from 'amqplib/callback_api';
|
||||
|
||||
import { configService, Rabbitmq } from '../config/env.config';
|
||||
import { Logger } from '../config/logger.config';
|
||||
|
||||
const logger = new Logger('AMQP');
|
||||
|
||||
let amqpChannel: amqp.Channel | null = null;
|
||||
|
||||
export const initAMQP = () => {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
const uri = configService.get<Rabbitmq>('RABBITMQ').URI;
|
||||
amqp.connect(uri, (error, connection) => {
|
||||
if (error) {
|
||||
reject(error);
|
||||
return;
|
||||
}
|
||||
|
||||
connection.createChannel((channelError, channel) => {
|
||||
if (channelError) {
|
||||
reject(channelError);
|
||||
return;
|
||||
}
|
||||
|
||||
const exchangeName = 'evolution_exchange';
|
||||
|
||||
channel.assertExchange(exchangeName, 'topic', {
|
||||
durable: true,
|
||||
autoDelete: false,
|
||||
});
|
||||
|
||||
amqpChannel = channel;
|
||||
|
||||
logger.info('AMQP initialized');
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
export const getAMQP = (): amqp.Channel | null => {
|
||||
return amqpChannel;
|
||||
};
|
||||
|
||||
export const initQueues = (instanceName: string, events: string[]) => {
|
||||
if (!events || !events.length) return;
|
||||
|
||||
const queues = events.map((event) => {
|
||||
return `${event.replace(/_/g, '.').toLowerCase()}`;
|
||||
});
|
||||
|
||||
queues.forEach((event) => {
|
||||
const amqp = getAMQP();
|
||||
const exchangeName = instanceName ?? 'evolution_exchange';
|
||||
|
||||
amqp.assertExchange(exchangeName, 'topic', {
|
||||
durable: true,
|
||||
autoDelete: false,
|
||||
});
|
||||
|
||||
const queueName = `${instanceName}.${event}`;
|
||||
|
||||
amqp.assertQueue(queueName, {
|
||||
durable: true,
|
||||
autoDelete: false,
|
||||
arguments: {
|
||||
'x-queue-type': 'quorum',
|
||||
},
|
||||
});
|
||||
|
||||
amqp.bindQueue(queueName, exchangeName, event);
|
||||
});
|
||||
};
|
||||
|
||||
export const removeQueues = (instanceName: string, events: string[]) => {
|
||||
if (!events || !events.length) return;
|
||||
|
||||
const channel = getAMQP();
|
||||
|
||||
const queues = events.map((event) => {
|
||||
return `${event.replace(/_/g, '.').toLowerCase()}`;
|
||||
});
|
||||
|
||||
const exchangeName = instanceName ?? 'evolution_exchange';
|
||||
|
||||
queues.forEach((event) => {
|
||||
const amqp = getAMQP();
|
||||
|
||||
amqp.assertExchange(exchangeName, 'topic', {
|
||||
durable: true,
|
||||
autoDelete: false,
|
||||
});
|
||||
|
||||
const queueName = `${instanceName}.${event}`;
|
||||
|
||||
amqp.deleteQueue(queueName);
|
||||
});
|
||||
|
||||
channel.deleteExchange(exchangeName);
|
||||
};
|
||||
22
src/libs/cacheengine.ts
Normal file
22
src/libs/cacheengine.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import { CacheConf, ConfigService } from '../config/env.config';
|
||||
import { ICache } from '../whatsapp/abstract/abstract.cache';
|
||||
import { LocalCache } from './localcache';
|
||||
import { RedisCache } from './rediscache';
|
||||
|
||||
export class CacheEngine {
|
||||
private engine: ICache;
|
||||
|
||||
constructor(private readonly configService: ConfigService, module: string) {
|
||||
const cacheConf = configService.get<CacheConf>('CACHE');
|
||||
|
||||
if (cacheConf?.REDIS?.ENABLED && cacheConf?.REDIS?.URI !== '') {
|
||||
this.engine = new RedisCache(configService, module);
|
||||
} else if (cacheConf?.LOCAL?.ENABLED) {
|
||||
this.engine = new LocalCache(configService, module);
|
||||
}
|
||||
}
|
||||
|
||||
public getEngine() {
|
||||
return this.engine;
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
import mongoose from 'mongoose';
|
||||
|
||||
import { configService, Database } from '../config/env.config';
|
||||
import { Logger } from '../config/logger.config';
|
||||
|
||||
48
src/libs/localcache.ts
Normal file
48
src/libs/localcache.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
import NodeCache from 'node-cache';
|
||||
|
||||
import { CacheConf, CacheConfLocal, ConfigService } from '../config/env.config';
|
||||
import { ICache } from '../whatsapp/abstract/abstract.cache';
|
||||
|
||||
export class LocalCache implements ICache {
|
||||
private conf: CacheConfLocal;
|
||||
static localCache = new NodeCache();
|
||||
|
||||
constructor(private readonly configService: ConfigService, private readonly module: string) {
|
||||
this.conf = this.configService.get<CacheConf>('CACHE')?.LOCAL;
|
||||
}
|
||||
|
||||
async get(key: string): Promise<any> {
|
||||
return LocalCache.localCache.get(this.buildKey(key));
|
||||
}
|
||||
|
||||
async set(key: string, value: any, ttl?: number) {
|
||||
return LocalCache.localCache.set(this.buildKey(key), value, ttl || this.conf.TTL);
|
||||
}
|
||||
|
||||
async has(key: string) {
|
||||
return LocalCache.localCache.has(this.buildKey(key));
|
||||
}
|
||||
|
||||
async delete(key: string) {
|
||||
return LocalCache.localCache.del(this.buildKey(key));
|
||||
}
|
||||
|
||||
async deleteAll(appendCriteria?: string) {
|
||||
const keys = await this.keys(appendCriteria);
|
||||
if (!keys?.length) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return LocalCache.localCache.del(keys);
|
||||
}
|
||||
|
||||
async keys(appendCriteria?: string) {
|
||||
const filter = `${this.buildKey('')}${appendCriteria ? `${appendCriteria}:` : ''}`;
|
||||
|
||||
return LocalCache.localCache.keys().filter((key) => key.substring(0, filter.length) === filter);
|
||||
}
|
||||
|
||||
buildKey(key: string) {
|
||||
return `${this.module}:${key}`;
|
||||
}
|
||||
}
|
||||
49
src/libs/postgres.client.ts
Normal file
49
src/libs/postgres.client.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
import postgresql from 'pg';
|
||||
|
||||
import { Chatwoot, configService } from '../config/env.config';
|
||||
import { Logger } from '../config/logger.config';
|
||||
|
||||
const { Pool } = postgresql;
|
||||
|
||||
class Postgres {
|
||||
private logger = new Logger(Postgres.name);
|
||||
private pool;
|
||||
private connected = false;
|
||||
|
||||
getConnection(connectionString: string) {
|
||||
if (this.connected) {
|
||||
return this.pool;
|
||||
} else {
|
||||
this.pool = new Pool({
|
||||
connectionString,
|
||||
ssl: {
|
||||
rejectUnauthorized: false,
|
||||
},
|
||||
});
|
||||
|
||||
this.pool.on('error', () => {
|
||||
this.logger.error('postgres disconnected');
|
||||
this.connected = false;
|
||||
});
|
||||
|
||||
try {
|
||||
this.logger.verbose('connecting new postgres');
|
||||
this.connected = true;
|
||||
} catch (e) {
|
||||
this.connected = false;
|
||||
this.logger.error('postgres connect exception caught: ' + e);
|
||||
return null;
|
||||
}
|
||||
|
||||
return this.pool;
|
||||
}
|
||||
}
|
||||
|
||||
getChatwootConnection() {
|
||||
const uri = configService.get<Chatwoot>('CHATWOOT').IMPORT.DATABASE.CONNECTION.URI;
|
||||
|
||||
return this.getConnection(uri);
|
||||
}
|
||||
}
|
||||
|
||||
export const postgresClient = new Postgres();
|
||||
@@ -1,48 +1,59 @@
|
||||
import { createClient, RedisClientType } from '@redis/client';
|
||||
import { Logger } from '../config/logger.config';
|
||||
import { BufferJSON } from '@whiskeysockets/baileys';
|
||||
|
||||
import { Redis } from '../config/env.config';
|
||||
import { Logger } from '../config/logger.config';
|
||||
|
||||
export class RedisCache {
|
||||
constructor() {
|
||||
this.logger.verbose('instance created');
|
||||
process.on('beforeExit', async () => {
|
||||
this.logger.verbose('instance destroyed');
|
||||
if (this.statusConnection) {
|
||||
this.logger.verbose('instance disconnect');
|
||||
await this.client.disconnect();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private readonly logger = new Logger(RedisCache.name);
|
||||
private client: RedisClientType;
|
||||
private statusConnection = false;
|
||||
private instanceName: string;
|
||||
private redisEnv: Redis;
|
||||
|
||||
constructor() {
|
||||
this.logger.verbose('RedisCache instance created');
|
||||
process.on('beforeExit', () => {
|
||||
this.logger.verbose('RedisCache instance destroyed');
|
||||
this.disconnect();
|
||||
});
|
||||
}
|
||||
|
||||
public set reference(reference: string) {
|
||||
this.logger.verbose('set reference: ' + reference);
|
||||
this.instanceName = reference;
|
||||
}
|
||||
|
||||
public async connect(redisEnv: Redis) {
|
||||
this.logger.verbose('connecting');
|
||||
this.logger.verbose('Connecting to Redis...');
|
||||
this.client = createClient({ url: redisEnv.URI });
|
||||
this.logger.verbose('connected in ' + redisEnv.URI);
|
||||
this.client.on('error', (err) => this.logger.error('Redis Client Error ' + err));
|
||||
|
||||
await this.client.connect();
|
||||
this.statusConnection = true;
|
||||
this.redisEnv = redisEnv;
|
||||
this.logger.verbose(`Connected to ${redisEnv.URI}`);
|
||||
}
|
||||
|
||||
private readonly logger = new Logger(RedisCache.name);
|
||||
private client: RedisClientType;
|
||||
public async disconnect() {
|
||||
if (this.statusConnection) {
|
||||
await this.client.disconnect();
|
||||
this.statusConnection = false;
|
||||
this.logger.verbose('Redis client disconnected');
|
||||
}
|
||||
}
|
||||
|
||||
public async instanceKeys(): Promise<string[]> {
|
||||
const keys: string[] = [];
|
||||
try {
|
||||
this.logger.verbose('instance keys: ' + this.redisEnv.PREFIX_KEY + ':*');
|
||||
return await this.client.sendCommand(['keys', this.redisEnv.PREFIX_KEY + ':*']);
|
||||
this.logger.verbose('Fetching instance keys');
|
||||
for await (const key of this.client.scanIterator({ MATCH: `${this.redisEnv.PREFIX_KEY}:*` })) {
|
||||
keys.push(key);
|
||||
}
|
||||
} catch (error) {
|
||||
this.logger.error(error);
|
||||
this.logger.error('Error fetching instance keys ' + error);
|
||||
}
|
||||
return keys;
|
||||
}
|
||||
|
||||
public async keyExists(key?: string) {
|
||||
@@ -59,11 +70,7 @@ export class RedisCache {
|
||||
this.logger.verbose('writeData: ' + field);
|
||||
const json = JSON.stringify(data, BufferJSON.replacer);
|
||||
|
||||
return await this.client.hSet(
|
||||
this.redisEnv.PREFIX_KEY + ':' + this.instanceName,
|
||||
field,
|
||||
json,
|
||||
);
|
||||
return await this.client.hSet(this.redisEnv.PREFIX_KEY + ':' + this.instanceName, field, json);
|
||||
} catch (error) {
|
||||
this.logger.error(error);
|
||||
}
|
||||
@@ -72,10 +79,7 @@ export class RedisCache {
|
||||
public async readData(field: string) {
|
||||
try {
|
||||
this.logger.verbose('readData: ' + field);
|
||||
const data = await this.client.hGet(
|
||||
this.redisEnv.PREFIX_KEY + ':' + this.instanceName,
|
||||
field,
|
||||
);
|
||||
const data = await this.client.hGet(this.redisEnv.PREFIX_KEY + ':' + this.instanceName, field);
|
||||
|
||||
if (data) {
|
||||
this.logger.verbose('readData: ' + field + ' success');
|
||||
@@ -92,10 +96,7 @@ export class RedisCache {
|
||||
public async removeData(field: string) {
|
||||
try {
|
||||
this.logger.verbose('removeData: ' + field);
|
||||
return await this.client.hDel(
|
||||
this.redisEnv.PREFIX_KEY + ':' + this.instanceName,
|
||||
field,
|
||||
);
|
||||
return await this.client.hDel(this.redisEnv.PREFIX_KEY + ':' + this.instanceName, field);
|
||||
} catch (error) {
|
||||
this.logger.error(error);
|
||||
}
|
||||
@@ -104,9 +105,7 @@ export class RedisCache {
|
||||
public async delAll(hash?: string) {
|
||||
try {
|
||||
this.logger.verbose('instance delAll: ' + hash);
|
||||
const result = await this.client.del(
|
||||
hash || this.redisEnv.PREFIX_KEY + ':' + this.instanceName,
|
||||
);
|
||||
const result = await this.client.del(hash || this.redisEnv.PREFIX_KEY + ':' + this.instanceName);
|
||||
|
||||
return result;
|
||||
} catch (error) {
|
||||
59
src/libs/rediscache.client.ts
Normal file
59
src/libs/rediscache.client.ts
Normal file
@@ -0,0 +1,59 @@
|
||||
import { createClient, RedisClientType } from 'redis';
|
||||
|
||||
import { CacheConf, CacheConfRedis, configService } from '../config/env.config';
|
||||
import { Logger } from '../config/logger.config';
|
||||
|
||||
class Redis {
|
||||
private logger = new Logger(Redis.name);
|
||||
private client: RedisClientType = null;
|
||||
private conf: CacheConfRedis;
|
||||
private connected = false;
|
||||
|
||||
constructor() {
|
||||
this.conf = configService.get<CacheConf>('CACHE')?.REDIS;
|
||||
}
|
||||
|
||||
getConnection(): RedisClientType {
|
||||
if (this.connected) {
|
||||
return this.client;
|
||||
} else {
|
||||
this.client = createClient({
|
||||
url: this.conf.URI,
|
||||
});
|
||||
|
||||
this.client.on('connect', () => {
|
||||
this.logger.verbose('redis connecting');
|
||||
});
|
||||
|
||||
this.client.on('ready', () => {
|
||||
this.logger.verbose('redis ready');
|
||||
this.connected = true;
|
||||
});
|
||||
|
||||
this.client.on('error', () => {
|
||||
this.logger.error('redis disconnected');
|
||||
this.connected = false;
|
||||
});
|
||||
|
||||
this.client.on('end', () => {
|
||||
this.logger.verbose('redis connection ended');
|
||||
this.connected = false;
|
||||
});
|
||||
|
||||
try {
|
||||
this.logger.verbose('connecting new redis client');
|
||||
this.client.connect();
|
||||
this.connected = true;
|
||||
this.logger.verbose('connected to new redis client');
|
||||
} catch (e) {
|
||||
this.connected = false;
|
||||
this.logger.error('redis connect exception caught: ' + e);
|
||||
return null;
|
||||
}
|
||||
|
||||
return this.client;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const redisClient = new Redis();
|
||||
83
src/libs/rediscache.ts
Normal file
83
src/libs/rediscache.ts
Normal file
@@ -0,0 +1,83 @@
|
||||
import { RedisClientType } from 'redis';
|
||||
|
||||
import { CacheConf, CacheConfRedis, ConfigService } from '../config/env.config';
|
||||
import { Logger } from '../config/logger.config';
|
||||
import { ICache } from '../whatsapp/abstract/abstract.cache';
|
||||
import { redisClient } from './rediscache.client';
|
||||
|
||||
export class RedisCache implements ICache {
|
||||
private readonly logger = new Logger(RedisCache.name);
|
||||
private client: RedisClientType;
|
||||
private conf: CacheConfRedis;
|
||||
|
||||
constructor(private readonly configService: ConfigService, private readonly module: string) {
|
||||
this.conf = this.configService.get<CacheConf>('CACHE')?.REDIS;
|
||||
this.client = redisClient.getConnection();
|
||||
}
|
||||
|
||||
async get(key: string): Promise<any> {
|
||||
try {
|
||||
return JSON.parse(await this.client.get(this.buildKey(key)));
|
||||
} catch (error) {
|
||||
this.logger.error(error);
|
||||
}
|
||||
}
|
||||
|
||||
async set(key: string, value: any, ttl?: number) {
|
||||
try {
|
||||
await this.client.setEx(this.buildKey(key), ttl || this.conf?.TTL, JSON.stringify(value));
|
||||
} catch (error) {
|
||||
this.logger.error(error);
|
||||
}
|
||||
}
|
||||
|
||||
async has(key: string) {
|
||||
try {
|
||||
return (await this.client.exists(this.buildKey(key))) > 0;
|
||||
} catch (error) {
|
||||
this.logger.error(error);
|
||||
}
|
||||
}
|
||||
|
||||
async delete(key: string) {
|
||||
try {
|
||||
return await this.client.del(this.buildKey(key));
|
||||
} catch (error) {
|
||||
this.logger.error(error);
|
||||
}
|
||||
}
|
||||
|
||||
async deleteAll(appendCriteria?: string) {
|
||||
try {
|
||||
const keys = await this.keys(appendCriteria);
|
||||
if (!keys?.length) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return await this.client.del(keys);
|
||||
} catch (error) {
|
||||
this.logger.error(error);
|
||||
}
|
||||
}
|
||||
|
||||
async keys(appendCriteria?: string) {
|
||||
try {
|
||||
const match = `${this.buildKey('')}${appendCriteria ? `${appendCriteria}:` : ''}*`;
|
||||
const keys = [];
|
||||
for await (const key of this.client.scanIterator({
|
||||
MATCH: match,
|
||||
COUNT: 100,
|
||||
})) {
|
||||
keys.push(key);
|
||||
}
|
||||
|
||||
return [...new Set(keys)];
|
||||
} catch (error) {
|
||||
this.logger.error(error);
|
||||
}
|
||||
}
|
||||
|
||||
buildKey(key: string) {
|
||||
return `${this.conf?.PREFIX_KEY}:${this.module}:${key}`;
|
||||
}
|
||||
}
|
||||
44
src/libs/socket.server.ts
Normal file
44
src/libs/socket.server.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
import { Server } from 'http';
|
||||
import { Server as SocketIO } from 'socket.io';
|
||||
|
||||
import { configService, Cors, Websocket } from '../config/env.config';
|
||||
import { Logger } from '../config/logger.config';
|
||||
|
||||
const logger = new Logger('Socket');
|
||||
|
||||
let io: SocketIO;
|
||||
|
||||
const cors = configService.get<Cors>('CORS').ORIGIN;
|
||||
|
||||
export const initIO = (httpServer: Server) => {
|
||||
if (configService.get<Websocket>('WEBSOCKET')?.ENABLED) {
|
||||
io = new SocketIO(httpServer, {
|
||||
cors: {
|
||||
origin: cors,
|
||||
},
|
||||
});
|
||||
|
||||
io.on('connection', (socket) => {
|
||||
logger.info('User connected');
|
||||
|
||||
socket.on('disconnect', () => {
|
||||
logger.info('User disconnected');
|
||||
});
|
||||
});
|
||||
|
||||
logger.info('Socket.io initialized');
|
||||
return io;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
export const getIO = (): SocketIO => {
|
||||
logger.verbose('Getting Socket.io');
|
||||
|
||||
if (!io) {
|
||||
logger.error('Socket.io not initialized');
|
||||
throw new Error('Socket.io not initialized');
|
||||
}
|
||||
|
||||
return io;
|
||||
};
|
||||
97
src/libs/sqs.server.ts
Normal file
97
src/libs/sqs.server.ts
Normal file
@@ -0,0 +1,97 @@
|
||||
import { SQS } from 'aws-sdk';
|
||||
|
||||
import { configService, Sqs } from '../config/env.config';
|
||||
import { Logger } from '../config/logger.config';
|
||||
|
||||
const logger = new Logger('SQS');
|
||||
|
||||
let sqs: SQS;
|
||||
|
||||
export const initSQS = () => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
const awsConfig = configService.get<Sqs>('SQS');
|
||||
sqs = new SQS({
|
||||
accessKeyId: awsConfig.ACCESS_KEY_ID,
|
||||
secretAccessKey: awsConfig.SECRET_ACCESS_KEY,
|
||||
region: awsConfig.REGION,
|
||||
});
|
||||
|
||||
logger.info('SQS initialized');
|
||||
resolve();
|
||||
});
|
||||
};
|
||||
|
||||
export const getSQS = (): SQS => {
|
||||
return sqs;
|
||||
};
|
||||
|
||||
export const initQueues = (instanceName: string, events: string[]) => {
|
||||
if (!events || !events.length) return;
|
||||
|
||||
const queues = events.map((event) => {
|
||||
return `${event.replace(/_/g, '_').toLowerCase()}`;
|
||||
});
|
||||
|
||||
const sqs = getSQS();
|
||||
|
||||
queues.forEach((event) => {
|
||||
const queueName = `${instanceName}_${event}.fifo`;
|
||||
|
||||
sqs.createQueue(
|
||||
{
|
||||
QueueName: queueName,
|
||||
Attributes: {
|
||||
FifoQueue: 'true',
|
||||
},
|
||||
},
|
||||
(err, data) => {
|
||||
if (err) {
|
||||
logger.error(`Error creating queue ${queueName}: ${err.message}`);
|
||||
} else {
|
||||
logger.info(`Queue ${queueName} created: ${data.QueueUrl}`);
|
||||
}
|
||||
},
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
export const removeQueues = (instanceName: string, events: string[]) => {
|
||||
if (!events || !events.length) return;
|
||||
|
||||
const sqs = getSQS();
|
||||
|
||||
const queues = events.map((event) => {
|
||||
return `${event.replace(/_/g, '_').toLowerCase()}`;
|
||||
});
|
||||
|
||||
queues.forEach((event) => {
|
||||
const queueName = `${instanceName}_${event}.fifo`;
|
||||
|
||||
sqs.getQueueUrl(
|
||||
{
|
||||
QueueName: queueName,
|
||||
},
|
||||
(err, data) => {
|
||||
if (err) {
|
||||
logger.error(`Error getting queue URL for ${queueName}: ${err.message}`);
|
||||
} else {
|
||||
const queueUrl = data.QueueUrl;
|
||||
|
||||
sqs.deleteQueue(
|
||||
{
|
||||
QueueUrl: queueUrl,
|
||||
},
|
||||
(deleteErr) => {
|
||||
if (deleteErr) {
|
||||
logger.error(`Error deleting queue ${queueName}: ${deleteErr.message}`);
|
||||
} else {
|
||||
logger.info(`Queue ${queueName} deleted`);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
});
|
||||
};
|
||||
79
src/main.ts
79
src/main.ts
@@ -1,15 +1,22 @@
|
||||
import 'express-async-errors';
|
||||
|
||||
import axios from 'axios';
|
||||
import compression from 'compression';
|
||||
import { configService, Cors, HttpServer } from './config/env.config';
|
||||
import cors from 'cors';
|
||||
import express, { json, NextFunction, Request, Response, urlencoded } from 'express';
|
||||
import { join } from 'path';
|
||||
|
||||
import { Auth, configService, Cors, HttpServer, Rabbitmq, Sqs, Webhook } from './config/env.config';
|
||||
import { onUnexpectedError } from './config/error.config';
|
||||
import { Logger } from './config/logger.config';
|
||||
import { ROOT_DIR } from './config/path.config';
|
||||
import { waMonitor } from './whatsapp/whatsapp.module';
|
||||
import { HttpStatus, router } from './whatsapp/routers/index.router';
|
||||
import 'express-async-errors';
|
||||
import { swaggerRouter } from './docs/swagger.conf';
|
||||
import { initAMQP } from './libs/amqp.server';
|
||||
import { initIO } from './libs/socket.server';
|
||||
import { initSQS } from './libs/sqs.server';
|
||||
import { ServerUP } from './utils/server-up';
|
||||
import { HttpStatus, router } from './whatsapp/routers/index.router';
|
||||
import { waMonitor } from './whatsapp/whatsapp.module';
|
||||
|
||||
function initWA() {
|
||||
waMonitor.loadInstance();
|
||||
@@ -23,7 +30,9 @@ function bootstrap() {
|
||||
cors({
|
||||
origin(requestOrigin, callback) {
|
||||
const { ORIGIN } = configService.get<Cors>('CORS');
|
||||
!requestOrigin ? (requestOrigin = '*') : undefined;
|
||||
if (ORIGIN.includes('*')) {
|
||||
return callback(null, true);
|
||||
}
|
||||
if (ORIGIN.indexOf(requestOrigin) !== -1) {
|
||||
return callback(null, true);
|
||||
}
|
||||
@@ -41,21 +50,67 @@ function bootstrap() {
|
||||
app.set('views', join(ROOT_DIR, 'views'));
|
||||
app.use(express.static(join(ROOT_DIR, 'public')));
|
||||
|
||||
app.use('/store', express.static(join(ROOT_DIR, 'store')));
|
||||
|
||||
app.use('/', router);
|
||||
|
||||
if (!configService.get('SERVER').DISABLE_DOCS) app.use(swaggerRouter);
|
||||
|
||||
app.use(
|
||||
(err: Error, req: Request, res: Response, next: NextFunction) => {
|
||||
if (err) {
|
||||
return res.status(err['status'] || 500).json(err);
|
||||
const webhook = configService.get<Webhook>('WEBHOOK');
|
||||
|
||||
if (webhook.EVENTS.ERRORS_WEBHOOK && webhook.EVENTS.ERRORS_WEBHOOK != '' && webhook.EVENTS.ERRORS) {
|
||||
const tzoffset = new Date().getTimezoneOffset() * 60000; //offset in milliseconds
|
||||
const localISOTime = new Date(Date.now() - tzoffset).toISOString();
|
||||
const now = localISOTime;
|
||||
const globalApiKey = configService.get<Auth>('AUTHENTICATION').API_KEY.KEY;
|
||||
const serverUrl = configService.get<HttpServer>('SERVER').URL;
|
||||
|
||||
const errorData = {
|
||||
event: 'error',
|
||||
data: {
|
||||
error: err['error'] || 'Internal Server Error',
|
||||
message: err['message'] || 'Internal Server Error',
|
||||
status: err['status'] || 500,
|
||||
response: {
|
||||
message: err['message'] || 'Internal Server Error',
|
||||
},
|
||||
},
|
||||
date_time: now,
|
||||
api_key: globalApiKey,
|
||||
server_url: serverUrl,
|
||||
};
|
||||
|
||||
logger.error(errorData);
|
||||
|
||||
const baseURL = webhook.EVENTS.ERRORS_WEBHOOK;
|
||||
const httpService = axios.create({ baseURL });
|
||||
|
||||
httpService.post('', errorData);
|
||||
}
|
||||
|
||||
return res.status(err['status'] || 500).json({
|
||||
status: err['status'] || 500,
|
||||
error: err['error'] || 'Internal Server Error',
|
||||
response: {
|
||||
message: err['message'] || 'Internal Server Error',
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
next();
|
||||
},
|
||||
(req: Request, res: Response, next: NextFunction) => {
|
||||
const { method, url } = req;
|
||||
|
||||
res.status(HttpStatus.NOT_FOUND).json({
|
||||
status: HttpStatus.NOT_FOUND,
|
||||
message: `Cannot ${method.toUpperCase()} ${url}`,
|
||||
error: 'Not Found',
|
||||
response: {
|
||||
message: [`Cannot ${method.toUpperCase()} ${url}`],
|
||||
},
|
||||
});
|
||||
|
||||
next();
|
||||
@@ -67,12 +122,16 @@ function bootstrap() {
|
||||
ServerUP.app = app;
|
||||
const server = ServerUP[httpServer.TYPE];
|
||||
|
||||
server.listen(httpServer.PORT, () =>
|
||||
logger.log(httpServer.TYPE.toUpperCase() + ' - ON: ' + httpServer.PORT),
|
||||
);
|
||||
server.listen(httpServer.PORT, () => logger.log(httpServer.TYPE.toUpperCase() + ' - ON: ' + httpServer.PORT));
|
||||
|
||||
initWA();
|
||||
|
||||
initIO(server);
|
||||
|
||||
if (configService.get<Rabbitmq>('RABBITMQ')?.ENABLED) initAMQP();
|
||||
|
||||
if (configService.get<Sqs>('SQS')?.ENABLED) initSQS();
|
||||
|
||||
onUnexpectedError();
|
||||
}
|
||||
|
||||
|
||||
472
src/utils/chatwoot-import-helper.ts
Normal file
472
src/utils/chatwoot-import-helper.ts
Normal file
@@ -0,0 +1,472 @@
|
||||
import { inbox } from '@figuro/chatwoot-sdk';
|
||||
import { proto } from '@whiskeysockets/baileys';
|
||||
|
||||
import { Chatwoot, configService } from '../config/env.config';
|
||||
import { Logger } from '../config/logger.config';
|
||||
import { postgresClient } from '../libs/postgres.client';
|
||||
import { InstanceDto } from '../whatsapp/dto/instance.dto';
|
||||
import { ChatwootRaw, ContactRaw, MessageRaw } from '../whatsapp/models';
|
||||
import { ChatwootService } from '../whatsapp/services/chatwoot.service';
|
||||
|
||||
type ChatwootUser = {
|
||||
user_type: string;
|
||||
user_id: number;
|
||||
};
|
||||
|
||||
type FksChatwoot = {
|
||||
phone_number: string;
|
||||
contact_id: string;
|
||||
conversation_id: string;
|
||||
};
|
||||
|
||||
type firstLastTimestamp = {
|
||||
first: number;
|
||||
last: number;
|
||||
};
|
||||
|
||||
type IWebMessageInfo = Omit<proto.IWebMessageInfo, 'key'> & Partial<Pick<proto.IWebMessageInfo, 'key'>>;
|
||||
|
||||
class ChatwootImport {
|
||||
private logger = new Logger(ChatwootImport.name);
|
||||
private repositoryMessagesCache = new Map<string, Set<string>>();
|
||||
private historyMessages = new Map<string, MessageRaw[]>();
|
||||
private historyContacts = new Map<string, ContactRaw[]>();
|
||||
|
||||
public getRepositoryMessagesCache(instance: InstanceDto) {
|
||||
return this.repositoryMessagesCache.has(instance.instanceName)
|
||||
? this.repositoryMessagesCache.get(instance.instanceName)
|
||||
: null;
|
||||
}
|
||||
|
||||
public setRepositoryMessagesCache(instance: InstanceDto, repositoryMessagesCache: Set<string>) {
|
||||
this.repositoryMessagesCache.set(instance.instanceName, repositoryMessagesCache);
|
||||
}
|
||||
|
||||
public deleteRepositoryMessagesCache(instance: InstanceDto) {
|
||||
this.repositoryMessagesCache.delete(instance.instanceName);
|
||||
}
|
||||
|
||||
public addHistoryMessages(instance: InstanceDto, messagesRaw: MessageRaw[]) {
|
||||
const actualValue = this.historyMessages.has(instance.instanceName)
|
||||
? this.historyMessages.get(instance.instanceName)
|
||||
: [];
|
||||
this.historyMessages.set(instance.instanceName, actualValue.concat(messagesRaw));
|
||||
}
|
||||
|
||||
public addHistoryContacts(instance: InstanceDto, contactsRaw: ContactRaw[]) {
|
||||
const actualValue = this.historyContacts.has(instance.instanceName)
|
||||
? this.historyContacts.get(instance.instanceName)
|
||||
: [];
|
||||
this.historyContacts.set(instance.instanceName, actualValue.concat(contactsRaw));
|
||||
}
|
||||
|
||||
public deleteHistoryMessages(instance: InstanceDto) {
|
||||
this.historyMessages.delete(instance.instanceName);
|
||||
}
|
||||
|
||||
public deleteHistoryContacts(instance: InstanceDto) {
|
||||
this.historyContacts.delete(instance.instanceName);
|
||||
}
|
||||
|
||||
public clearAll(instance: InstanceDto) {
|
||||
this.deleteRepositoryMessagesCache(instance);
|
||||
this.deleteHistoryMessages(instance);
|
||||
this.deleteHistoryContacts(instance);
|
||||
}
|
||||
|
||||
public getHistoryMessagesLenght(instance: InstanceDto) {
|
||||
return this.historyMessages.get(instance.instanceName)?.length ?? 0;
|
||||
}
|
||||
|
||||
public async importHistoryContacts(instance: InstanceDto, provider: ChatwootRaw) {
|
||||
try {
|
||||
if (this.getHistoryMessagesLenght(instance) > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const pgClient = postgresClient.getChatwootConnection();
|
||||
|
||||
let totalContactsImported = 0;
|
||||
|
||||
const contacts = this.historyContacts.get(instance.instanceName) || [];
|
||||
if (contacts.length === 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
let contactsChunk: ContactRaw[] = this.sliceIntoChunks(contacts, 3000);
|
||||
while (contactsChunk.length > 0) {
|
||||
// inserting contacts in chatwoot db
|
||||
let sqlInsert = `INSERT INTO contacts
|
||||
(name, phone_number, account_id, identifier, created_at, updated_at) VALUES `;
|
||||
const bindInsert = [provider.account_id];
|
||||
|
||||
for (const contact of contactsChunk) {
|
||||
bindInsert.push(contact.pushName);
|
||||
const bindName = `$${bindInsert.length}`;
|
||||
|
||||
bindInsert.push(`+${contact.id.split('@')[0]}`);
|
||||
const bindPhoneNumber = `$${bindInsert.length}`;
|
||||
|
||||
bindInsert.push(contact.id);
|
||||
const bindIdentifier = `$${bindInsert.length}`;
|
||||
|
||||
sqlInsert += `(${bindName}, ${bindPhoneNumber}, $1, ${bindIdentifier}, NOW(), NOW()),`;
|
||||
}
|
||||
if (sqlInsert.slice(-1) === ',') {
|
||||
sqlInsert = sqlInsert.slice(0, -1);
|
||||
}
|
||||
sqlInsert += ` ON CONFLICT (identifier, account_id)
|
||||
DO UPDATE SET
|
||||
name = EXCLUDED.name,
|
||||
phone_number = EXCLUDED.phone_number,
|
||||
identifier = EXCLUDED.identifier`;
|
||||
|
||||
totalContactsImported += (await pgClient.query(sqlInsert, bindInsert))?.rowCount ?? 0;
|
||||
contactsChunk = this.sliceIntoChunks(contacts, 3000);
|
||||
}
|
||||
|
||||
this.deleteHistoryContacts(instance);
|
||||
|
||||
return totalContactsImported;
|
||||
} catch (error) {
|
||||
this.logger.error(`Error on import history contacts: ${error.toString()}`);
|
||||
}
|
||||
}
|
||||
|
||||
public async importHistoryMessages(
|
||||
instance: InstanceDto,
|
||||
chatwootService: ChatwootService,
|
||||
inbox: inbox,
|
||||
provider: ChatwootRaw,
|
||||
) {
|
||||
try {
|
||||
const pgClient = postgresClient.getChatwootConnection();
|
||||
|
||||
const chatwootUser = await this.getChatwootUser(provider);
|
||||
if (!chatwootUser) {
|
||||
throw new Error('User not found to import messages.');
|
||||
}
|
||||
|
||||
let totalMessagesImported = 0;
|
||||
|
||||
const messagesOrdered = this.historyMessages.get(instance.instanceName) || [];
|
||||
if (messagesOrdered.length === 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ordering messages by number and timestamp asc
|
||||
messagesOrdered.sort((a, b) => {
|
||||
return (
|
||||
parseInt(a.key.remoteJid) - parseInt(b.key.remoteJid) ||
|
||||
(a.messageTimestamp as number) - (b.messageTimestamp as number)
|
||||
);
|
||||
});
|
||||
|
||||
const allMessagesMappedByPhoneNumber = this.createMessagesMapByPhoneNumber(messagesOrdered);
|
||||
// Map structure: +552199999999 => { first message timestamp from number, last message timestamp from number}
|
||||
const phoneNumbersWithTimestamp = new Map<string, firstLastTimestamp>();
|
||||
allMessagesMappedByPhoneNumber.forEach((messages: MessageRaw[], phoneNumber: string) => {
|
||||
phoneNumbersWithTimestamp.set(phoneNumber, {
|
||||
first: messages[0]?.messageTimestamp as number,
|
||||
last: messages[messages.length - 1]?.messageTimestamp as number,
|
||||
});
|
||||
});
|
||||
|
||||
// processing messages in batch
|
||||
const batchSize = 4000;
|
||||
let messagesChunk: MessageRaw[] = this.sliceIntoChunks(messagesOrdered, batchSize);
|
||||
while (messagesChunk.length > 0) {
|
||||
// Map structure: +552199999999 => MessageRaw[]
|
||||
const messagesByPhoneNumber = this.createMessagesMapByPhoneNumber(messagesChunk);
|
||||
|
||||
if (messagesByPhoneNumber.size > 0) {
|
||||
const fksByNumber = await this.selectOrCreateFksFromChatwoot(
|
||||
provider,
|
||||
inbox,
|
||||
phoneNumbersWithTimestamp,
|
||||
messagesByPhoneNumber,
|
||||
);
|
||||
|
||||
// inserting messages in chatwoot db
|
||||
let sqlInsertMsg = `INSERT INTO messages
|
||||
(content, account_id, inbox_id, conversation_id, message_type, private, content_type,
|
||||
sender_type, sender_id, created_at, updated_at) VALUES `;
|
||||
const bindInsertMsg = [provider.account_id, inbox.id];
|
||||
|
||||
messagesByPhoneNumber.forEach((messages: MessageRaw[], phoneNumber: string) => {
|
||||
const fksChatwoot = fksByNumber.get(phoneNumber);
|
||||
|
||||
messages.forEach((message) => {
|
||||
if (!message.message) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!fksChatwoot?.conversation_id || !fksChatwoot?.contact_id) {
|
||||
return;
|
||||
}
|
||||
|
||||
const contentMessage = this.getContentMessage(chatwootService, message);
|
||||
if (!contentMessage) {
|
||||
return;
|
||||
}
|
||||
|
||||
bindInsertMsg.push(contentMessage);
|
||||
const bindContent = `$${bindInsertMsg.length}`;
|
||||
|
||||
bindInsertMsg.push(fksChatwoot.conversation_id);
|
||||
const bindConversationId = `$${bindInsertMsg.length}`;
|
||||
|
||||
bindInsertMsg.push(message.key.fromMe ? '1' : '0');
|
||||
const bindMessageType = `$${bindInsertMsg.length}`;
|
||||
|
||||
bindInsertMsg.push(message.key.fromMe ? chatwootUser.user_type : 'Contact');
|
||||
const bindSenderType = `$${bindInsertMsg.length}`;
|
||||
|
||||
bindInsertMsg.push(message.key.fromMe ? chatwootUser.user_id : fksChatwoot.contact_id);
|
||||
const bindSenderId = `$${bindInsertMsg.length}`;
|
||||
|
||||
bindInsertMsg.push(message.messageTimestamp as number);
|
||||
const bindmessageTimestamp = `$${bindInsertMsg.length}`;
|
||||
|
||||
sqlInsertMsg += `(${bindContent}, $1, $2, ${bindConversationId}, ${bindMessageType}, FALSE, 0,
|
||||
${bindSenderType},${bindSenderId}, to_timestamp(${bindmessageTimestamp}), to_timestamp(${bindmessageTimestamp})),`;
|
||||
});
|
||||
});
|
||||
if (bindInsertMsg.length > 2) {
|
||||
if (sqlInsertMsg.slice(-1) === ',') {
|
||||
sqlInsertMsg = sqlInsertMsg.slice(0, -1);
|
||||
}
|
||||
totalMessagesImported += (await pgClient.query(sqlInsertMsg, bindInsertMsg))?.rowCount ?? 0;
|
||||
}
|
||||
}
|
||||
messagesChunk = this.sliceIntoChunks(messagesOrdered, batchSize);
|
||||
}
|
||||
|
||||
this.deleteHistoryMessages(instance);
|
||||
this.deleteRepositoryMessagesCache(instance);
|
||||
|
||||
this.importHistoryContacts(instance, provider);
|
||||
|
||||
return totalMessagesImported;
|
||||
} catch (error) {
|
||||
this.logger.error(`Error on import history messages: ${error.toString()}`);
|
||||
|
||||
this.deleteHistoryMessages(instance);
|
||||
this.deleteRepositoryMessagesCache(instance);
|
||||
}
|
||||
}
|
||||
|
||||
public async selectOrCreateFksFromChatwoot(
|
||||
provider: ChatwootRaw,
|
||||
inbox: inbox,
|
||||
phoneNumbersWithTimestamp: Map<string, firstLastTimestamp>,
|
||||
messagesByPhoneNumber: Map<string, MessageRaw[]>,
|
||||
): Promise<Map<string, FksChatwoot>> {
|
||||
const pgClient = postgresClient.getChatwootConnection();
|
||||
|
||||
const bindValues = [provider.account_id, inbox.id];
|
||||
const phoneNumberBind = Array.from(messagesByPhoneNumber.keys())
|
||||
.map((phoneNumber) => {
|
||||
const phoneNumberTimestamp = phoneNumbersWithTimestamp.get(phoneNumber);
|
||||
|
||||
if (phoneNumberTimestamp) {
|
||||
bindValues.push(phoneNumber);
|
||||
let bindStr = `($${bindValues.length},`;
|
||||
|
||||
bindValues.push(phoneNumberTimestamp.first);
|
||||
bindStr += `$${bindValues.length},`;
|
||||
|
||||
bindValues.push(phoneNumberTimestamp.last);
|
||||
return `${bindStr}$${bindValues.length})`;
|
||||
}
|
||||
})
|
||||
.join(',');
|
||||
|
||||
// select (or insert when necessary) data from tables contacts, contact_inboxes, conversations from chatwoot db
|
||||
const sqlFromChatwoot = `WITH
|
||||
phone_number AS (
|
||||
SELECT phone_number, created_at::INTEGER, last_activity_at::INTEGER FROM (
|
||||
VALUES
|
||||
${phoneNumberBind}
|
||||
) as t (phone_number, created_at, last_activity_at)
|
||||
),
|
||||
|
||||
only_new_phone_number AS (
|
||||
SELECT * FROM phone_number
|
||||
WHERE phone_number NOT IN (
|
||||
SELECT phone_number
|
||||
FROM contacts
|
||||
JOIN contact_inboxes ci ON ci.contact_id = contacts.id AND ci.inbox_id = $2
|
||||
JOIN conversations con ON con.contact_inbox_id = ci.id
|
||||
AND con.account_id = $1
|
||||
AND con.inbox_id = $2
|
||||
AND con.contact_id = contacts.id
|
||||
WHERE contacts.account_id = $1
|
||||
)
|
||||
),
|
||||
|
||||
new_contact AS (
|
||||
INSERT INTO contacts (name, phone_number, account_id, identifier, created_at, updated_at)
|
||||
SELECT REPLACE(p.phone_number, '+', ''), p.phone_number, $1, CONCAT(REPLACE(p.phone_number, '+', ''),
|
||||
'@s.whatsapp.net'), to_timestamp(p.created_at), to_timestamp(p.last_activity_at)
|
||||
FROM only_new_phone_number AS p
|
||||
ON CONFLICT(identifier, account_id) DO UPDATE SET updated_at = EXCLUDED.updated_at
|
||||
RETURNING id, phone_number, created_at, updated_at
|
||||
),
|
||||
|
||||
new_contact_inbox AS (
|
||||
INSERT INTO contact_inboxes (contact_id, inbox_id, source_id, created_at, updated_at)
|
||||
SELECT new_contact.id, $2, gen_random_uuid(), new_contact.created_at, new_contact.updated_at
|
||||
FROM new_contact
|
||||
RETURNING id, contact_id, created_at, updated_at
|
||||
),
|
||||
|
||||
new_conversation AS (
|
||||
INSERT INTO conversations (account_id, inbox_id, status, contact_id,
|
||||
contact_inbox_id, uuid, last_activity_at, created_at, updated_at)
|
||||
SELECT $1, $2, 0, new_contact_inbox.contact_id, new_contact_inbox.id, gen_random_uuid(),
|
||||
new_contact_inbox.updated_at, new_contact_inbox.created_at, new_contact_inbox.updated_at
|
||||
FROM new_contact_inbox
|
||||
RETURNING id, contact_id
|
||||
)
|
||||
|
||||
SELECT new_contact.phone_number, new_conversation.contact_id, new_conversation.id AS conversation_id
|
||||
FROM new_conversation
|
||||
JOIN new_contact ON new_conversation.contact_id = new_contact.id
|
||||
|
||||
UNION
|
||||
|
||||
SELECT p.phone_number, c.id contact_id, con.id conversation_id
|
||||
FROM phone_number p
|
||||
JOIN contacts c ON c.phone_number = p.phone_number
|
||||
JOIN contact_inboxes ci ON ci.contact_id = c.id AND ci.inbox_id = $2
|
||||
JOIN conversations con ON con.contact_inbox_id = ci.id AND con.account_id = $1
|
||||
AND con.inbox_id = $2 AND con.contact_id = c.id`;
|
||||
|
||||
const fksFromChatwoot = await pgClient.query(sqlFromChatwoot, bindValues);
|
||||
|
||||
return new Map(fksFromChatwoot.rows.map((item: FksChatwoot) => [item.phone_number, item]));
|
||||
}
|
||||
|
||||
public async getChatwootUser(provider: ChatwootRaw): Promise<ChatwootUser> {
|
||||
try {
|
||||
const pgClient = postgresClient.getChatwootConnection();
|
||||
|
||||
const sqlUser = `SELECT owner_type AS user_type, owner_id AS user_id
|
||||
FROM access_tokens
|
||||
WHERE token = $1`;
|
||||
|
||||
return (await pgClient.query(sqlUser, [provider.token]))?.rows[0] || false;
|
||||
} catch (error) {
|
||||
this.logger.error(`Error on getChatwootUser: ${error.toString()}`);
|
||||
}
|
||||
}
|
||||
|
||||
public createMessagesMapByPhoneNumber(messages: MessageRaw[]): Map<string, MessageRaw[]> {
|
||||
return messages.reduce((acc: Map<string, MessageRaw[]>, message: MessageRaw) => {
|
||||
if (!this.isIgnorePhoneNumber(message?.key?.remoteJid)) {
|
||||
const phoneNumber = message?.key?.remoteJid?.split('@')[0];
|
||||
if (phoneNumber) {
|
||||
const phoneNumberPlus = `+${phoneNumber}`;
|
||||
const messages = acc.has(phoneNumberPlus) ? acc.get(phoneNumberPlus) : [];
|
||||
messages.push(message);
|
||||
acc.set(phoneNumberPlus, messages);
|
||||
}
|
||||
}
|
||||
|
||||
return acc;
|
||||
}, new Map());
|
||||
}
|
||||
|
||||
public async getContactsOrderByRecentConversations(
|
||||
inbox: inbox,
|
||||
provider: ChatwootRaw,
|
||||
limit = 50,
|
||||
): Promise<{ id: number; phone_number: string; identifier: string }[]> {
|
||||
try {
|
||||
const pgClient = postgresClient.getChatwootConnection();
|
||||
|
||||
const sql = `SELECT contacts.id, contacts.identifier, contacts.phone_number
|
||||
FROM conversations
|
||||
JOIN contacts ON contacts.id = conversations.contact_id
|
||||
WHERE conversations.account_id = $1
|
||||
AND inbox_id = $2
|
||||
ORDER BY conversations.last_activity_at DESC
|
||||
LIMIT $3`;
|
||||
|
||||
return (await pgClient.query(sql, [provider.account_id, inbox.id, limit]))?.rows;
|
||||
} catch (error) {
|
||||
this.logger.error(`Error on get recent conversations: ${error.toString()}`);
|
||||
}
|
||||
}
|
||||
|
||||
public getContentMessage(chatwootService: ChatwootService, msg: IWebMessageInfo) {
|
||||
const contentMessage = chatwootService.getConversationMessage(msg.message);
|
||||
if (contentMessage) {
|
||||
return contentMessage;
|
||||
}
|
||||
|
||||
if (!configService.get<Chatwoot>('CHATWOOT').IMPORT.PLACEHOLDER_MEDIA_MESSAGE) {
|
||||
return '';
|
||||
}
|
||||
|
||||
const types = {
|
||||
documentMessage: msg.message.documentMessage,
|
||||
documentWithCaptionMessage: msg.message.documentWithCaptionMessage?.message?.documentMessage,
|
||||
imageMessage: msg.message.imageMessage,
|
||||
videoMessage: msg.message.videoMessage,
|
||||
audioMessage: msg.message.audioMessage,
|
||||
stickerMessage: msg.message.stickerMessage,
|
||||
templateMessage: msg.message.templateMessage?.hydratedTemplate?.hydratedContentText,
|
||||
};
|
||||
const typeKey = Object.keys(types).find((key) => types[key] !== undefined);
|
||||
|
||||
switch (typeKey) {
|
||||
case 'documentMessage':
|
||||
return `_<File: ${msg.message.documentMessage.fileName}${
|
||||
msg.message.documentMessage.caption ? ` ${msg.message.documentMessage.caption}` : ''
|
||||
}>_`;
|
||||
|
||||
case 'documentWithCaptionMessage':
|
||||
return `_<File: ${msg.message.documentWithCaptionMessage.message.documentMessage.fileName}${
|
||||
msg.message.documentWithCaptionMessage.message.documentMessage.caption
|
||||
? ` ${msg.message.documentWithCaptionMessage.message.documentMessage.caption}`
|
||||
: ''
|
||||
}>_`;
|
||||
|
||||
case 'templateMessage':
|
||||
return msg.message.templateMessage.hydratedTemplate.hydratedTitleText
|
||||
? `*${msg.message.templateMessage.hydratedTemplate.hydratedTitleText}*\\n`
|
||||
: '' + msg.message.templateMessage.hydratedTemplate.hydratedContentText;
|
||||
|
||||
case 'imageMessage':
|
||||
return '_<Image Message>_';
|
||||
|
||||
case 'videoMessage':
|
||||
return '_<Video Message>_';
|
||||
|
||||
case 'audioMessage':
|
||||
return '_<Audio Message>_';
|
||||
|
||||
case 'stickerMessage':
|
||||
return '_<Sticker Message>_';
|
||||
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
public sliceIntoChunks(arr: any[], chunkSize: number) {
|
||||
return arr.splice(0, chunkSize);
|
||||
}
|
||||
|
||||
public isGroup(remoteJid: string) {
|
||||
return remoteJid.includes('@g.us');
|
||||
}
|
||||
|
||||
public isIgnorePhoneNumber(remoteJid: string) {
|
||||
return this.isGroup(remoteJid) || remoteJid === 'status@broadcast' || remoteJid === '0@s.whatsapp.net';
|
||||
}
|
||||
}
|
||||
|
||||
export const chatwootImport = new ChatwootImport();
|
||||
32
src/utils/i18n.ts
Normal file
32
src/utils/i18n.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
import fs from 'fs';
|
||||
import i18next from 'i18next';
|
||||
import path from 'path';
|
||||
|
||||
import { ConfigService, Language } from '../config/env.config';
|
||||
|
||||
const languages = ['en', 'pt-BR'];
|
||||
const translationsPath = path.join(__dirname, 'translations');
|
||||
const configService: ConfigService = new ConfigService();
|
||||
|
||||
const resources: any = {};
|
||||
|
||||
languages.forEach((language) => {
|
||||
const languagePath = path.join(translationsPath, `${language}.json`);
|
||||
if (fs.existsSync(languagePath)) {
|
||||
resources[language] = {
|
||||
translation: require(languagePath),
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
i18next.init({
|
||||
resources,
|
||||
fallbackLng: 'en',
|
||||
lng: configService.get<Language>('LANGUAGE'),
|
||||
debug: false,
|
||||
|
||||
interpolation: {
|
||||
escapeValue: false,
|
||||
},
|
||||
});
|
||||
export default i18next;
|
||||
17
src/utils/makeProxyAgent.ts
Normal file
17
src/utils/makeProxyAgent.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import { HttpsProxyAgent } from 'https-proxy-agent';
|
||||
|
||||
import { wa } from '../whatsapp/types/wa.types';
|
||||
|
||||
export function makeProxyAgent(proxy: wa.Proxy | string) {
|
||||
if (typeof proxy === 'string') {
|
||||
return new HttpsProxyAgent(proxy);
|
||||
}
|
||||
|
||||
const { host, password, port, protocol, username } = proxy;
|
||||
let proxyUrl = `${protocol}://${host}:${port}`;
|
||||
|
||||
if (username && password) {
|
||||
proxyUrl = `${protocol}://${username}:${password}@${host}:${port}`;
|
||||
}
|
||||
return new HttpsProxyAgent(proxyUrl);
|
||||
}
|
||||
@@ -1,8 +1,9 @@
|
||||
import { Express } from 'express';
|
||||
import { readFileSync } from 'fs';
|
||||
import { configService, SslConf } from '../config/env.config';
|
||||
import * as https from 'https';
|
||||
import * as http from 'http';
|
||||
import * as https from 'https';
|
||||
|
||||
import { configService, SslConf } from '../config/env.config';
|
||||
|
||||
export class ServerUP {
|
||||
static #app: Express;
|
||||
|
||||
26
src/utils/translations/en.json
Normal file
26
src/utils/translations/en.json
Normal file
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"qrgeneratedsuccesfully": "QRCode successfully generated!",
|
||||
"scanqr": "Scan this QR code within the next 40 seconds.",
|
||||
"qrlimitreached": "QRCode generation limit reached, to generate a new QRCode, send the 'init' message again.",
|
||||
"numbernotinwhatsapp": "The message was not sent as the contact is not a valid Whatsapp number.",
|
||||
"cw.inbox.connected": "🚀 Connection successfully established!",
|
||||
"cw.inbox.disconnect": "🚨 Disconnecting WhatsApp from inbox *{{inboxName}}*.",
|
||||
"cw.inbox.alreadyConnected": "🚨 {{inboxName}} instance is connected.",
|
||||
"cw.inbox.clearCache": "✅ {{inboxName}} instance cache cleared.",
|
||||
"cw.inbox.notFound": "⚠️ {{inboxName}} instance not found.",
|
||||
"cw.inbox.status": "⚠️ {{inboxName}} instance status: *{{state}}*.",
|
||||
"cw.import.startImport": "💬 Starting to import messages. Please wait...",
|
||||
"cw.import.importingMessages": "💬 Importing messages. More one moment...",
|
||||
"cw.import.messagesImported": "💬 {{totalMessagesImported}} messages imported. Refresh page to see the new messages.",
|
||||
"cw.import.messagesException": "💬 Something went wrong in importing messages.",
|
||||
"cw.locationMessage.location": "Location",
|
||||
"cw.locationMessage.latitude": "Latitude",
|
||||
"cw.locationMessage.longitude": "Longitude",
|
||||
"cw.locationMessage.locationName": "Name",
|
||||
"cw.locationMessage.locationAddress": "Address",
|
||||
"cw.locationMessage.locationUrl": "URL",
|
||||
"cw.contactMessage.contact": "Contact",
|
||||
"cw.contactMessage.name": "Name",
|
||||
"cw.contactMessage.number": "Number",
|
||||
"cw.message.edited": "Edited Message"
|
||||
}
|
||||
26
src/utils/translations/pt-BR.json
Normal file
26
src/utils/translations/pt-BR.json
Normal file
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"qrgeneratedsuccesfully": "QRCode gerado com sucesso!",
|
||||
"scanqr": "Escaneie o QRCode com o WhatsApp nos próximos 40 segundos.",
|
||||
"qrlimitreached": "Limite de geração de QRCode atingido! Para gerar um novo QRCode, envie o texto 'init' nesta conversa.",
|
||||
"numbernotinwhatsapp": "A mensagem não foi enviada, pois o contato não é um número válido do WhatsApp.",
|
||||
"cw.inbox.connected": "🚀 Conectado com sucesso!",
|
||||
"cw.inbox.disconnect": "🚨 Instância *{{inboxName}}* desconectada do WhatsApp.",
|
||||
"cw.inbox.alreadyConnected": "🚨 Instância *{{inboxName}}* já está conectada.",
|
||||
"cw.inbox.clearCache": "✅ Instância *{{inboxName}}* cache removido.",
|
||||
"cw.inbox.notFound": "⚠️ Instância *{{inboxName}}* não encontrada.",
|
||||
"cw.inbox.status": "⚠️ Status da instância {{inboxName}}: *{{state}}*.",
|
||||
"cw.import.startImport": "💬 Iniciando importação de mensagens. Por favor, aguarde...",
|
||||
"cw.import.importingMessages": "💬 Importando mensagens. Mais um momento...",
|
||||
"cw.import.messagesImported": "💬 {{totalMessagesImported}} mensagens importadas. Atualize a página para ver as novas mensagens.",
|
||||
"cw.import.messagesException": "💬 Não foi possível importar as mensagens.",
|
||||
"cw.locationMessage.location": "Localização",
|
||||
"cw.locationMessage.latitude": "Latitude",
|
||||
"cw.locationMessage.longitude": "Longitude",
|
||||
"cw.locationMessage.locationName": "Nome",
|
||||
"cw.locationMessage.locationAddress": "Endereço",
|
||||
"cw.locationMessage.locationUrl": "URL",
|
||||
"cw.contactMessage.contact": "Contato",
|
||||
"cw.contactMessage.name": "Nome",
|
||||
"cw.contactMessage.number": "Número",
|
||||
"cw.message.edited": "Mensagem editada"
|
||||
}
|
||||
@@ -6,9 +6,10 @@ import {
|
||||
proto,
|
||||
SignalDataTypeMap,
|
||||
} from '@whiskeysockets/baileys';
|
||||
|
||||
import { configService, Database } from '../config/env.config';
|
||||
import { Logger } from '../config/logger.config';
|
||||
import { dbserver } from '../db/db.connect';
|
||||
import { dbserver } from '../libs/db.connect';
|
||||
|
||||
export async function useMultiFileAuthStateDb(
|
||||
coll: string,
|
||||
@@ -24,28 +25,42 @@ export async function useMultiFileAuthStateDb(
|
||||
const writeData = async (data: any, key: string): Promise<any> => {
|
||||
try {
|
||||
await client.connect();
|
||||
return await collection.replaceOne(
|
||||
{ _id: key },
|
||||
JSON.parse(JSON.stringify(data, BufferJSON.replacer)),
|
||||
{ upsert: true },
|
||||
);
|
||||
} catch {}
|
||||
let msgParsed = JSON.parse(JSON.stringify(data, BufferJSON.replacer));
|
||||
if (Array.isArray(msgParsed)) {
|
||||
msgParsed = {
|
||||
_id: key,
|
||||
content_array: msgParsed,
|
||||
};
|
||||
}
|
||||
return await collection.replaceOne({ _id: key }, msgParsed, {
|
||||
upsert: true,
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error(error);
|
||||
}
|
||||
};
|
||||
|
||||
const readData = async (key: string): Promise<any> => {
|
||||
try {
|
||||
await client.connect();
|
||||
const data = await collection.findOne({ _id: key });
|
||||
let data = (await collection.findOne({ _id: key })) as any;
|
||||
if (data?.content_array) {
|
||||
data = data.content_array;
|
||||
}
|
||||
const creds = JSON.stringify(data);
|
||||
return JSON.parse(creds, BufferJSON.reviver);
|
||||
} catch {}
|
||||
} catch (error) {
|
||||
logger.error(error);
|
||||
}
|
||||
};
|
||||
|
||||
const removeData = async (key: string) => {
|
||||
try {
|
||||
await client.connect();
|
||||
return await collection.deleteOne({ _id: key });
|
||||
} catch {}
|
||||
} catch (error) {
|
||||
logger.error(error);
|
||||
}
|
||||
};
|
||||
|
||||
const creds: AuthenticationCreds = (await readData('creds')) || initAuthCreds();
|
||||
@@ -86,7 +101,7 @@ export async function useMultiFileAuthStateDb(
|
||||
},
|
||||
},
|
||||
saveCreds: async () => {
|
||||
return writeData(creds, 'creds');
|
||||
return await writeData(creds, 'creds');
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -5,9 +5,9 @@ import {
|
||||
proto,
|
||||
SignalDataTypeMap,
|
||||
} from '@whiskeysockets/baileys';
|
||||
import { RedisCache } from '../db/redis.client';
|
||||
|
||||
import { Logger } from '../config/logger.config';
|
||||
import { Redis } from '../config/env.config';
|
||||
import { RedisCache } from '../libs/redis.client';
|
||||
|
||||
export async function useMultiFileAuthStateRedisDb(cache: RedisCache): Promise<{
|
||||
state: AuthenticationState;
|
||||
|
||||
@@ -39,6 +39,7 @@ export const instanceNameSchema: JSONSchema7 = {
|
||||
'MESSAGES_SET',
|
||||
'MESSAGES_UPSERT',
|
||||
'MESSAGES_UPDATE',
|
||||
'MESSAGES_DELETE',
|
||||
'SEND_MESSAGE',
|
||||
'CONTACTS_SET',
|
||||
'CONTACTS_UPSERT',
|
||||
@@ -52,11 +53,18 @@ export const instanceNameSchema: JSONSchema7 = {
|
||||
'GROUP_UPDATE',
|
||||
'GROUP_PARTICIPANTS_UPDATE',
|
||||
'CONNECTION_UPDATE',
|
||||
'LABELS_EDIT',
|
||||
'LABELS_ASSOCIATION',
|
||||
'CALL',
|
||||
'NEW_JWT_TOKEN',
|
||||
'TYPEBOT_START',
|
||||
'TYPEBOT_CHANGE_STATUS',
|
||||
'CHAMA_AI_ACTION',
|
||||
],
|
||||
},
|
||||
},
|
||||
qrcode: { type: 'boolean', enum: [true, false] },
|
||||
number: { type: 'string', pattern: '^\\d+[\\.@\\w-]+' },
|
||||
token: { type: 'string' },
|
||||
},
|
||||
...isNotEmpty('instanceName'),
|
||||
@@ -81,8 +89,8 @@ const quotedOptionsSchema: JSONSchema7 = {
|
||||
remoteJid: { type: 'string' },
|
||||
fromMe: { type: 'boolean', enum: [true, false] },
|
||||
},
|
||||
required: ['id', 'remoteJid', 'fromMe'],
|
||||
...isNotEmpty('id', 'remoteJid'),
|
||||
required: ['id'],
|
||||
...isNotEmpty('id'),
|
||||
},
|
||||
message: { type: 'object' },
|
||||
},
|
||||
@@ -122,7 +130,6 @@ const optionsSchema: JSONSchema7 = {
|
||||
|
||||
const numberDefinition: JSONSchema7Definition = {
|
||||
type: 'string',
|
||||
pattern: '^\\d+[\\.@\\w-]+',
|
||||
description: 'Invalid format',
|
||||
};
|
||||
|
||||
@@ -144,22 +151,26 @@ export const textMessageSchema: JSONSchema7 = {
|
||||
required: ['textMessage', 'number'],
|
||||
};
|
||||
|
||||
export const linkPreviewSchema: JSONSchema7 = {
|
||||
export const presenceSchema: JSONSchema7 = {
|
||||
$id: v4(),
|
||||
type: 'object',
|
||||
properties: {
|
||||
number: { ...numberDefinition },
|
||||
options: { ...optionsSchema },
|
||||
linkPreview: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
text: { type: 'string' },
|
||||
},
|
||||
required: ['text'],
|
||||
...isNotEmpty('text'),
|
||||
options: { ...optionsSchema, required: ['presence', 'delay'] },
|
||||
},
|
||||
required: ['options', 'number'],
|
||||
};
|
||||
|
||||
export const presenceOnlySchema: JSONSchema7 = {
|
||||
$id: v4(),
|
||||
type: 'object',
|
||||
properties: {
|
||||
presence: {
|
||||
type: 'string',
|
||||
enum: ['unavailable', 'available', 'composing', 'recording', 'paused'],
|
||||
},
|
||||
},
|
||||
required: ['linkPreview', 'number'],
|
||||
required: ['presence'],
|
||||
};
|
||||
|
||||
export const pollMessageSchema: JSONSchema7 = {
|
||||
@@ -278,6 +289,26 @@ export const audioMessageSchema: JSONSchema7 = {
|
||||
required: ['audioMessage', 'number'],
|
||||
};
|
||||
|
||||
export const templateMessageSchema: JSONSchema7 = {
|
||||
$id: v4(),
|
||||
type: 'object',
|
||||
properties: {
|
||||
number: { ...numberDefinition },
|
||||
options: { ...optionsSchema },
|
||||
templateMessage: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
name: { type: 'string' },
|
||||
language: { type: 'string' },
|
||||
components: { type: 'array' },
|
||||
},
|
||||
required: ['name', 'language'],
|
||||
...isNotEmpty('name', 'language'),
|
||||
},
|
||||
},
|
||||
required: ['templateMessage', 'number'],
|
||||
};
|
||||
|
||||
export const buttonMessageSchema: JSONSchema7 = {
|
||||
$id: v4(),
|
||||
type: 'object',
|
||||
@@ -375,7 +406,7 @@ export const listMessageSchema: JSONSchema7 = {
|
||||
description: { type: 'string' },
|
||||
rowId: { type: 'string' },
|
||||
},
|
||||
required: ['title', 'description', 'rowId'],
|
||||
required: ['title', 'rowId'],
|
||||
...isNotEmpty('title', 'description', 'rowId'),
|
||||
},
|
||||
},
|
||||
@@ -415,7 +446,7 @@ export const contactMessageSchema: JSONSchema7 = {
|
||||
email: { type: 'string' },
|
||||
url: { type: 'string' },
|
||||
},
|
||||
required: ['fullName', 'wuid', 'phoneNumber'],
|
||||
required: ['fullName', 'phoneNumber'],
|
||||
...isNotEmpty('fullName'),
|
||||
},
|
||||
minItems: 1,
|
||||
@@ -462,7 +493,6 @@ export const whatsappNumberSchema: JSONSchema7 = {
|
||||
uniqueItems: true,
|
||||
items: {
|
||||
type: 'string',
|
||||
pattern: '^\\d+',
|
||||
description: '"numbers" must be an array of numeric strings',
|
||||
},
|
||||
},
|
||||
@@ -473,7 +503,7 @@ export const readMessageSchema: JSONSchema7 = {
|
||||
$id: v4(),
|
||||
type: 'object',
|
||||
properties: {
|
||||
readMessages: {
|
||||
read_messages: {
|
||||
type: 'array',
|
||||
minItems: 1,
|
||||
uniqueItems: true,
|
||||
@@ -488,7 +518,7 @@ export const readMessageSchema: JSONSchema7 = {
|
||||
},
|
||||
},
|
||||
},
|
||||
required: ['readMessages'],
|
||||
required: ['read_messages'],
|
||||
};
|
||||
|
||||
export const privacySettingsSchema: JSONSchema7 = {
|
||||
@@ -521,10 +551,22 @@ export const privacySettingsSchema: JSONSchema7 = {
|
||||
required: ['privacySettings'],
|
||||
};
|
||||
|
||||
export const blockUserSchema: JSONSchema7 = {
|
||||
$id: v4(),
|
||||
type: 'object',
|
||||
properties: {
|
||||
number: { type: 'string' },
|
||||
status: { type: 'string', enum: ['block', 'unblock'] },
|
||||
},
|
||||
required: ['number', 'status'],
|
||||
...isNotEmpty('number', 'status'),
|
||||
};
|
||||
|
||||
export const archiveChatSchema: JSONSchema7 = {
|
||||
$id: v4(),
|
||||
type: 'object',
|
||||
properties: {
|
||||
chat: { type: 'string' },
|
||||
lastMessage: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
@@ -545,7 +587,7 @@ export const archiveChatSchema: JSONSchema7 = {
|
||||
},
|
||||
archive: { type: 'boolean', enum: [true, false] },
|
||||
},
|
||||
required: ['lastMessage', 'archive'],
|
||||
required: ['archive'],
|
||||
};
|
||||
|
||||
export const deleteMessageSchema: JSONSchema7 = {
|
||||
@@ -595,6 +637,26 @@ export const profileStatusSchema: JSONSchema7 = {
|
||||
...isNotEmpty('status'),
|
||||
};
|
||||
|
||||
export const updateMessageSchema: JSONSchema7 = {
|
||||
$id: v4(),
|
||||
type: 'object',
|
||||
properties: {
|
||||
number: { type: 'string' },
|
||||
text: { type: 'string' },
|
||||
key: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: { type: 'string' },
|
||||
remoteJid: { type: 'string' },
|
||||
fromMe: { type: 'boolean', enum: [true, false] },
|
||||
},
|
||||
required: ['id', 'fromMe', 'remoteJid'],
|
||||
...isNotEmpty('id', 'remoteJid'),
|
||||
},
|
||||
},
|
||||
...isNotEmpty('number', 'text', 'key'),
|
||||
};
|
||||
|
||||
export const profilePictureSchema: JSONSchema7 = {
|
||||
$id: v4(),
|
||||
type: 'object',
|
||||
@@ -604,6 +666,17 @@ export const profilePictureSchema: JSONSchema7 = {
|
||||
},
|
||||
};
|
||||
|
||||
export const profileSchema: JSONSchema7 = {
|
||||
type: 'object',
|
||||
properties: {
|
||||
wuid: { type: 'string' },
|
||||
name: { type: 'string' },
|
||||
picture: { type: 'string' },
|
||||
status: { type: 'string' },
|
||||
isBusiness: { type: 'boolean' },
|
||||
},
|
||||
};
|
||||
|
||||
export const messageValidateSchema: JSONSchema7 = {
|
||||
$id: v4(),
|
||||
type: 'object',
|
||||
@@ -674,6 +747,7 @@ export const createGroupSchema: JSONSchema7 = {
|
||||
subject: { type: 'string' },
|
||||
description: { type: 'string' },
|
||||
profilePicture: { type: 'string' },
|
||||
promoteParticipants: { type: 'boolean', enum: [true, false] },
|
||||
participants: {
|
||||
type: 'array',
|
||||
minItems: 1,
|
||||
@@ -742,6 +816,16 @@ export const groupInviteSchema: JSONSchema7 = {
|
||||
...isNotEmpty('inviteCode'),
|
||||
};
|
||||
|
||||
export const AcceptGroupInviteSchema: JSONSchema7 = {
|
||||
$id: v4(),
|
||||
type: 'object',
|
||||
properties: {
|
||||
inviteCode: { type: 'string', pattern: '^[a-zA-Z0-9]{22}$' },
|
||||
},
|
||||
required: ['inviteCode'],
|
||||
...isNotEmpty('inviteCode'),
|
||||
};
|
||||
|
||||
export const updateParticipantsSchema: JSONSchema7 = {
|
||||
$id: v4(),
|
||||
type: 'object',
|
||||
@@ -828,13 +912,11 @@ export const updateGroupDescriptionSchema: JSONSchema7 = {
|
||||
...isNotEmpty('groupJid', 'description'),
|
||||
};
|
||||
|
||||
// Webhook Schema
|
||||
export const webhookSchema: JSONSchema7 = {
|
||||
$id: v4(),
|
||||
type: 'object',
|
||||
properties: {
|
||||
url: { type: 'string' },
|
||||
enabled: { type: 'boolean', enum: [true, false] },
|
||||
events: {
|
||||
type: 'array',
|
||||
minItems: 0,
|
||||
@@ -846,6 +928,7 @@ export const webhookSchema: JSONSchema7 = {
|
||||
'MESSAGES_SET',
|
||||
'MESSAGES_UPSERT',
|
||||
'MESSAGES_UPDATE',
|
||||
'MESSAGES_DELETE',
|
||||
'SEND_MESSAGE',
|
||||
'CONTACTS_SET',
|
||||
'CONTACTS_UPSERT',
|
||||
@@ -859,12 +942,18 @@ export const webhookSchema: JSONSchema7 = {
|
||||
'GROUP_UPDATE',
|
||||
'GROUP_PARTICIPANTS_UPDATE',
|
||||
'CONNECTION_UPDATE',
|
||||
'LABELS_EDIT',
|
||||
'LABELS_ASSOCIATION',
|
||||
'CALL',
|
||||
'NEW_JWT_TOKEN',
|
||||
'TYPEBOT_START',
|
||||
'TYPEBOT_CHANGE_STATUS',
|
||||
'CHAMA_AI_ACTION',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
required: ['url', 'enabled'],
|
||||
required: ['url'],
|
||||
...isNotEmpty('url'),
|
||||
};
|
||||
|
||||
@@ -877,7 +966,251 @@ export const chatwootSchema: JSONSchema7 = {
|
||||
token: { type: 'string' },
|
||||
url: { type: 'string' },
|
||||
sign_msg: { type: 'boolean', enum: [true, false] },
|
||||
sign_delimiter: { type: ['string', 'null'] },
|
||||
reopen_conversation: { type: 'boolean', enum: [true, false] },
|
||||
conversation_pending: { type: 'boolean', enum: [true, false] },
|
||||
auto_create: { type: 'boolean', enum: [true, false] },
|
||||
import_contacts: { type: 'boolean', enum: [true, false] },
|
||||
import_messages: { type: 'boolean', enum: [true, false] },
|
||||
days_limit_import_messages: { type: 'number' },
|
||||
},
|
||||
required: ['enabled', 'account_id', 'token', 'url', 'sign_msg'],
|
||||
...isNotEmpty('account_id', 'token', 'url', 'sign_msg'),
|
||||
required: ['enabled', 'account_id', 'token', 'url', 'sign_msg', 'reopen_conversation', 'conversation_pending'],
|
||||
...isNotEmpty('account_id', 'token', 'url', 'sign_msg', 'reopen_conversation', 'conversation_pending'),
|
||||
};
|
||||
|
||||
export const settingsSchema: JSONSchema7 = {
|
||||
$id: v4(),
|
||||
type: 'object',
|
||||
properties: {
|
||||
reject_call: { type: 'boolean', enum: [true, false] },
|
||||
msg_call: { type: 'string' },
|
||||
groups_ignore: { type: 'boolean', enum: [true, false] },
|
||||
always_online: { type: 'boolean', enum: [true, false] },
|
||||
read_messages: { type: 'boolean', enum: [true, false] },
|
||||
read_status: { type: 'boolean', enum: [true, false] },
|
||||
sync_full_history: { type: 'boolean', enum: [true, false] },
|
||||
},
|
||||
required: ['reject_call', 'groups_ignore', 'always_online', 'read_messages', 'read_status', 'sync_full_history'],
|
||||
...isNotEmpty('reject_call', 'groups_ignore', 'always_online', 'read_messages', 'read_status', 'sync_full_history'),
|
||||
};
|
||||
|
||||
export const websocketSchema: JSONSchema7 = {
|
||||
$id: v4(),
|
||||
type: 'object',
|
||||
properties: {
|
||||
enabled: { type: 'boolean', enum: [true, false] },
|
||||
events: {
|
||||
type: 'array',
|
||||
minItems: 0,
|
||||
items: {
|
||||
type: 'string',
|
||||
enum: [
|
||||
'APPLICATION_STARTUP',
|
||||
'QRCODE_UPDATED',
|
||||
'MESSAGES_SET',
|
||||
'MESSAGES_UPSERT',
|
||||
'MESSAGES_UPDATE',
|
||||
'MESSAGES_DELETE',
|
||||
'SEND_MESSAGE',
|
||||
'CONTACTS_SET',
|
||||
'CONTACTS_UPSERT',
|
||||
'CONTACTS_UPDATE',
|
||||
'PRESENCE_UPDATE',
|
||||
'CHATS_SET',
|
||||
'CHATS_UPSERT',
|
||||
'CHATS_UPDATE',
|
||||
'CHATS_DELETE',
|
||||
'GROUPS_UPSERT',
|
||||
'GROUP_UPDATE',
|
||||
'GROUP_PARTICIPANTS_UPDATE',
|
||||
'CONNECTION_UPDATE',
|
||||
'LABELS_EDIT',
|
||||
'LABELS_ASSOCIATION',
|
||||
'CALL',
|
||||
'NEW_JWT_TOKEN',
|
||||
'TYPEBOT_START',
|
||||
'TYPEBOT_CHANGE_STATUS',
|
||||
'CHAMA_AI_ACTION',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
required: ['enabled'],
|
||||
...isNotEmpty('enabled'),
|
||||
};
|
||||
|
||||
export const rabbitmqSchema: JSONSchema7 = {
|
||||
$id: v4(),
|
||||
type: 'object',
|
||||
properties: {
|
||||
enabled: { type: 'boolean', enum: [true, false] },
|
||||
events: {
|
||||
type: 'array',
|
||||
minItems: 0,
|
||||
items: {
|
||||
type: 'string',
|
||||
enum: [
|
||||
'APPLICATION_STARTUP',
|
||||
'QRCODE_UPDATED',
|
||||
'MESSAGES_SET',
|
||||
'MESSAGES_UPSERT',
|
||||
'MESSAGES_UPDATE',
|
||||
'MESSAGES_DELETE',
|
||||
'SEND_MESSAGE',
|
||||
'CONTACTS_SET',
|
||||
'CONTACTS_UPSERT',
|
||||
'CONTACTS_UPDATE',
|
||||
'PRESENCE_UPDATE',
|
||||
'CHATS_SET',
|
||||
'CHATS_UPSERT',
|
||||
'CHATS_UPDATE',
|
||||
'CHATS_DELETE',
|
||||
'GROUPS_UPSERT',
|
||||
'GROUP_UPDATE',
|
||||
'GROUP_PARTICIPANTS_UPDATE',
|
||||
'CONNECTION_UPDATE',
|
||||
'LABELS_EDIT',
|
||||
'LABELS_ASSOCIATION',
|
||||
'CALL',
|
||||
'NEW_JWT_TOKEN',
|
||||
'TYPEBOT_START',
|
||||
'TYPEBOT_CHANGE_STATUS',
|
||||
'CHAMA_AI_ACTION',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
required: ['enabled'],
|
||||
...isNotEmpty('enabled'),
|
||||
};
|
||||
|
||||
export const sqsSchema: JSONSchema7 = {
|
||||
$id: v4(),
|
||||
type: 'object',
|
||||
properties: {
|
||||
enabled: { type: 'boolean', enum: [true, false] },
|
||||
events: {
|
||||
type: 'array',
|
||||
minItems: 0,
|
||||
items: {
|
||||
type: 'string',
|
||||
enum: [
|
||||
'APPLICATION_STARTUP',
|
||||
'QRCODE_UPDATED',
|
||||
'MESSAGES_SET',
|
||||
'MESSAGES_UPSERT',
|
||||
'MESSAGES_UPDATE',
|
||||
'MESSAGES_DELETE',
|
||||
'SEND_MESSAGE',
|
||||
'CONTACTS_SET',
|
||||
'CONTACTS_UPSERT',
|
||||
'CONTACTS_UPDATE',
|
||||
'PRESENCE_UPDATE',
|
||||
'CHATS_SET',
|
||||
'CHATS_UPSERT',
|
||||
'CHATS_UPDATE',
|
||||
'CHATS_DELETE',
|
||||
'GROUPS_UPSERT',
|
||||
'GROUP_UPDATE',
|
||||
'GROUP_PARTICIPANTS_UPDATE',
|
||||
'CONNECTION_UPDATE',
|
||||
'LABELS_EDIT',
|
||||
'LABELS_ASSOCIATION',
|
||||
'CALL',
|
||||
'NEW_JWT_TOKEN',
|
||||
'TYPEBOT_START',
|
||||
'TYPEBOT_CHANGE_STATUS',
|
||||
'CHAMA_AI_ACTION',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
required: ['enabled'],
|
||||
...isNotEmpty('enabled'),
|
||||
};
|
||||
|
||||
export const typebotSchema: JSONSchema7 = {
|
||||
$id: v4(),
|
||||
type: 'object',
|
||||
properties: {
|
||||
enabled: { type: 'boolean', enum: [true, false] },
|
||||
url: { type: 'string' },
|
||||
typebot: { type: 'string' },
|
||||
expire: { type: 'integer' },
|
||||
delay_message: { type: 'integer' },
|
||||
unknown_message: { type: 'string' },
|
||||
listening_from_me: { type: 'boolean', enum: [true, false] },
|
||||
},
|
||||
required: ['enabled', 'url', 'typebot', 'expire', 'delay_message', 'unknown_message', 'listening_from_me'],
|
||||
...isNotEmpty('enabled', 'url', 'typebot', 'expire', 'delay_message', 'unknown_message', 'listening_from_me'),
|
||||
};
|
||||
|
||||
export const typebotStatusSchema: JSONSchema7 = {
|
||||
$id: v4(),
|
||||
type: 'object',
|
||||
properties: {
|
||||
remoteJid: { type: 'string' },
|
||||
status: { type: 'string', enum: ['opened', 'closed', 'paused'] },
|
||||
},
|
||||
required: ['remoteJid', 'status'],
|
||||
...isNotEmpty('remoteJid', 'status'),
|
||||
};
|
||||
|
||||
export const typebotStartSchema: JSONSchema7 = {
|
||||
$id: v4(),
|
||||
type: 'object',
|
||||
properties: {
|
||||
remoteJid: { type: 'string' },
|
||||
url: { type: 'string' },
|
||||
typebot: { type: 'string' },
|
||||
},
|
||||
required: ['remoteJid', 'url', 'typebot'],
|
||||
...isNotEmpty('remoteJid', 'url', 'typebot'),
|
||||
};
|
||||
|
||||
export const proxySchema: JSONSchema7 = {
|
||||
$id: v4(),
|
||||
type: 'object',
|
||||
properties: {
|
||||
enabled: { type: 'boolean', enum: [true, false] },
|
||||
proxy: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
host: { type: 'string' },
|
||||
port: { type: 'string' },
|
||||
protocol: { type: 'string' },
|
||||
username: { type: 'string' },
|
||||
password: { type: 'string' },
|
||||
},
|
||||
required: ['host', 'port', 'protocol'],
|
||||
...isNotEmpty('host', 'port', 'protocol'),
|
||||
},
|
||||
},
|
||||
required: ['enabled', 'proxy'],
|
||||
...isNotEmpty('enabled', 'proxy'),
|
||||
};
|
||||
|
||||
export const chamaaiSchema: JSONSchema7 = {
|
||||
$id: v4(),
|
||||
type: 'object',
|
||||
properties: {
|
||||
enabled: { type: 'boolean', enum: [true, false] },
|
||||
url: { type: 'string' },
|
||||
token: { type: 'string' },
|
||||
waNumber: { type: 'string' },
|
||||
answerByAudio: { type: 'boolean', enum: [true, false] },
|
||||
},
|
||||
required: ['enabled', 'url', 'token', 'waNumber', 'answerByAudio'],
|
||||
...isNotEmpty('enabled', 'url', 'token', 'waNumber', 'answerByAudio'),
|
||||
};
|
||||
|
||||
export const handleLabelSchema: JSONSchema7 = {
|
||||
$id: v4(),
|
||||
type: 'object',
|
||||
properties: {
|
||||
number: { ...numberDefinition },
|
||||
labelId: { type: 'string' },
|
||||
action: { type: 'string', enum: ['add', 'remove'] },
|
||||
},
|
||||
required: ['number', 'labelId', 'action'],
|
||||
};
|
||||
|
||||
13
src/whatsapp/abstract/abstract.cache.ts
Normal file
13
src/whatsapp/abstract/abstract.cache.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
export interface ICache {
|
||||
get(key: string): Promise<any>;
|
||||
|
||||
set(key: string, value: any, ttl?: number): void;
|
||||
|
||||
has(key: string): Promise<boolean>;
|
||||
|
||||
keys(appendCriteria?: string): Promise<string[]>;
|
||||
|
||||
delete(key: string | string[]): Promise<number>;
|
||||
|
||||
deleteAll(appendCriteria?: string): Promise<number>;
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
import { existsSync, mkdirSync, writeFileSync } from 'fs';
|
||||
import { join } from 'path';
|
||||
|
||||
import { ConfigService, Database } from '../../config/env.config';
|
||||
import { ROOT_DIR } from '../../config/path.config';
|
||||
|
||||
@@ -34,11 +35,9 @@ export abstract class Repository implements IRepository {
|
||||
mkdirSync(create.path, { recursive: true });
|
||||
}
|
||||
try {
|
||||
writeFileSync(
|
||||
join(create.path, create.fileName + '.json'),
|
||||
JSON.stringify({ ...create.data }),
|
||||
{ encoding: 'utf-8' },
|
||||
);
|
||||
writeFileSync(join(create.path, create.fileName + '.json'), JSON.stringify({ ...create.data }), {
|
||||
encoding: 'utf-8',
|
||||
});
|
||||
|
||||
return { message: 'create - success' };
|
||||
} finally {
|
||||
@@ -46,19 +45,23 @@ export abstract class Repository implements IRepository {
|
||||
}
|
||||
};
|
||||
|
||||
public insert(data: any, instanceName: string, saveDb = false): Promise<IInsert> {
|
||||
// eslint-disable-next-line
|
||||
public insert(data: any, instanceName: string, saveDb = false): Promise<IInsert> {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
|
||||
public update(data: any, instanceName: string, saveDb = false): Promise<IInsert> {
|
||||
// eslint-disable-next-line
|
||||
public update(data: any, instanceName: string, saveDb = false): Promise<IInsert> {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
|
||||
public find(query: any): Promise<any> {
|
||||
// eslint-disable-next-line
|
||||
public find(query: any): Promise<any> {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
|
||||
delete(query: any, force?: boolean): Promise<any> {
|
||||
// eslint-disable-next-line
|
||||
delete(query: any, force?: boolean): Promise<any> {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
import { InstanceDto } from '../dto/instance.dto';
|
||||
import { JSONSchema7 } from 'json-schema';
|
||||
import { Request } from 'express';
|
||||
import { validate } from 'jsonschema';
|
||||
import { BadRequestException } from '../../exceptions';
|
||||
import 'express-async-errors';
|
||||
|
||||
import { Request } from 'express';
|
||||
import { JSONSchema7 } from 'json-schema';
|
||||
import { validate } from 'jsonschema';
|
||||
|
||||
import { Logger } from '../../config/logger.config';
|
||||
import { GetParticipant, GroupInvite, GroupJid } from '../dto/group.dto';
|
||||
import { BadRequestException } from '../../exceptions';
|
||||
import { GetParticipant, GroupInvite } from '../dto/group.dto';
|
||||
import { InstanceDto } from '../dto/instance.dto';
|
||||
|
||||
type DataValidate<T> = {
|
||||
request: Request;
|
||||
@@ -19,7 +21,6 @@ const logger = new Logger('Validate');
|
||||
export abstract class RouterBroker {
|
||||
constructor() {}
|
||||
public routerPath(path: string, param = true) {
|
||||
// const route = param ? '/:instanceName/' + path : '/' + path;
|
||||
let route = '/' + path;
|
||||
param ? (route += '/:instanceName') : null;
|
||||
|
||||
@@ -46,20 +47,17 @@ export abstract class RouterBroker {
|
||||
const v = schema ? validate(ref, schema) : { valid: true, errors: [] };
|
||||
|
||||
if (!v.valid) {
|
||||
const message: any[] = v.errors.map(({ property, stack, schema }) => {
|
||||
const message: any[] = v.errors.map(({ stack, schema }) => {
|
||||
let message: string;
|
||||
if (schema['description']) {
|
||||
message = schema['description'];
|
||||
} else {
|
||||
message = stack.replace('instance.', '');
|
||||
}
|
||||
return {
|
||||
property: property.replace('instance.', ''),
|
||||
message,
|
||||
};
|
||||
return message;
|
||||
});
|
||||
logger.error([...message]);
|
||||
throw new BadRequestException(...message);
|
||||
logger.error(message);
|
||||
throw new BadRequestException(message);
|
||||
}
|
||||
|
||||
return await execute(instance, ref);
|
||||
@@ -99,21 +97,29 @@ export abstract class RouterBroker {
|
||||
public async groupValidate<T>(args: DataValidate<T>) {
|
||||
const { request, ClassRef, schema, execute } = args;
|
||||
|
||||
const groupJid = request.query as unknown as GroupJid;
|
||||
|
||||
if (!groupJid?.groupJid) {
|
||||
throw new BadRequestException(
|
||||
'The group id needs to be informed in the query',
|
||||
'ex: "groupJid=120362@g.us"',
|
||||
);
|
||||
}
|
||||
|
||||
const instance = request.params as unknown as InstanceDto;
|
||||
const body = request.body;
|
||||
|
||||
let groupJid = body?.groupJid;
|
||||
|
||||
if (!groupJid) {
|
||||
if (request.query?.groupJid) {
|
||||
groupJid = request.query.groupJid;
|
||||
} else {
|
||||
throw new BadRequestException('The group id needs to be informed in the query', 'ex: "groupJid=120362@g.us"');
|
||||
}
|
||||
}
|
||||
|
||||
if (!groupJid.endsWith('@g.us')) {
|
||||
groupJid = groupJid + '@g.us';
|
||||
}
|
||||
|
||||
Object.assign(body, {
|
||||
groupJid: groupJid,
|
||||
});
|
||||
|
||||
const ref = new ClassRef();
|
||||
|
||||
Object.assign(body, groupJid);
|
||||
Object.assign(ref, body);
|
||||
|
||||
const v = validate(ref, schema);
|
||||
@@ -160,8 +166,6 @@ export abstract class RouterBroker {
|
||||
|
||||
const v = validate(ref, schema);
|
||||
|
||||
console.log(v, '@checkei aqui');
|
||||
|
||||
if (!v.valid) {
|
||||
const message: any[] = v.errors.map(({ property, stack, schema }) => {
|
||||
let message: string;
|
||||
@@ -188,9 +192,7 @@ export abstract class RouterBroker {
|
||||
const getParticipants = request.query as unknown as GetParticipant;
|
||||
|
||||
if (!getParticipants?.getParticipants) {
|
||||
throw new BadRequestException(
|
||||
'The getParticipants needs to be informed in the query',
|
||||
);
|
||||
throw new BadRequestException('The getParticipants needs to be informed in the query');
|
||||
}
|
||||
|
||||
const instance = request.params as unknown as InstanceDto;
|
||||
@@ -203,8 +205,6 @@ export abstract class RouterBroker {
|
||||
|
||||
const v = validate(ref, schema);
|
||||
|
||||
console.log(v, '@checkei aqui');
|
||||
|
||||
if (!v.valid) {
|
||||
const message: any[] = v.errors.map(({ property, stack, schema }) => {
|
||||
let message: string;
|
||||
|
||||
29
src/whatsapp/controllers/chamaai.controller.ts
Normal file
29
src/whatsapp/controllers/chamaai.controller.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import { Logger } from '../../config/logger.config';
|
||||
import { ChamaaiDto } from '../dto/chamaai.dto';
|
||||
import { InstanceDto } from '../dto/instance.dto';
|
||||
import { ChamaaiService } from '../services/chamaai.service';
|
||||
|
||||
const logger = new Logger('ChamaaiController');
|
||||
|
||||
export class ChamaaiController {
|
||||
constructor(private readonly chamaaiService: ChamaaiService) {}
|
||||
|
||||
public async createChamaai(instance: InstanceDto, data: ChamaaiDto) {
|
||||
logger.verbose('requested createChamaai from ' + instance.instanceName + ' instance');
|
||||
|
||||
if (!data.enabled) {
|
||||
logger.verbose('chamaai disabled');
|
||||
data.url = '';
|
||||
data.token = '';
|
||||
data.waNumber = '';
|
||||
data.answerByAudio = false;
|
||||
}
|
||||
|
||||
return this.chamaaiService.create(instance, data);
|
||||
}
|
||||
|
||||
public async findChamaai(instance: InstanceDto) {
|
||||
logger.verbose('requested findChamaai from ' + instance.instanceName + ' instance');
|
||||
return this.chamaaiService.find(instance);
|
||||
}
|
||||
}
|
||||
@@ -1,22 +1,24 @@
|
||||
import { proto } from '@whiskeysockets/baileys';
|
||||
import { Logger } from '../../config/logger.config';
|
||||
import {
|
||||
ArchiveChatDto,
|
||||
BlockUserDto,
|
||||
DeleteMessage,
|
||||
getBase64FromMediaMessageDto,
|
||||
NumberDto,
|
||||
PrivacySettingDto,
|
||||
ProfileNameDto,
|
||||
ProfilePictureDto,
|
||||
ProfileStatusDto,
|
||||
ReadMessageDto,
|
||||
SendPresenceDto,
|
||||
UpdateMessageDto,
|
||||
WhatsAppNumberDto,
|
||||
getBase64FromMediaMessageDto,
|
||||
} from '../dto/chat.dto';
|
||||
import { InstanceDto } from '../dto/instance.dto';
|
||||
import { ContactQuery } from '../repository/contact.repository';
|
||||
import { MessageQuery } from '../repository/message.repository';
|
||||
import { MessageUpQuery } from '../repository/messageUp.repository';
|
||||
import { WAMonitoringService } from '../services/monitor.service';
|
||||
import { Logger } from '../../config/logger.config';
|
||||
|
||||
const logger = new Logger('ChatController');
|
||||
|
||||
@@ -48,18 +50,18 @@ export class ChatController {
|
||||
return await this.waMonitor.waInstances[instanceName].profilePicture(data.number);
|
||||
}
|
||||
|
||||
public async fetchProfile({ instanceName }: InstanceDto, data: NumberDto) {
|
||||
logger.verbose('requested fetchProfile from ' + instanceName + ' instance');
|
||||
return await this.waMonitor.waInstances[instanceName].fetchProfile(instanceName, data.number);
|
||||
}
|
||||
|
||||
public async fetchContacts({ instanceName }: InstanceDto, query: ContactQuery) {
|
||||
logger.verbose('requested fetchContacts from ' + instanceName + ' instance');
|
||||
return await this.waMonitor.waInstances[instanceName].fetchContacts(query);
|
||||
}
|
||||
|
||||
public async getBase64FromMediaMessage(
|
||||
{ instanceName }: InstanceDto,
|
||||
data: getBase64FromMediaMessageDto,
|
||||
) {
|
||||
logger.verbose(
|
||||
'requested getBase64FromMediaMessage from ' + instanceName + ' instance',
|
||||
);
|
||||
public async getBase64FromMediaMessage({ instanceName }: InstanceDto, data: getBase64FromMediaMessageDto) {
|
||||
logger.verbose('requested getBase64FromMediaMessage from ' + instanceName + ' instance');
|
||||
return await this.waMonitor.waInstances[instanceName].getBase64FromMediaMessage(data);
|
||||
}
|
||||
|
||||
@@ -78,27 +80,24 @@ export class ChatController {
|
||||
return await this.waMonitor.waInstances[instanceName].fetchChats();
|
||||
}
|
||||
|
||||
public async sendPresence({ instanceName }: InstanceDto, data: SendPresenceDto) {
|
||||
logger.verbose('requested sendPresence from ' + instanceName + ' instance');
|
||||
return await this.waMonitor.waInstances[instanceName].sendPresence(data);
|
||||
}
|
||||
|
||||
public async fetchPrivacySettings({ instanceName }: InstanceDto) {
|
||||
logger.verbose('requested fetchPrivacySettings from ' + instanceName + ' instance');
|
||||
return await this.waMonitor.waInstances[instanceName].fetchPrivacySettings();
|
||||
}
|
||||
|
||||
public async updatePrivacySettings(
|
||||
{ instanceName }: InstanceDto,
|
||||
data: PrivacySettingDto,
|
||||
) {
|
||||
public async updatePrivacySettings({ instanceName }: InstanceDto, data: PrivacySettingDto) {
|
||||
logger.verbose('requested updatePrivacySettings from ' + instanceName + ' instance');
|
||||
return await this.waMonitor.waInstances[instanceName].updatePrivacySettings(data);
|
||||
}
|
||||
|
||||
public async fetchBusinessProfile(
|
||||
{ instanceName }: InstanceDto,
|
||||
data: ProfilePictureDto,
|
||||
) {
|
||||
public async fetchBusinessProfile({ instanceName }: InstanceDto, data: ProfilePictureDto) {
|
||||
logger.verbose('requested fetchBusinessProfile from ' + instanceName + ' instance');
|
||||
return await this.waMonitor.waInstances[instanceName].fetchBusinessProfile(
|
||||
data.number,
|
||||
);
|
||||
return await this.waMonitor.waInstances[instanceName].fetchBusinessProfile(data.number);
|
||||
}
|
||||
|
||||
public async updateProfileName({ instanceName }: InstanceDto, data: ProfileNameDto) {
|
||||
@@ -106,31 +105,28 @@ export class ChatController {
|
||||
return await this.waMonitor.waInstances[instanceName].updateProfileName(data.name);
|
||||
}
|
||||
|
||||
public async updateProfileStatus(
|
||||
{ instanceName }: InstanceDto,
|
||||
data: ProfileStatusDto,
|
||||
) {
|
||||
public async updateProfileStatus({ instanceName }: InstanceDto, data: ProfileStatusDto) {
|
||||
logger.verbose('requested updateProfileStatus from ' + instanceName + ' instance');
|
||||
return await this.waMonitor.waInstances[instanceName].updateProfileStatus(
|
||||
data.status,
|
||||
);
|
||||
return await this.waMonitor.waInstances[instanceName].updateProfileStatus(data.status);
|
||||
}
|
||||
|
||||
public async updateProfilePicture(
|
||||
{ instanceName }: InstanceDto,
|
||||
data: ProfilePictureDto,
|
||||
) {
|
||||
public async updateProfilePicture({ instanceName }: InstanceDto, data: ProfilePictureDto) {
|
||||
logger.verbose('requested updateProfilePicture from ' + instanceName + ' instance');
|
||||
return await this.waMonitor.waInstances[instanceName].updateProfilePicture(
|
||||
data.picture,
|
||||
);
|
||||
return await this.waMonitor.waInstances[instanceName].updateProfilePicture(data.picture);
|
||||
}
|
||||
|
||||
public async removeProfilePicture(
|
||||
{ instanceName }: InstanceDto,
|
||||
data: ProfilePictureDto,
|
||||
) {
|
||||
public async removeProfilePicture({ instanceName }: InstanceDto) {
|
||||
logger.verbose('requested removeProfilePicture from ' + instanceName + ' instance');
|
||||
return await this.waMonitor.waInstances[instanceName].removeProfilePicture();
|
||||
}
|
||||
|
||||
public async updateMessage({ instanceName }: InstanceDto, data: UpdateMessageDto) {
|
||||
logger.verbose('requested updateMessage from ' + instanceName + ' instance');
|
||||
return await this.waMonitor.waInstances[instanceName].updateMessage(data);
|
||||
}
|
||||
|
||||
public async blockUser({ instanceName }: InstanceDto, data: BlockUserDto) {
|
||||
logger.verbose('requested blockUser from ' + instanceName + ' instance');
|
||||
return await this.waMonitor.waInstances[instanceName].blockUser(data);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
import { isURL } from 'class-validator';
|
||||
import { BadRequestException } from '../../exceptions';
|
||||
import { InstanceDto } from '../dto/instance.dto';
|
||||
import { ChatwootDto } from '../dto/chatwoot.dto';
|
||||
import { ChatwootService } from '../services/chatwoot.service';
|
||||
import { Logger } from '../../config/logger.config';
|
||||
import { waMonitor } from '../whatsapp.module';
|
||||
|
||||
import { ConfigService, HttpServer } from '../../config/env.config';
|
||||
import { Logger } from '../../config/logger.config';
|
||||
import { BadRequestException } from '../../exceptions';
|
||||
import { CacheEngine } from '../../libs/cacheengine';
|
||||
import { ChatwootDto } from '../dto/chatwoot.dto';
|
||||
import { InstanceDto } from '../dto/instance.dto';
|
||||
import { RepositoryBroker } from '../repository/repository.manager';
|
||||
import { CacheService } from '../services/cache.service';
|
||||
import { ChatwootService } from '../services/chatwoot.service';
|
||||
import { waMonitor } from '../whatsapp.module';
|
||||
|
||||
const logger = new Logger('ChatwootController');
|
||||
|
||||
@@ -13,12 +17,11 @@ export class ChatwootController {
|
||||
constructor(
|
||||
private readonly chatwootService: ChatwootService,
|
||||
private readonly configService: ConfigService,
|
||||
private readonly repository: RepositoryBroker,
|
||||
) {}
|
||||
|
||||
public async createChatwoot(instance: InstanceDto, data: ChatwootDto) {
|
||||
logger.verbose(
|
||||
'requested createChatwoot from ' + instance.instanceName + ' instance',
|
||||
);
|
||||
logger.verbose('requested createChatwoot from ' + instance.instanceName + ' instance');
|
||||
|
||||
if (data.enabled) {
|
||||
if (!isURL(data.url, { require_tld: false })) {
|
||||
@@ -33,9 +36,10 @@ export class ChatwootController {
|
||||
throw new BadRequestException('token is required');
|
||||
}
|
||||
|
||||
if (!data.sign_msg) {
|
||||
if (data.sign_msg !== true && data.sign_msg !== false) {
|
||||
throw new BadRequestException('sign_msg is required');
|
||||
}
|
||||
if (data.sign_msg === false) data.sign_delimiter = null;
|
||||
}
|
||||
|
||||
if (!data.enabled) {
|
||||
@@ -44,17 +48,24 @@ export class ChatwootController {
|
||||
data.token = '';
|
||||
data.url = '';
|
||||
data.sign_msg = false;
|
||||
data.sign_delimiter = null;
|
||||
data.reopen_conversation = false;
|
||||
data.conversation_pending = false;
|
||||
data.import_contacts = false;
|
||||
data.import_messages = false;
|
||||
data.days_limit_import_messages = 0;
|
||||
data.auto_create = false;
|
||||
}
|
||||
|
||||
data.name_inbox = instance.instanceName;
|
||||
|
||||
const result = this.chatwootService.create(instance, data);
|
||||
const result = await this.chatwootService.create(instance, data);
|
||||
|
||||
const urlServer = this.configService.get<HttpServer>('SERVER').URL;
|
||||
|
||||
const response = {
|
||||
...result,
|
||||
webhook_url: `${urlServer}/chatwoot/webhook/${instance.instanceName}`,
|
||||
webhook_url: `${urlServer}/chatwoot/webhook/${encodeURIComponent(instance.instanceName)}`,
|
||||
};
|
||||
|
||||
return response;
|
||||
@@ -66,19 +77,31 @@ export class ChatwootController {
|
||||
|
||||
const urlServer = this.configService.get<HttpServer>('SERVER').URL;
|
||||
|
||||
if (Object.keys(result || {}).length === 0) {
|
||||
return {
|
||||
enabled: false,
|
||||
url: '',
|
||||
account_id: '',
|
||||
token: '',
|
||||
sign_msg: false,
|
||||
name_inbox: '',
|
||||
webhook_url: '',
|
||||
};
|
||||
}
|
||||
|
||||
const response = {
|
||||
...result,
|
||||
webhook_url: `${urlServer}/chatwoot/webhook/${instance.instanceName}`,
|
||||
webhook_url: `${urlServer}/chatwoot/webhook/${encodeURIComponent(instance.instanceName)}`,
|
||||
};
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
public async receiveWebhook(instance: InstanceDto, data: any) {
|
||||
logger.verbose(
|
||||
'requested receiveWebhook from ' + instance.instanceName + ' instance',
|
||||
);
|
||||
const chatwootService = new ChatwootService(waMonitor);
|
||||
logger.verbose('requested receiveWebhook from ' + instance.instanceName + ' instance');
|
||||
|
||||
const chatwootCache = new CacheService(new CacheEngine(this.configService, ChatwootService.name).getEngine());
|
||||
const chatwootService = new ChatwootService(waMonitor, this.configService, this.repository, chatwootCache);
|
||||
|
||||
return chatwootService.receiveWebhook(instance, data);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import { Logger } from '../../config/logger.config';
|
||||
import {
|
||||
AcceptGroupInvite,
|
||||
CreateGroupDto,
|
||||
GetParticipant,
|
||||
GroupDescriptionDto,
|
||||
@@ -13,7 +15,6 @@ import {
|
||||
} from '../dto/group.dto';
|
||||
import { InstanceDto } from '../dto/instance.dto';
|
||||
import { WAMonitoringService } from '../services/monitor.service';
|
||||
import { Logger } from '../../config/logger.config';
|
||||
|
||||
const logger = new Logger('ChatController');
|
||||
|
||||
@@ -26,33 +27,18 @@ export class GroupController {
|
||||
}
|
||||
|
||||
public async updateGroupPicture(instance: InstanceDto, update: GroupPictureDto) {
|
||||
logger.verbose(
|
||||
'requested updateGroupPicture from ' + instance.instanceName + ' instance',
|
||||
);
|
||||
return await this.waMonitor.waInstances[instance.instanceName].updateGroupPicture(
|
||||
update,
|
||||
);
|
||||
logger.verbose('requested updateGroupPicture from ' + instance.instanceName + ' instance');
|
||||
return await this.waMonitor.waInstances[instance.instanceName].updateGroupPicture(update);
|
||||
}
|
||||
|
||||
public async updateGroupSubject(instance: InstanceDto, update: GroupSubjectDto) {
|
||||
logger.verbose(
|
||||
'requested updateGroupSubject from ' + instance.instanceName + ' instance',
|
||||
);
|
||||
return await this.waMonitor.waInstances[instance.instanceName].updateGroupSubject(
|
||||
update,
|
||||
);
|
||||
logger.verbose('requested updateGroupSubject from ' + instance.instanceName + ' instance');
|
||||
return await this.waMonitor.waInstances[instance.instanceName].updateGroupSubject(update);
|
||||
}
|
||||
|
||||
public async updateGroupDescription(
|
||||
instance: InstanceDto,
|
||||
update: GroupDescriptionDto,
|
||||
) {
|
||||
logger.verbose(
|
||||
'requested updateGroupDescription from ' + instance.instanceName + ' instance',
|
||||
);
|
||||
return await this.waMonitor.waInstances[instance.instanceName].updateGroupDescription(
|
||||
update,
|
||||
);
|
||||
public async updateGroupDescription(instance: InstanceDto, update: GroupDescriptionDto) {
|
||||
logger.verbose('requested updateGroupDescription from ' + instance.instanceName + ' instance');
|
||||
return await this.waMonitor.waInstances[instance.instanceName].updateGroupDescription(update);
|
||||
}
|
||||
|
||||
public async findGroupInfo(instance: InstanceDto, groupJid: GroupJid) {
|
||||
@@ -61,12 +47,8 @@ export class GroupController {
|
||||
}
|
||||
|
||||
public async fetchAllGroups(instance: InstanceDto, getPaticipants: GetParticipant) {
|
||||
logger.verbose(
|
||||
'requested fetchAllGroups from ' + instance.instanceName + ' instance',
|
||||
);
|
||||
return await this.waMonitor.waInstances[instance.instanceName].fetchAllGroups(
|
||||
getPaticipants,
|
||||
);
|
||||
logger.verbose('requested fetchAllGroups from ' + instance.instanceName + ' instance');
|
||||
return await this.waMonitor.waInstances[instance.instanceName].fetchAllGroups(getPaticipants);
|
||||
}
|
||||
|
||||
public async inviteCode(instance: InstanceDto, groupJid: GroupJid) {
|
||||
@@ -84,50 +66,34 @@ export class GroupController {
|
||||
return await this.waMonitor.waInstances[instance.instanceName].sendInvite(data);
|
||||
}
|
||||
|
||||
public async acceptInviteCode(instance: InstanceDto, inviteCode: AcceptGroupInvite) {
|
||||
logger.verbose('requested acceptInviteCode from ' + instance.instanceName + ' instance');
|
||||
return await this.waMonitor.waInstances[instance.instanceName].acceptInviteCode(inviteCode);
|
||||
}
|
||||
|
||||
public async revokeInviteCode(instance: InstanceDto, groupJid: GroupJid) {
|
||||
logger.verbose(
|
||||
'requested revokeInviteCode from ' + instance.instanceName + ' instance',
|
||||
);
|
||||
return await this.waMonitor.waInstances[instance.instanceName].revokeInviteCode(
|
||||
groupJid,
|
||||
);
|
||||
logger.verbose('requested revokeInviteCode from ' + instance.instanceName + ' instance');
|
||||
return await this.waMonitor.waInstances[instance.instanceName].revokeInviteCode(groupJid);
|
||||
}
|
||||
|
||||
public async findParticipants(instance: InstanceDto, groupJid: GroupJid) {
|
||||
logger.verbose(
|
||||
'requested findParticipants from ' + instance.instanceName + ' instance',
|
||||
);
|
||||
return await this.waMonitor.waInstances[instance.instanceName].findParticipants(
|
||||
groupJid,
|
||||
);
|
||||
logger.verbose('requested findParticipants from ' + instance.instanceName + ' instance');
|
||||
return await this.waMonitor.waInstances[instance.instanceName].findParticipants(groupJid);
|
||||
}
|
||||
|
||||
public async updateGParticipate(
|
||||
instance: InstanceDto,
|
||||
update: GroupUpdateParticipantDto,
|
||||
) {
|
||||
logger.verbose(
|
||||
'requested updateGParticipate from ' + instance.instanceName + ' instance',
|
||||
);
|
||||
return await this.waMonitor.waInstances[instance.instanceName].updateGParticipant(
|
||||
update,
|
||||
);
|
||||
public async updateGParticipate(instance: InstanceDto, update: GroupUpdateParticipantDto) {
|
||||
logger.verbose('requested updateGParticipate from ' + instance.instanceName + ' instance');
|
||||
return await this.waMonitor.waInstances[instance.instanceName].updateGParticipant(update);
|
||||
}
|
||||
|
||||
public async updateGSetting(instance: InstanceDto, update: GroupUpdateSettingDto) {
|
||||
logger.verbose(
|
||||
'requested updateGSetting from ' + instance.instanceName + ' instance',
|
||||
);
|
||||
logger.verbose('requested updateGSetting from ' + instance.instanceName + ' instance');
|
||||
return await this.waMonitor.waInstances[instance.instanceName].updateGSetting(update);
|
||||
}
|
||||
|
||||
public async toggleEphemeral(instance: InstanceDto, update: GroupToggleEphemeralDto) {
|
||||
logger.verbose(
|
||||
'requested toggleEphemeral from ' + instance.instanceName + ' instance',
|
||||
);
|
||||
return await this.waMonitor.waInstances[instance.instanceName].toggleEphemeral(
|
||||
update,
|
||||
);
|
||||
logger.verbose('requested toggleEphemeral from ' + instance.instanceName + ' instance');
|
||||
return await this.waMonitor.waInstances[instance.instanceName].toggleEphemeral(update);
|
||||
}
|
||||
|
||||
public async leaveGroup(instance: InstanceDto, groupJid: GroupJid) {
|
||||
|
||||
@@ -1,17 +1,28 @@
|
||||
import { delay } from '@whiskeysockets/baileys';
|
||||
import { isURL } from 'class-validator';
|
||||
import EventEmitter2 from 'eventemitter2';
|
||||
import { Auth, ConfigService, HttpServer } from '../../config/env.config';
|
||||
import { v4 } from 'uuid';
|
||||
|
||||
import { ConfigService, HttpServer, WaBusiness } from '../../config/env.config';
|
||||
import { Logger } from '../../config/logger.config';
|
||||
import { BadRequestException, InternalServerErrorException } from '../../exceptions';
|
||||
import { InstanceDto } from '../dto/instance.dto';
|
||||
import { RedisCache } from '../../libs/redis.client';
|
||||
import { InstanceDto, SetPresenceDto } from '../dto/instance.dto';
|
||||
import { RepositoryBroker } from '../repository/repository.manager';
|
||||
import { AuthService, OldToken } from '../services/auth.service';
|
||||
import { WAMonitoringService } from '../services/monitor.service';
|
||||
import { WAStartupService } from '../services/whatsapp.service';
|
||||
import { WebhookService } from '../services/webhook.service';
|
||||
import { CacheService } from '../services/cache.service';
|
||||
import { ChatwootService } from '../services/chatwoot.service';
|
||||
import { Logger } from '../../config/logger.config';
|
||||
import { wa } from '../types/wa.types';
|
||||
import { RedisCache } from '../../db/redis.client';
|
||||
import { IntegrationService } from '../services/integration.service';
|
||||
import { WAMonitoringService } from '../services/monitor.service';
|
||||
import { RabbitmqService } from '../services/rabbitmq.service';
|
||||
import { SettingsService } from '../services/settings.service';
|
||||
import { SqsService } from '../services/sqs.service';
|
||||
import { TypebotService } from '../services/typebot.service';
|
||||
import { WebhookService } from '../services/webhook.service';
|
||||
import { WebsocketService } from '../services/websocket.service';
|
||||
import { BaileysStartupService } from '../services/whatsapp.baileys.service';
|
||||
import { BusinessStartupService } from '../services/whatsapp.business.service';
|
||||
import { Events, Integration, wa } from '../types/wa.types';
|
||||
|
||||
export class InstanceController {
|
||||
constructor(
|
||||
@@ -22,7 +33,14 @@ export class InstanceController {
|
||||
private readonly authService: AuthService,
|
||||
private readonly webhookService: WebhookService,
|
||||
private readonly chatwootService: ChatwootService,
|
||||
private readonly settingsService: SettingsService,
|
||||
private readonly websocketService: WebsocketService,
|
||||
private readonly rabbitmqService: RabbitmqService,
|
||||
private readonly sqsService: SqsService,
|
||||
private readonly typebotService: TypebotService,
|
||||
private readonly integrationService: IntegrationService,
|
||||
private readonly cache: RedisCache,
|
||||
private readonly chatwootCache: CacheService,
|
||||
) {}
|
||||
|
||||
private readonly logger = new Logger(InstanceController.name);
|
||||
@@ -31,160 +49,83 @@ export class InstanceController {
|
||||
instanceName,
|
||||
webhook,
|
||||
webhook_by_events,
|
||||
webhook_base64,
|
||||
events,
|
||||
qrcode,
|
||||
number,
|
||||
integration,
|
||||
token,
|
||||
chatwoot_account_id,
|
||||
chatwoot_token,
|
||||
chatwoot_url,
|
||||
chatwoot_sign_msg,
|
||||
chatwoot_reopen_conversation,
|
||||
chatwoot_conversation_pending,
|
||||
chatwoot_import_contacts,
|
||||
chatwoot_import_messages,
|
||||
chatwoot_days_limit_import_messages,
|
||||
reject_call,
|
||||
msg_call,
|
||||
groups_ignore,
|
||||
always_online,
|
||||
read_messages,
|
||||
read_status,
|
||||
sync_full_history,
|
||||
websocket_enabled,
|
||||
websocket_events,
|
||||
rabbitmq_enabled,
|
||||
rabbitmq_events,
|
||||
sqs_enabled,
|
||||
sqs_events,
|
||||
typebot_url,
|
||||
typebot,
|
||||
typebot_expire,
|
||||
typebot_keyword_finish,
|
||||
typebot_delay_message,
|
||||
typebot_unknown_message,
|
||||
typebot_listening_from_me,
|
||||
}: InstanceDto) {
|
||||
this.logger.verbose('requested createInstance from ' + instanceName + ' instance');
|
||||
|
||||
const mode = this.configService.get<Auth>('AUTHENTICATION').INSTANCE.MODE;
|
||||
|
||||
if (mode === 'container') {
|
||||
this.logger.verbose('container mode');
|
||||
|
||||
if (Object.keys(this.waMonitor.waInstances).length > 0) {
|
||||
throw new BadRequestException([
|
||||
'Instance already created',
|
||||
'Only one instance can be created',
|
||||
]);
|
||||
}
|
||||
try {
|
||||
this.logger.verbose('requested createInstance from ' + instanceName + ' instance');
|
||||
|
||||
this.logger.verbose('checking duplicate token');
|
||||
await this.authService.checkDuplicateToken(token);
|
||||
|
||||
this.logger.verbose('creating instance');
|
||||
const instance = new WAStartupService(
|
||||
this.configService,
|
||||
this.eventEmitter,
|
||||
this.repository,
|
||||
this.cache,
|
||||
);
|
||||
instance.instanceName = instanceName;
|
||||
this.logger.verbose('instance: ' + instance.instanceName + ' created');
|
||||
|
||||
this.waMonitor.waInstances[instance.instanceName] = instance;
|
||||
this.waMonitor.delInstanceTime(instance.instanceName);
|
||||
|
||||
this.logger.verbose('generating hash');
|
||||
const hash = await this.authService.generateHash(
|
||||
{
|
||||
instanceName: instance.instanceName,
|
||||
},
|
||||
token,
|
||||
);
|
||||
|
||||
this.logger.verbose('hash: ' + hash + ' generated');
|
||||
|
||||
let getEvents: string[];
|
||||
|
||||
if (webhook) {
|
||||
this.logger.verbose('creating webhook');
|
||||
try {
|
||||
this.webhookService.create(instance, {
|
||||
enabled: true,
|
||||
url: webhook,
|
||||
events,
|
||||
webhook_by_events,
|
||||
});
|
||||
|
||||
getEvents = (await this.webhookService.find(instance)).events;
|
||||
} catch (error) {
|
||||
this.logger.log(error);
|
||||
}
|
||||
}
|
||||
|
||||
if (!chatwoot_account_id || !chatwoot_token || !chatwoot_url) {
|
||||
this.logger.verbose('instance created');
|
||||
this.logger.verbose({
|
||||
instance: {
|
||||
instanceName: instance.instanceName,
|
||||
status: 'created',
|
||||
},
|
||||
hash,
|
||||
webhook,
|
||||
events: getEvents,
|
||||
});
|
||||
|
||||
return {
|
||||
instance: {
|
||||
instanceName: instance.instanceName,
|
||||
status: 'created',
|
||||
},
|
||||
hash,
|
||||
webhook,
|
||||
events: getEvents,
|
||||
};
|
||||
}
|
||||
|
||||
if (!chatwoot_account_id) {
|
||||
throw new BadRequestException('account_id is required');
|
||||
}
|
||||
|
||||
if (!chatwoot_token) {
|
||||
if (!token && integration === Integration.WHATSAPP_BUSINESS) {
|
||||
throw new BadRequestException('token is required');
|
||||
}
|
||||
|
||||
if (!chatwoot_url) {
|
||||
throw new BadRequestException('url is required');
|
||||
}
|
||||
|
||||
const urlServer = this.configService.get<HttpServer>('SERVER').URL;
|
||||
|
||||
try {
|
||||
this.chatwootService.create(instance, {
|
||||
enabled: true,
|
||||
account_id: chatwoot_account_id,
|
||||
token: chatwoot_token,
|
||||
url: chatwoot_url,
|
||||
sign_msg: chatwoot_sign_msg || false,
|
||||
name_inbox: instance.instanceName,
|
||||
});
|
||||
|
||||
this.chatwootService.initInstanceChatwoot(
|
||||
instance,
|
||||
instance.instanceName,
|
||||
`${urlServer}/chatwoot/webhook/${instance.instanceName}`,
|
||||
qrcode,
|
||||
);
|
||||
} catch (error) {
|
||||
this.logger.log(error);
|
||||
}
|
||||
|
||||
return {
|
||||
instance: {
|
||||
instanceName: instance.instanceName,
|
||||
status: 'created',
|
||||
},
|
||||
hash,
|
||||
chatwoot: {
|
||||
enabled: true,
|
||||
account_id: chatwoot_account_id,
|
||||
token: chatwoot_token,
|
||||
url: chatwoot_url,
|
||||
sign_msg: chatwoot_sign_msg || false,
|
||||
name_inbox: instance.instanceName,
|
||||
webhook_url: `${urlServer}/chatwoot/webhook/${instance.instanceName}`,
|
||||
},
|
||||
};
|
||||
} else {
|
||||
this.logger.verbose('server mode');
|
||||
|
||||
this.logger.verbose('checking duplicate token');
|
||||
await this.authService.checkDuplicateToken(token);
|
||||
|
||||
this.logger.verbose('creating instance');
|
||||
const instance = new WAStartupService(
|
||||
this.configService,
|
||||
this.eventEmitter,
|
||||
this.repository,
|
||||
this.cache,
|
||||
);
|
||||
let instance: BaileysStartupService | BusinessStartupService;
|
||||
if (integration === Integration.WHATSAPP_BUSINESS) {
|
||||
instance = new BusinessStartupService(
|
||||
this.configService,
|
||||
this.eventEmitter,
|
||||
this.repository,
|
||||
this.cache,
|
||||
this.chatwootCache,
|
||||
);
|
||||
} else {
|
||||
instance = new BaileysStartupService(
|
||||
this.configService,
|
||||
this.eventEmitter,
|
||||
this.repository,
|
||||
this.cache,
|
||||
this.chatwootCache,
|
||||
);
|
||||
}
|
||||
|
||||
await this.waMonitor.saveInstance({ integration, instanceName, token, number });
|
||||
|
||||
instance.instanceName = instanceName;
|
||||
|
||||
const instanceId = v4();
|
||||
|
||||
instance.sendDataWebhook(Events.INSTANCE_CREATE, {
|
||||
instanceName,
|
||||
instanceId: instanceId,
|
||||
});
|
||||
|
||||
this.logger.verbose('instance: ' + instance.instanceName + ' created');
|
||||
|
||||
this.waMonitor.waInstances[instance.instanceName] = instance;
|
||||
@@ -194,64 +135,327 @@ export class InstanceController {
|
||||
const hash = await this.authService.generateHash(
|
||||
{
|
||||
instanceName: instance.instanceName,
|
||||
instanceId: instanceId,
|
||||
},
|
||||
token,
|
||||
);
|
||||
|
||||
this.logger.verbose('hash: ' + hash + ' generated');
|
||||
|
||||
let getEvents: string[];
|
||||
let webhookEvents: string[];
|
||||
|
||||
if (webhook) {
|
||||
if (!isURL(webhook, { require_tld: false })) {
|
||||
throw new BadRequestException('Invalid "url" property in webhook');
|
||||
}
|
||||
|
||||
this.logger.verbose('creating webhook');
|
||||
try {
|
||||
let newEvents: string[] = [];
|
||||
if (events.length === 0) {
|
||||
newEvents = [
|
||||
'APPLICATION_STARTUP',
|
||||
'QRCODE_UPDATED',
|
||||
'MESSAGES_SET',
|
||||
'MESSAGES_UPSERT',
|
||||
'MESSAGES_UPDATE',
|
||||
'MESSAGES_DELETE',
|
||||
'SEND_MESSAGE',
|
||||
'CONTACTS_SET',
|
||||
'CONTACTS_UPSERT',
|
||||
'CONTACTS_UPDATE',
|
||||
'PRESENCE_UPDATE',
|
||||
'CHATS_SET',
|
||||
'CHATS_UPSERT',
|
||||
'CHATS_UPDATE',
|
||||
'CHATS_DELETE',
|
||||
'GROUPS_UPSERT',
|
||||
'GROUP_UPDATE',
|
||||
'GROUP_PARTICIPANTS_UPDATE',
|
||||
'CONNECTION_UPDATE',
|
||||
'LABELS_EDIT',
|
||||
'LABELS_ASSOCIATION',
|
||||
'CALL',
|
||||
'NEW_JWT_TOKEN',
|
||||
'TYPEBOT_START',
|
||||
'TYPEBOT_CHANGE_STATUS',
|
||||
'CHAMA_AI_ACTION',
|
||||
];
|
||||
} else {
|
||||
newEvents = events;
|
||||
}
|
||||
this.webhookService.create(instance, {
|
||||
enabled: true,
|
||||
url: webhook,
|
||||
events,
|
||||
events: newEvents,
|
||||
webhook_by_events,
|
||||
webhook_base64,
|
||||
});
|
||||
|
||||
getEvents = (await this.webhookService.find(instance)).events;
|
||||
webhookEvents = (await this.webhookService.find(instance)).events;
|
||||
} catch (error) {
|
||||
this.logger.log(error);
|
||||
}
|
||||
}
|
||||
|
||||
let websocketEvents: string[];
|
||||
|
||||
if (websocket_enabled) {
|
||||
this.logger.verbose('creating websocket');
|
||||
try {
|
||||
let newEvents: string[] = [];
|
||||
if (websocket_events.length === 0) {
|
||||
newEvents = [
|
||||
'APPLICATION_STARTUP',
|
||||
'QRCODE_UPDATED',
|
||||
'MESSAGES_SET',
|
||||
'MESSAGES_UPSERT',
|
||||
'MESSAGES_UPDATE',
|
||||
'MESSAGES_DELETE',
|
||||
'SEND_MESSAGE',
|
||||
'CONTACTS_SET',
|
||||
'CONTACTS_UPSERT',
|
||||
'CONTACTS_UPDATE',
|
||||
'PRESENCE_UPDATE',
|
||||
'CHATS_SET',
|
||||
'CHATS_UPSERT',
|
||||
'CHATS_UPDATE',
|
||||
'CHATS_DELETE',
|
||||
'GROUPS_UPSERT',
|
||||
'GROUP_UPDATE',
|
||||
'GROUP_PARTICIPANTS_UPDATE',
|
||||
'CONNECTION_UPDATE',
|
||||
'LABELS_EDIT',
|
||||
'LABELS_ASSOCIATION',
|
||||
'CALL',
|
||||
'NEW_JWT_TOKEN',
|
||||
'TYPEBOT_START',
|
||||
'TYPEBOT_CHANGE_STATUS',
|
||||
'CHAMA_AI_ACTION',
|
||||
];
|
||||
} else {
|
||||
newEvents = websocket_events;
|
||||
}
|
||||
this.websocketService.create(instance, {
|
||||
enabled: true,
|
||||
events: newEvents,
|
||||
});
|
||||
|
||||
websocketEvents = (await this.websocketService.find(instance)).events;
|
||||
} catch (error) {
|
||||
this.logger.log(error);
|
||||
}
|
||||
}
|
||||
|
||||
let rabbitmqEvents: string[];
|
||||
|
||||
if (rabbitmq_enabled) {
|
||||
this.logger.verbose('creating rabbitmq');
|
||||
try {
|
||||
let newEvents: string[] = [];
|
||||
if (rabbitmq_events.length === 0) {
|
||||
newEvents = [
|
||||
'APPLICATION_STARTUP',
|
||||
'QRCODE_UPDATED',
|
||||
'MESSAGES_SET',
|
||||
'MESSAGES_UPSERT',
|
||||
'MESSAGES_UPDATE',
|
||||
'MESSAGES_DELETE',
|
||||
'SEND_MESSAGE',
|
||||
'CONTACTS_SET',
|
||||
'CONTACTS_UPSERT',
|
||||
'CONTACTS_UPDATE',
|
||||
'PRESENCE_UPDATE',
|
||||
'CHATS_SET',
|
||||
'CHATS_UPSERT',
|
||||
'CHATS_UPDATE',
|
||||
'CHATS_DELETE',
|
||||
'GROUPS_UPSERT',
|
||||
'GROUP_UPDATE',
|
||||
'GROUP_PARTICIPANTS_UPDATE',
|
||||
'CONNECTION_UPDATE',
|
||||
'LABELS_EDIT',
|
||||
'LABELS_ASSOCIATION',
|
||||
'CALL',
|
||||
'NEW_JWT_TOKEN',
|
||||
'TYPEBOT_START',
|
||||
'TYPEBOT_CHANGE_STATUS',
|
||||
'CHAMA_AI_ACTION',
|
||||
];
|
||||
} else {
|
||||
newEvents = rabbitmq_events;
|
||||
}
|
||||
this.rabbitmqService.create(instance, {
|
||||
enabled: true,
|
||||
events: newEvents,
|
||||
});
|
||||
|
||||
rabbitmqEvents = (await this.rabbitmqService.find(instance)).events;
|
||||
} catch (error) {
|
||||
this.logger.log(error);
|
||||
}
|
||||
}
|
||||
|
||||
let sqsEvents: string[];
|
||||
|
||||
if (sqs_enabled) {
|
||||
this.logger.verbose('creating sqs');
|
||||
try {
|
||||
let newEvents: string[] = [];
|
||||
if (sqs_events.length === 0) {
|
||||
newEvents = [
|
||||
'APPLICATION_STARTUP',
|
||||
'QRCODE_UPDATED',
|
||||
'MESSAGES_SET',
|
||||
'MESSAGES_UPSERT',
|
||||
'MESSAGES_UPDATE',
|
||||
'MESSAGES_DELETE',
|
||||
'SEND_MESSAGE',
|
||||
'CONTACTS_SET',
|
||||
'CONTACTS_UPSERT',
|
||||
'CONTACTS_UPDATE',
|
||||
'PRESENCE_UPDATE',
|
||||
'CHATS_SET',
|
||||
'CHATS_UPSERT',
|
||||
'CHATS_UPDATE',
|
||||
'CHATS_DELETE',
|
||||
'GROUPS_UPSERT',
|
||||
'GROUP_UPDATE',
|
||||
'GROUP_PARTICIPANTS_UPDATE',
|
||||
'CONNECTION_UPDATE',
|
||||
'LABELS_EDIT',
|
||||
'LABELS_ASSOCIATION',
|
||||
'CALL',
|
||||
'NEW_JWT_TOKEN',
|
||||
'TYPEBOT_START',
|
||||
'TYPEBOT_CHANGE_STATUS',
|
||||
'CHAMA_AI_ACTION',
|
||||
];
|
||||
} else {
|
||||
newEvents = sqs_events;
|
||||
}
|
||||
this.sqsService.create(instance, {
|
||||
enabled: true,
|
||||
events: newEvents,
|
||||
});
|
||||
|
||||
sqsEvents = (await this.sqsService.find(instance)).events;
|
||||
} catch (error) {
|
||||
this.logger.log(error);
|
||||
}
|
||||
}
|
||||
|
||||
if (typebot_url) {
|
||||
try {
|
||||
if (!isURL(typebot_url, { require_tld: false })) {
|
||||
throw new BadRequestException('Invalid "url" property in typebot_url');
|
||||
}
|
||||
|
||||
this.logger.verbose('creating typebot');
|
||||
|
||||
this.typebotService.create(instance, {
|
||||
enabled: true,
|
||||
url: typebot_url,
|
||||
typebot: typebot,
|
||||
expire: typebot_expire,
|
||||
keyword_finish: typebot_keyword_finish,
|
||||
delay_message: typebot_delay_message,
|
||||
unknown_message: typebot_unknown_message,
|
||||
listening_from_me: typebot_listening_from_me,
|
||||
});
|
||||
} catch (error) {
|
||||
this.logger.log(error);
|
||||
}
|
||||
}
|
||||
|
||||
this.logger.verbose('creating settings');
|
||||
const settings: wa.LocalSettings = {
|
||||
reject_call: reject_call || false,
|
||||
msg_call: msg_call || '',
|
||||
groups_ignore: groups_ignore || true,
|
||||
always_online: always_online || false,
|
||||
read_messages: read_messages || false,
|
||||
read_status: read_status || false,
|
||||
sync_full_history: sync_full_history ?? false,
|
||||
};
|
||||
|
||||
this.logger.verbose('settings: ' + JSON.stringify(settings));
|
||||
|
||||
this.settingsService.create(instance, settings);
|
||||
|
||||
let webhook_wa_business = null,
|
||||
access_token_wa_business = '';
|
||||
|
||||
if (integration === Integration.WHATSAPP_BUSINESS) {
|
||||
if (!number) {
|
||||
throw new BadRequestException('number is required');
|
||||
}
|
||||
const urlServer = this.configService.get<HttpServer>('SERVER').URL;
|
||||
webhook_wa_business = `${urlServer}/webhook/whatsapp/${encodeURIComponent(instance.instanceName)}`;
|
||||
access_token_wa_business = this.configService.get<WaBusiness>('WA_BUSINESS').TOKEN_WEBHOOK;
|
||||
}
|
||||
|
||||
this.integrationService.create(instance, {
|
||||
integration,
|
||||
number,
|
||||
token,
|
||||
});
|
||||
if (!chatwoot_account_id || !chatwoot_token || !chatwoot_url) {
|
||||
let getQrcode: wa.QrCode;
|
||||
|
||||
if (qrcode) {
|
||||
this.logger.verbose('creating qrcode');
|
||||
await instance.connectToWhatsapp();
|
||||
await delay(2000);
|
||||
await instance.connectToWhatsapp(number);
|
||||
await delay(5000);
|
||||
getQrcode = instance.qrCode;
|
||||
}
|
||||
|
||||
this.logger.verbose('instance created');
|
||||
this.logger.verbose({
|
||||
const result = {
|
||||
instance: {
|
||||
instanceName: instance.instanceName,
|
||||
instanceId: instanceId,
|
||||
integration: integration,
|
||||
webhook_wa_business,
|
||||
access_token_wa_business,
|
||||
status: 'created',
|
||||
},
|
||||
hash,
|
||||
webhook,
|
||||
webhook_by_events,
|
||||
events: getEvents,
|
||||
qrcode: getQrcode,
|
||||
});
|
||||
|
||||
return {
|
||||
instance: {
|
||||
instanceName: instance.instanceName,
|
||||
status: 'created',
|
||||
webhook: {
|
||||
webhook,
|
||||
webhook_by_events,
|
||||
webhook_base64,
|
||||
events: webhookEvents,
|
||||
},
|
||||
hash,
|
||||
webhook,
|
||||
webhook_by_events,
|
||||
events: getEvents,
|
||||
websocket: {
|
||||
enabled: websocket_enabled,
|
||||
events: websocketEvents,
|
||||
},
|
||||
rabbitmq: {
|
||||
enabled: rabbitmq_enabled,
|
||||
events: rabbitmqEvents,
|
||||
},
|
||||
sqs: {
|
||||
enabled: sqs_enabled,
|
||||
events: sqsEvents,
|
||||
},
|
||||
typebot: {
|
||||
enabled: typebot_url ? true : false,
|
||||
url: typebot_url,
|
||||
typebot,
|
||||
expire: typebot_expire,
|
||||
keyword_finish: typebot_keyword_finish,
|
||||
delay_message: typebot_delay_message,
|
||||
unknown_message: typebot_unknown_message,
|
||||
listening_from_me: typebot_listening_from_me,
|
||||
},
|
||||
settings,
|
||||
qrcode: getQrcode,
|
||||
};
|
||||
|
||||
this.logger.verbose('instance created');
|
||||
this.logger.verbose(result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
if (!chatwoot_account_id) {
|
||||
@@ -266,6 +470,22 @@ export class InstanceController {
|
||||
throw new BadRequestException('url is required');
|
||||
}
|
||||
|
||||
if (!isURL(chatwoot_url, { require_tld: false })) {
|
||||
throw new BadRequestException('Invalid "url" property in chatwoot');
|
||||
}
|
||||
|
||||
if (chatwoot_sign_msg !== true && chatwoot_sign_msg !== false) {
|
||||
throw new BadRequestException('sign_msg is required');
|
||||
}
|
||||
|
||||
if (chatwoot_reopen_conversation !== true && chatwoot_reopen_conversation !== false) {
|
||||
throw new BadRequestException('reopen_conversation is required');
|
||||
}
|
||||
|
||||
if (chatwoot_conversation_pending !== true && chatwoot_conversation_pending !== false) {
|
||||
throw new BadRequestException('conversation_pending is required');
|
||||
}
|
||||
|
||||
const urlServer = this.configService.get<HttpServer>('SERVER').URL;
|
||||
|
||||
try {
|
||||
@@ -275,15 +495,15 @@ export class InstanceController {
|
||||
token: chatwoot_token,
|
||||
url: chatwoot_url,
|
||||
sign_msg: chatwoot_sign_msg || false,
|
||||
name_inbox: instance.instanceName,
|
||||
name_inbox: instance.instanceName.split('-cwId-')[0],
|
||||
number,
|
||||
reopen_conversation: chatwoot_reopen_conversation || false,
|
||||
conversation_pending: chatwoot_conversation_pending || false,
|
||||
import_contacts: chatwoot_import_contacts ?? true,
|
||||
import_messages: chatwoot_import_messages ?? true,
|
||||
days_limit_import_messages: chatwoot_days_limit_import_messages ?? 60,
|
||||
auto_create: true,
|
||||
});
|
||||
|
||||
this.chatwootService.initInstanceChatwoot(
|
||||
instance,
|
||||
instance.instanceName,
|
||||
`${urlServer}/chatwoot/webhook/${instance.instanceName}`,
|
||||
qrcode,
|
||||
);
|
||||
} catch (error) {
|
||||
this.logger.log(error);
|
||||
}
|
||||
@@ -291,47 +511,100 @@ export class InstanceController {
|
||||
return {
|
||||
instance: {
|
||||
instanceName: instance.instanceName,
|
||||
instanceId: instanceId,
|
||||
integration: integration,
|
||||
webhook_wa_business,
|
||||
access_token_wa_business,
|
||||
status: 'created',
|
||||
},
|
||||
hash,
|
||||
webhook,
|
||||
webhook_by_events,
|
||||
events: getEvents,
|
||||
webhook: {
|
||||
webhook,
|
||||
webhook_by_events,
|
||||
webhook_base64,
|
||||
events: webhookEvents,
|
||||
},
|
||||
websocket: {
|
||||
enabled: websocket_enabled,
|
||||
events: websocketEvents,
|
||||
},
|
||||
rabbitmq: {
|
||||
enabled: rabbitmq_enabled,
|
||||
events: rabbitmqEvents,
|
||||
},
|
||||
sqs: {
|
||||
enabled: sqs_enabled,
|
||||
events: sqsEvents,
|
||||
},
|
||||
typebot: {
|
||||
enabled: typebot_url ? true : false,
|
||||
url: typebot_url,
|
||||
typebot,
|
||||
expire: typebot_expire,
|
||||
keyword_finish: typebot_keyword_finish,
|
||||
delay_message: typebot_delay_message,
|
||||
unknown_message: typebot_unknown_message,
|
||||
listening_from_me: typebot_listening_from_me,
|
||||
},
|
||||
settings,
|
||||
chatwoot: {
|
||||
enabled: true,
|
||||
account_id: chatwoot_account_id,
|
||||
token: chatwoot_token,
|
||||
url: chatwoot_url,
|
||||
sign_msg: chatwoot_sign_msg || false,
|
||||
reopen_conversation: chatwoot_reopen_conversation || false,
|
||||
conversation_pending: chatwoot_conversation_pending || false,
|
||||
import_contacts: chatwoot_import_contacts ?? true,
|
||||
import_messages: chatwoot_import_messages ?? true,
|
||||
days_limit_import_messages: chatwoot_days_limit_import_messages || 60,
|
||||
number,
|
||||
name_inbox: instance.instanceName,
|
||||
webhook_url: `${urlServer}/chatwoot/webhook/${instance.instanceName}`,
|
||||
webhook_url: `${urlServer}/chatwoot/webhook/${encodeURIComponent(instance.instanceName)}`,
|
||||
},
|
||||
};
|
||||
} catch (error) {
|
||||
this.logger.error(error.message[0]);
|
||||
throw new BadRequestException(error.message[0]);
|
||||
}
|
||||
}
|
||||
|
||||
public async connectToWhatsapp({ instanceName }: InstanceDto) {
|
||||
public async connectToWhatsapp({ instanceName, number = null }: InstanceDto) {
|
||||
try {
|
||||
this.logger.verbose(
|
||||
'requested connectToWhatsapp from ' + instanceName + ' instance',
|
||||
);
|
||||
this.logger.verbose('requested connectToWhatsapp from ' + instanceName + ' instance');
|
||||
|
||||
const instance = this.waMonitor.waInstances[instanceName];
|
||||
const state = instance?.connectionStatus?.state;
|
||||
|
||||
this.logger.verbose('state: ' + state);
|
||||
|
||||
switch (state) {
|
||||
case 'close':
|
||||
this.logger.verbose('connecting');
|
||||
await instance.connectToWhatsapp();
|
||||
await delay(2000);
|
||||
return instance.qrCode;
|
||||
case 'connecting':
|
||||
return instance.qrCode;
|
||||
default:
|
||||
return await this.connectionState({ instanceName });
|
||||
if (!state) {
|
||||
throw new BadRequestException('The "' + instanceName + '" instance does not exist');
|
||||
}
|
||||
|
||||
if (state == 'open') {
|
||||
return await this.connectionState({ instanceName });
|
||||
}
|
||||
|
||||
if (state == 'connecting') {
|
||||
return instance.qrCode;
|
||||
}
|
||||
|
||||
if (state == 'close') {
|
||||
this.logger.verbose('connecting');
|
||||
await instance.connectToWhatsapp(number);
|
||||
|
||||
await delay(5000);
|
||||
return instance.qrCode;
|
||||
}
|
||||
|
||||
return {
|
||||
instance: {
|
||||
instanceName: instanceName,
|
||||
status: state,
|
||||
},
|
||||
qrcode: instance?.qrCode,
|
||||
};
|
||||
} catch (error) {
|
||||
this.logger.error(error);
|
||||
}
|
||||
@@ -341,26 +614,20 @@ export class InstanceController {
|
||||
try {
|
||||
this.logger.verbose('requested restartInstance from ' + instanceName + ' instance');
|
||||
|
||||
this.logger.verbose('deleting instance: ' + instanceName);
|
||||
delete this.waMonitor.waInstances[instanceName];
|
||||
const instance = this.waMonitor.waInstances[instanceName];
|
||||
const state = instance?.connectionStatus?.state;
|
||||
|
||||
this.logger.verbose('creating instance: ' + instanceName);
|
||||
const instance = new WAStartupService(
|
||||
this.configService,
|
||||
this.eventEmitter,
|
||||
this.repository,
|
||||
this.cache,
|
||||
);
|
||||
switch (state) {
|
||||
case 'open':
|
||||
this.logger.verbose('logging out instance: ' + instanceName);
|
||||
instance.clearCacheChatwoot();
|
||||
await instance.reloadConnection();
|
||||
await delay(2000);
|
||||
|
||||
instance.instanceName = instanceName;
|
||||
|
||||
this.logger.verbose('instance: ' + instance.instanceName + ' created');
|
||||
|
||||
this.logger.verbose('connecting instance: ' + instanceName);
|
||||
await instance.connectToWhatsapp();
|
||||
this.waMonitor.waInstances[instance.instanceName] = instance;
|
||||
|
||||
return { error: false, message: 'Instance restarted' };
|
||||
return await this.connectionState({ instanceName });
|
||||
default:
|
||||
return await this.connectionState({ instanceName });
|
||||
}
|
||||
} catch (error) {
|
||||
this.logger.error(error);
|
||||
}
|
||||
@@ -368,39 +635,44 @@ export class InstanceController {
|
||||
|
||||
public async connectionState({ instanceName }: InstanceDto) {
|
||||
this.logger.verbose('requested connectionState from ' + instanceName + ' instance');
|
||||
return this.waMonitor.waInstances[instanceName]?.connectionStatus;
|
||||
return {
|
||||
instance: {
|
||||
instanceName: instanceName,
|
||||
state: this.waMonitor.waInstances[instanceName]?.connectionStatus?.state,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
public async fetchInstances({ instanceName }: InstanceDto) {
|
||||
this.logger.verbose('requested fetchInstances from ' + instanceName + ' instance');
|
||||
public async fetchInstances({ instanceName, instanceId, number }: InstanceDto) {
|
||||
if (instanceName) {
|
||||
this.logger.verbose('requested fetchInstances from ' + instanceName + ' instance');
|
||||
this.logger.verbose('instanceName: ' + instanceName);
|
||||
return this.waMonitor.instanceInfo(instanceName);
|
||||
} else if (instanceId || number) {
|
||||
return this.waMonitor.instanceInfoById(instanceId, number);
|
||||
}
|
||||
|
||||
this.logger.verbose('requested fetchInstances (all instances)');
|
||||
return this.waMonitor.instanceInfo();
|
||||
}
|
||||
|
||||
public async setPresence({ instanceName }: InstanceDto, data: SetPresenceDto) {
|
||||
this.logger.verbose('requested sendPresence from ' + instanceName + ' instance');
|
||||
return await this.waMonitor.waInstances[instanceName].setPresence(data);
|
||||
}
|
||||
|
||||
public async logout({ instanceName }: InstanceDto) {
|
||||
this.logger.verbose('requested logout from ' + instanceName + ' instance');
|
||||
const stateConn = await this.connectionState({ instanceName });
|
||||
const { instance } = await this.connectionState({ instanceName });
|
||||
|
||||
if (stateConn.state === 'close') {
|
||||
throw new BadRequestException(
|
||||
'The "' + instanceName + '" instance is not connected',
|
||||
);
|
||||
if (instance.state === 'close') {
|
||||
throw new BadRequestException('The "' + instanceName + '" instance is not connected');
|
||||
}
|
||||
|
||||
try {
|
||||
this.logger.verbose('logging out instance: ' + instanceName);
|
||||
await this.waMonitor.waInstances[instanceName]?.client?.logout(
|
||||
'Log out instance: ' + instanceName,
|
||||
);
|
||||
this.waMonitor.waInstances[instanceName]?.logoutInstance();
|
||||
|
||||
this.logger.verbose('close connection instance: ' + instanceName);
|
||||
this.waMonitor.waInstances[instanceName]?.client?.ws?.close();
|
||||
|
||||
return { error: false, message: 'Instance logged out' };
|
||||
return { status: 'SUCCESS', error: false, response: { message: 'Instance logged out' } };
|
||||
} catch (error) {
|
||||
throw new InternalServerErrorException(error.toString());
|
||||
}
|
||||
@@ -408,27 +680,35 @@ export class InstanceController {
|
||||
|
||||
public async deleteInstance({ instanceName }: InstanceDto) {
|
||||
this.logger.verbose('requested deleteInstance from ' + instanceName + ' instance');
|
||||
const stateConn = await this.connectionState({ instanceName });
|
||||
const { instance } = await this.connectionState({ instanceName });
|
||||
|
||||
if (stateConn.state === 'open') {
|
||||
throw new BadRequestException(
|
||||
'The "' + instanceName + '" instance needs to be disconnected',
|
||||
);
|
||||
if (instance.state === 'open') {
|
||||
throw new BadRequestException('The "' + instanceName + '" instance needs to be disconnected');
|
||||
}
|
||||
try {
|
||||
if (stateConn.state === 'connecting') {
|
||||
this.waMonitor.waInstances[instanceName]?.removeRabbitmqQueues();
|
||||
this.waMonitor.waInstances[instanceName]?.clearCacheChatwoot();
|
||||
|
||||
if (instance.state === 'connecting') {
|
||||
this.logger.verbose('logging out instance: ' + instanceName);
|
||||
|
||||
await this.logout({ instanceName });
|
||||
delete this.waMonitor.waInstances[instanceName];
|
||||
return { error: false, message: 'Instance deleted' };
|
||||
} else {
|
||||
this.logger.verbose('deleting instance: ' + instanceName);
|
||||
|
||||
delete this.waMonitor.waInstances[instanceName];
|
||||
this.eventEmitter.emit('remove.instance', instanceName, 'inner');
|
||||
return { error: false, message: 'Instance deleted' };
|
||||
}
|
||||
|
||||
this.logger.verbose('deleting instance: ' + instanceName);
|
||||
|
||||
try {
|
||||
this.waMonitor.waInstances[instanceName].sendDataWebhook(Events.INSTANCE_DELETE, {
|
||||
instanceName,
|
||||
instanceId: (await this.repository.auth.find(instanceName))?.instanceId,
|
||||
});
|
||||
} catch (error) {
|
||||
this.logger.error(error);
|
||||
}
|
||||
|
||||
delete this.waMonitor.waInstances[instanceName];
|
||||
this.eventEmitter.emit('remove.instance', instanceName, 'inner');
|
||||
return { status: 'SUCCESS', error: false, response: { message: 'Instance deleted' } };
|
||||
} catch (error) {
|
||||
throw new BadRequestException(error.toString());
|
||||
}
|
||||
|
||||
20
src/whatsapp/controllers/label.controller.ts
Normal file
20
src/whatsapp/controllers/label.controller.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import { Logger } from '../../config/logger.config';
|
||||
import { InstanceDto } from '../dto/instance.dto';
|
||||
import { HandleLabelDto } from '../dto/label.dto';
|
||||
import { WAMonitoringService } from '../services/monitor.service';
|
||||
|
||||
const logger = new Logger('LabelController');
|
||||
|
||||
export class LabelController {
|
||||
constructor(private readonly waMonitor: WAMonitoringService) {}
|
||||
|
||||
public async fetchLabels({ instanceName }: InstanceDto) {
|
||||
logger.verbose('requested fetchLabels from ' + instanceName + ' instance');
|
||||
return await this.waMonitor.waInstances[instanceName].fetchLabels();
|
||||
}
|
||||
|
||||
public async handleLabel({ instanceName }: InstanceDto, data: HandleLabelDto) {
|
||||
logger.verbose('requested chat label change from ' + instanceName + ' instance');
|
||||
return await this.waMonitor.waInstances[instanceName].handleLabel(data);
|
||||
}
|
||||
}
|
||||
72
src/whatsapp/controllers/proxy.controller.ts
Normal file
72
src/whatsapp/controllers/proxy.controller.ts
Normal file
@@ -0,0 +1,72 @@
|
||||
import axios from 'axios';
|
||||
|
||||
import { Logger } from '../../config/logger.config';
|
||||
import { BadRequestException, NotFoundException } from '../../exceptions';
|
||||
import { makeProxyAgent } from '../../utils/makeProxyAgent';
|
||||
import { InstanceDto } from '../dto/instance.dto';
|
||||
import { ProxyDto } from '../dto/proxy.dto';
|
||||
import { WAMonitoringService } from '../services/monitor.service';
|
||||
import { ProxyService } from '../services/proxy.service';
|
||||
|
||||
const logger = new Logger('ProxyController');
|
||||
|
||||
export class ProxyController {
|
||||
constructor(private readonly proxyService: ProxyService, private readonly waMonitor: WAMonitoringService) {}
|
||||
|
||||
public async createProxy(instance: InstanceDto, data: ProxyDto) {
|
||||
logger.verbose('requested createProxy from ' + instance.instanceName + ' instance');
|
||||
|
||||
if (!this.waMonitor.waInstances[instance.instanceName]) {
|
||||
throw new NotFoundException(`The "${instance.instanceName}" instance does not exist`);
|
||||
}
|
||||
|
||||
if (!data.enabled) {
|
||||
logger.verbose('proxy disabled');
|
||||
data.proxy = null;
|
||||
}
|
||||
|
||||
if (data.proxy) {
|
||||
const testProxy = await this.testProxy(data.proxy);
|
||||
if (!testProxy) {
|
||||
throw new BadRequestException('Invalid proxy');
|
||||
}
|
||||
logger.verbose('proxy enabled');
|
||||
}
|
||||
|
||||
return this.proxyService.create(instance, data);
|
||||
}
|
||||
|
||||
public async findProxy(instance: InstanceDto) {
|
||||
logger.verbose('requested findProxy from ' + instance.instanceName + ' instance');
|
||||
|
||||
if (!this.waMonitor.waInstances[instance.instanceName]) {
|
||||
throw new NotFoundException(`The "${instance.instanceName}" instance does not exist`);
|
||||
}
|
||||
|
||||
return this.proxyService.find(instance);
|
||||
}
|
||||
|
||||
private async testProxy(proxy: ProxyDto['proxy']) {
|
||||
logger.verbose('requested testProxy');
|
||||
try {
|
||||
const serverIp = await axios.get('https://icanhazip.com/');
|
||||
const response = await axios.get('https://icanhazip.com/', {
|
||||
httpsAgent: makeProxyAgent(proxy),
|
||||
});
|
||||
|
||||
logger.verbose('[testProxy] from IP: ' + response?.data + ' To IP: ' + serverIp?.data);
|
||||
return response?.data !== serverIp?.data;
|
||||
} catch (error) {
|
||||
if (axios.isAxiosError(error) && error.response?.data) {
|
||||
logger.error('testProxy error: ' + error.response.data);
|
||||
} else if (axios.isAxiosError(error)) {
|
||||
logger.error('testProxy error: ');
|
||||
logger.verbose(error.cause ?? error.message);
|
||||
} else {
|
||||
logger.error('testProxy error: ');
|
||||
logger.verbose(error);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
58
src/whatsapp/controllers/rabbitmq.controller.ts
Normal file
58
src/whatsapp/controllers/rabbitmq.controller.ts
Normal file
@@ -0,0 +1,58 @@
|
||||
import { Logger } from '../../config/logger.config';
|
||||
import { InstanceDto } from '../dto/instance.dto';
|
||||
import { RabbitmqDto } from '../dto/rabbitmq.dto';
|
||||
import { RabbitmqService } from '../services/rabbitmq.service';
|
||||
|
||||
const logger = new Logger('RabbitmqController');
|
||||
|
||||
export class RabbitmqController {
|
||||
constructor(private readonly rabbitmqService: RabbitmqService) {}
|
||||
|
||||
public async createRabbitmq(instance: InstanceDto, data: RabbitmqDto) {
|
||||
logger.verbose('requested createRabbitmq from ' + instance.instanceName + ' instance');
|
||||
|
||||
if (!data.enabled) {
|
||||
logger.verbose('rabbitmq disabled');
|
||||
data.events = [];
|
||||
}
|
||||
|
||||
if (data.events.length === 0) {
|
||||
logger.verbose('rabbitmq events empty');
|
||||
data.events = [
|
||||
'APPLICATION_STARTUP',
|
||||
'QRCODE_UPDATED',
|
||||
'MESSAGES_SET',
|
||||
'MESSAGES_UPSERT',
|
||||
'MESSAGES_UPDATE',
|
||||
'MESSAGES_DELETE',
|
||||
'SEND_MESSAGE',
|
||||
'CONTACTS_SET',
|
||||
'CONTACTS_UPSERT',
|
||||
'CONTACTS_UPDATE',
|
||||
'PRESENCE_UPDATE',
|
||||
'CHATS_SET',
|
||||
'CHATS_UPSERT',
|
||||
'CHATS_UPDATE',
|
||||
'CHATS_DELETE',
|
||||
'GROUPS_UPSERT',
|
||||
'GROUP_UPDATE',
|
||||
'GROUP_PARTICIPANTS_UPDATE',
|
||||
'CONNECTION_UPDATE',
|
||||
'LABELS_EDIT',
|
||||
'LABELS_ASSOCIATION',
|
||||
'CALL',
|
||||
'NEW_JWT_TOKEN',
|
||||
'TYPEBOT_START',
|
||||
'TYPEBOT_CHANGE_STATUS',
|
||||
'CHAMA_AI_ACTION',
|
||||
];
|
||||
}
|
||||
|
||||
return this.rabbitmqService.create(instance, data);
|
||||
}
|
||||
|
||||
public async findRabbitmq(instance: InstanceDto) {
|
||||
logger.verbose('requested findRabbitmq from ' + instance.instanceName + ' instance');
|
||||
return this.rabbitmqService.find(instance);
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,12 @@
|
||||
import { isBase64, isURL } from 'class-validator';
|
||||
|
||||
import { Logger } from '../../config/logger.config';
|
||||
import { BadRequestException } from '../../exceptions';
|
||||
import { InstanceDto } from '../dto/instance.dto';
|
||||
import {
|
||||
SendAudioDto,
|
||||
SendButtonDto,
|
||||
SendContactDto,
|
||||
SendLinkPreviewDto,
|
||||
SendListDto,
|
||||
SendLocationDto,
|
||||
SendMediaDto,
|
||||
@@ -13,12 +14,11 @@ import {
|
||||
SendReactionDto,
|
||||
SendStatusDto,
|
||||
SendStickerDto,
|
||||
SendTemplateDto,
|
||||
SendTextDto,
|
||||
} from '../dto/sendMessage.dto';
|
||||
import { WAMonitoringService } from '../services/monitor.service';
|
||||
|
||||
import { Logger } from '../../config/logger.config';
|
||||
|
||||
const logger = new Logger('MessageRouter');
|
||||
|
||||
export class SendMessageController {
|
||||
@@ -29,17 +29,23 @@ export class SendMessageController {
|
||||
return await this.waMonitor.waInstances[instanceName].textMessage(data);
|
||||
}
|
||||
|
||||
public async sendTemplate({ instanceName }: InstanceDto, data: SendTemplateDto) {
|
||||
logger.verbose('requested sendList from ' + instanceName + ' instance');
|
||||
return await this.waMonitor.waInstances[instanceName].templateMessage(data);
|
||||
}
|
||||
|
||||
public async sendMedia({ instanceName }: InstanceDto, data: SendMediaDto) {
|
||||
logger.verbose('requested sendMedia from ' + instanceName + ' instance');
|
||||
if (isBase64(data?.mediaMessage?.media) && !data?.mediaMessage?.fileName) {
|
||||
throw new BadRequestException('For bse64 the file name must be informed.');
|
||||
|
||||
if (
|
||||
isBase64(data?.mediaMessage?.media) &&
|
||||
!data?.mediaMessage?.fileName &&
|
||||
data?.mediaMessage?.mediatype === 'document'
|
||||
) {
|
||||
throw new BadRequestException('For base64 the file name must be informed.');
|
||||
}
|
||||
logger.verbose(
|
||||
'isURL: ' +
|
||||
isURL(data?.mediaMessage?.media) +
|
||||
', isBase64: ' +
|
||||
isBase64(data?.mediaMessage?.media),
|
||||
);
|
||||
|
||||
logger.verbose('isURL: ' + isURL(data?.mediaMessage?.media) + ', isBase64: ' + isBase64(data?.mediaMessage?.media));
|
||||
if (isURL(data?.mediaMessage?.media) || isBase64(data?.mediaMessage?.media)) {
|
||||
return await this.waMonitor.waInstances[instanceName].mediaMessage(data);
|
||||
}
|
||||
@@ -50,10 +56,7 @@ export class SendMessageController {
|
||||
logger.verbose('requested sendSticker from ' + instanceName + ' instance');
|
||||
|
||||
logger.verbose(
|
||||
'isURL: ' +
|
||||
isURL(data?.stickerMessage?.image) +
|
||||
', isBase64: ' +
|
||||
isBase64(data?.stickerMessage?.image),
|
||||
'isURL: ' + isURL(data?.stickerMessage?.image) + ', isBase64: ' + isBase64(data?.stickerMessage?.image),
|
||||
);
|
||||
if (isURL(data.stickerMessage.image) || isBase64(data.stickerMessage.image)) {
|
||||
return await this.waMonitor.waInstances[instanceName].mediaSticker(data);
|
||||
@@ -64,12 +67,7 @@ export class SendMessageController {
|
||||
public async sendWhatsAppAudio({ instanceName }: InstanceDto, data: SendAudioDto) {
|
||||
logger.verbose('requested sendWhatsAppAudio from ' + instanceName + ' instance');
|
||||
|
||||
logger.verbose(
|
||||
'isURL: ' +
|
||||
isURL(data?.audioMessage?.audio) +
|
||||
', isBase64: ' +
|
||||
isBase64(data?.audioMessage?.audio),
|
||||
);
|
||||
logger.verbose('isURL: ' + isURL(data?.audioMessage?.audio) + ', isBase64: ' + isBase64(data?.audioMessage?.audio));
|
||||
if (isURL(data.audioMessage.audio) || isBase64(data.audioMessage.audio)) {
|
||||
return await this.waMonitor.waInstances[instanceName].audioWhatsapp(data);
|
||||
}
|
||||
@@ -78,10 +76,7 @@ export class SendMessageController {
|
||||
|
||||
public async sendButtons({ instanceName }: InstanceDto, data: SendButtonDto) {
|
||||
logger.verbose('requested sendButtons from ' + instanceName + ' instance');
|
||||
if (
|
||||
isBase64(data.buttonMessage.mediaMessage?.media) &&
|
||||
!data.buttonMessage.mediaMessage?.fileName
|
||||
) {
|
||||
if (isBase64(data.buttonMessage.mediaMessage?.media) && !data.buttonMessage.mediaMessage?.fileName) {
|
||||
throw new BadRequestException('For bse64 the file name must be informed.');
|
||||
}
|
||||
return await this.waMonitor.waInstances[instanceName].buttonMessage(data);
|
||||
@@ -104,7 +99,7 @@ export class SendMessageController {
|
||||
|
||||
public async sendReaction({ instanceName }: InstanceDto, data: SendReactionDto) {
|
||||
logger.verbose('requested sendReaction from ' + instanceName + ' instance');
|
||||
if (!data.reactionMessage.reaction.match(/[^\(\)\w\sà-ú"-\+]+/)) {
|
||||
if (!data.reactionMessage.reaction.match(/[^()\w\sà-ú"-+]+/)) {
|
||||
throw new BadRequestException('"reaction" must be an emoji');
|
||||
}
|
||||
return await this.waMonitor.waInstances[instanceName].reactionMessage(data);
|
||||
@@ -119,9 +114,4 @@ export class SendMessageController {
|
||||
logger.verbose('requested sendStatus from ' + instanceName + ' instance');
|
||||
return await this.waMonitor.waInstances[instanceName].statusMessage(data);
|
||||
}
|
||||
|
||||
public async sendLinkPreview({ instanceName }: InstanceDto, data: SendLinkPreviewDto) {
|
||||
logger.verbose('requested sendLinkPreview from ' + instanceName + ' instance');
|
||||
return await this.waMonitor.waInstances[instanceName].linkPreview(data);
|
||||
}
|
||||
}
|
||||
|
||||
22
src/whatsapp/controllers/settings.controller.ts
Normal file
22
src/whatsapp/controllers/settings.controller.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import { Logger } from '../../config/logger.config';
|
||||
import { InstanceDto } from '../dto/instance.dto';
|
||||
import { SettingsDto } from '../dto/settings.dto';
|
||||
import { SettingsService } from '../services/settings.service';
|
||||
|
||||
const logger = new Logger('SettingsController');
|
||||
|
||||
export class SettingsController {
|
||||
constructor(private readonly settingsService: SettingsService) {}
|
||||
|
||||
public async createSettings(instance: InstanceDto, data: SettingsDto) {
|
||||
logger.verbose('requested createSettings from ' + instance.instanceName + ' instance');
|
||||
|
||||
return this.settingsService.create(instance, data);
|
||||
}
|
||||
|
||||
public async findSettings(instance: InstanceDto) {
|
||||
logger.verbose('requested findSettings from ' + instance.instanceName + ' instance');
|
||||
const settings = this.settingsService.find(instance);
|
||||
return settings;
|
||||
}
|
||||
}
|
||||
58
src/whatsapp/controllers/sqs.controller.ts
Normal file
58
src/whatsapp/controllers/sqs.controller.ts
Normal file
@@ -0,0 +1,58 @@
|
||||
import { Logger } from '../../config/logger.config';
|
||||
import { InstanceDto } from '../dto/instance.dto';
|
||||
import { SqsDto } from '../dto/sqs.dto';
|
||||
import { SqsService } from '../services/sqs.service';
|
||||
|
||||
const logger = new Logger('SqsController');
|
||||
|
||||
export class SqsController {
|
||||
constructor(private readonly sqsService: SqsService) {}
|
||||
|
||||
public async createSqs(instance: InstanceDto, data: SqsDto) {
|
||||
logger.verbose('requested createSqs from ' + instance.instanceName + ' instance');
|
||||
|
||||
if (!data.enabled) {
|
||||
logger.verbose('sqs disabled');
|
||||
data.events = [];
|
||||
}
|
||||
|
||||
if (data.events.length === 0) {
|
||||
logger.verbose('sqs events empty');
|
||||
data.events = [
|
||||
'APPLICATION_STARTUP',
|
||||
'QRCODE_UPDATED',
|
||||
'MESSAGES_SET',
|
||||
'MESSAGES_UPSERT',
|
||||
'MESSAGES_UPDATE',
|
||||
'MESSAGES_DELETE',
|
||||
'SEND_MESSAGE',
|
||||
'CONTACTS_SET',
|
||||
'CONTACTS_UPSERT',
|
||||
'CONTACTS_UPDATE',
|
||||
'PRESENCE_UPDATE',
|
||||
'CHATS_SET',
|
||||
'CHATS_UPSERT',
|
||||
'CHATS_UPDATE',
|
||||
'CHATS_DELETE',
|
||||
'GROUPS_UPSERT',
|
||||
'GROUP_UPDATE',
|
||||
'GROUP_PARTICIPANTS_UPDATE',
|
||||
'CONNECTION_UPDATE',
|
||||
'LABELS_EDIT',
|
||||
'LABELS_ASSOCIATION',
|
||||
'CALL',
|
||||
'NEW_JWT_TOKEN',
|
||||
'TYPEBOT_START',
|
||||
'TYPEBOT_CHANGE_STATUS',
|
||||
'CHAMA_AI_ACTION',
|
||||
];
|
||||
}
|
||||
|
||||
return this.sqsService.create(instance, data);
|
||||
}
|
||||
|
||||
public async findSqs(instance: InstanceDto) {
|
||||
logger.verbose('requested findSqs from ' + instance.instanceName + ' instance');
|
||||
return this.sqsService.find(instance);
|
||||
}
|
||||
}
|
||||
46
src/whatsapp/controllers/typebot.controller.ts
Normal file
46
src/whatsapp/controllers/typebot.controller.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
import { Logger } from '../../config/logger.config';
|
||||
import { InstanceDto } from '../dto/instance.dto';
|
||||
import { TypebotDto } from '../dto/typebot.dto';
|
||||
import { TypebotService } from '../services/typebot.service';
|
||||
|
||||
const logger = new Logger('TypebotController');
|
||||
|
||||
export class TypebotController {
|
||||
constructor(private readonly typebotService: TypebotService) {}
|
||||
|
||||
public async createTypebot(instance: InstanceDto, data: TypebotDto) {
|
||||
logger.verbose('requested createTypebot from ' + instance.instanceName + ' instance');
|
||||
|
||||
if (!data.enabled) {
|
||||
logger.verbose('typebot disabled');
|
||||
data.url = '';
|
||||
data.typebot = '';
|
||||
data.expire = 0;
|
||||
data.sessions = [];
|
||||
} else {
|
||||
const saveData = await this.typebotService.find(instance);
|
||||
|
||||
if (saveData.enabled) {
|
||||
logger.verbose('typebot enabled');
|
||||
data.sessions = saveData.sessions;
|
||||
}
|
||||
}
|
||||
|
||||
return this.typebotService.create(instance, data);
|
||||
}
|
||||
|
||||
public async findTypebot(instance: InstanceDto) {
|
||||
logger.verbose('requested findTypebot from ' + instance.instanceName + ' instance');
|
||||
return this.typebotService.find(instance);
|
||||
}
|
||||
|
||||
public async changeStatus(instance: InstanceDto, data: any) {
|
||||
logger.verbose('requested changeStatus from ' + instance.instanceName + ' instance');
|
||||
return this.typebotService.changeStatus(instance, data);
|
||||
}
|
||||
|
||||
public async startTypebot(instance: InstanceDto, data: any) {
|
||||
logger.verbose('requested startTypebot from ' + instance.instanceName + ' instance');
|
||||
return this.typebotService.startTypebot(instance, data);
|
||||
}
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
import { Request, Response } from 'express';
|
||||
import { Auth, ConfigService } from '../../config/env.config';
|
||||
import { BadRequestException } from '../../exceptions';
|
||||
import { InstanceDto } from '../dto/instance.dto';
|
||||
import { HttpStatus } from '../routers/index.router';
|
||||
import { WAMonitoringService } from '../services/monitor.service';
|
||||
|
||||
export class ViewsController {
|
||||
constructor(
|
||||
private readonly waMonit: WAMonitoringService,
|
||||
private readonly configService: ConfigService,
|
||||
) {}
|
||||
|
||||
public async qrcode(request: Request, response: Response) {
|
||||
try {
|
||||
const param = request.params as unknown as InstanceDto;
|
||||
const instance = this.waMonit.waInstances[param.instanceName];
|
||||
if (instance.connectionStatus.state === 'open') {
|
||||
throw new BadRequestException('The instance is already connected');
|
||||
}
|
||||
const type = this.configService.get<Auth>('AUTHENTICATION').TYPE;
|
||||
|
||||
return response.status(HttpStatus.OK).render('qrcode', { type, ...param });
|
||||
} catch (error) {
|
||||
console.log('ERROR: ', error);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,26 +1,60 @@
|
||||
import { isURL } from 'class-validator';
|
||||
|
||||
import { Logger } from '../../config/logger.config';
|
||||
import { BadRequestException } from '../../exceptions';
|
||||
import { InstanceDto } from '../dto/instance.dto';
|
||||
import { WebhookDto } from '../dto/webhook.dto';
|
||||
import { WAMonitoringService } from '../services/monitor.service';
|
||||
import { WebhookService } from '../services/webhook.service';
|
||||
import { Logger } from '../../config/logger.config';
|
||||
|
||||
const logger = new Logger('WebhookController');
|
||||
|
||||
export class WebhookController {
|
||||
constructor(private readonly webhookService: WebhookService) {}
|
||||
constructor(private readonly webhookService: WebhookService, private readonly waMonitor: WAMonitoringService) {}
|
||||
|
||||
public async createWebhook(instance: InstanceDto, data: WebhookDto) {
|
||||
logger.verbose('requested createWebhook from ' + instance.instanceName + ' instance');
|
||||
|
||||
if (data.enabled && !isURL(data.url, { require_tld: false })) {
|
||||
if (!isURL(data.url, { require_tld: false })) {
|
||||
throw new BadRequestException('Invalid "url" property');
|
||||
}
|
||||
|
||||
data.enabled = data.enabled ?? true;
|
||||
|
||||
if (!data.enabled) {
|
||||
logger.verbose('webhook disabled');
|
||||
data.url = '';
|
||||
data.events = [];
|
||||
} else if (data.events.length === 0) {
|
||||
logger.verbose('webhook events empty');
|
||||
data.events = [
|
||||
'APPLICATION_STARTUP',
|
||||
'QRCODE_UPDATED',
|
||||
'MESSAGES_SET',
|
||||
'MESSAGES_UPSERT',
|
||||
'MESSAGES_UPDATE',
|
||||
'MESSAGES_DELETE',
|
||||
'SEND_MESSAGE',
|
||||
'CONTACTS_SET',
|
||||
'CONTACTS_UPSERT',
|
||||
'CONTACTS_UPDATE',
|
||||
'PRESENCE_UPDATE',
|
||||
'CHATS_SET',
|
||||
'CHATS_UPSERT',
|
||||
'CHATS_UPDATE',
|
||||
'CHATS_DELETE',
|
||||
'GROUPS_UPSERT',
|
||||
'GROUP_UPDATE',
|
||||
'GROUP_PARTICIPANTS_UPDATE',
|
||||
'CONNECTION_UPDATE',
|
||||
'LABELS_EDIT',
|
||||
'LABELS_ASSOCIATION',
|
||||
'CALL',
|
||||
'NEW_JWT_TOKEN',
|
||||
'TYPEBOT_START',
|
||||
'TYPEBOT_CHANGE_STATUS',
|
||||
'CHAMA_AI_ACTION',
|
||||
];
|
||||
}
|
||||
|
||||
return this.webhookService.create(instance, data);
|
||||
@@ -30,4 +64,9 @@ export class WebhookController {
|
||||
logger.verbose('requested findWebhook from ' + instance.instanceName + ' instance');
|
||||
return this.webhookService.find(instance);
|
||||
}
|
||||
|
||||
public async receiveWebhook(instance: InstanceDto, data: any) {
|
||||
logger.verbose('requested receiveWebhook from ' + instance.instanceName + ' instance');
|
||||
return await this.waMonitor.waInstances[instance.instanceName].connectToWhatsapp(data);
|
||||
}
|
||||
}
|
||||
|
||||
58
src/whatsapp/controllers/websocket.controller.ts
Normal file
58
src/whatsapp/controllers/websocket.controller.ts
Normal file
@@ -0,0 +1,58 @@
|
||||
import { Logger } from '../../config/logger.config';
|
||||
import { InstanceDto } from '../dto/instance.dto';
|
||||
import { WebsocketDto } from '../dto/websocket.dto';
|
||||
import { WebsocketService } from '../services/websocket.service';
|
||||
|
||||
const logger = new Logger('WebsocketController');
|
||||
|
||||
export class WebsocketController {
|
||||
constructor(private readonly websocketService: WebsocketService) {}
|
||||
|
||||
public async createWebsocket(instance: InstanceDto, data: WebsocketDto) {
|
||||
logger.verbose('requested createWebsocket from ' + instance.instanceName + ' instance');
|
||||
|
||||
if (!data.enabled) {
|
||||
logger.verbose('websocket disabled');
|
||||
data.events = [];
|
||||
}
|
||||
|
||||
if (data.events.length === 0) {
|
||||
logger.verbose('websocket events empty');
|
||||
data.events = [
|
||||
'APPLICATION_STARTUP',
|
||||
'QRCODE_UPDATED',
|
||||
'MESSAGES_SET',
|
||||
'MESSAGES_UPSERT',
|
||||
'MESSAGES_UPDATE',
|
||||
'MESSAGES_DELETE',
|
||||
'SEND_MESSAGE',
|
||||
'CONTACTS_SET',
|
||||
'CONTACTS_UPSERT',
|
||||
'CONTACTS_UPDATE',
|
||||
'PRESENCE_UPDATE',
|
||||
'CHATS_SET',
|
||||
'CHATS_UPSERT',
|
||||
'CHATS_UPDATE',
|
||||
'CHATS_DELETE',
|
||||
'GROUPS_UPSERT',
|
||||
'GROUP_UPDATE',
|
||||
'GROUP_PARTICIPANTS_UPDATE',
|
||||
'CONNECTION_UPDATE',
|
||||
'LABELS_EDIT',
|
||||
'LABELS_ASSOCIATION',
|
||||
'CALL',
|
||||
'NEW_JWT_TOKEN',
|
||||
'TYPEBOT_START',
|
||||
'TYPEBOT_CHANGE_STATUS',
|
||||
'CHAMA_AI_ACTION',
|
||||
];
|
||||
}
|
||||
|
||||
return this.websocketService.create(instance, data);
|
||||
}
|
||||
|
||||
public async findWebsocket(instance: InstanceDto) {
|
||||
logger.verbose('requested findWebsocket from ' + instance.instanceName + ' instance');
|
||||
return this.websocketService.find(instance);
|
||||
}
|
||||
}
|
||||
7
src/whatsapp/dto/chamaai.dto.ts
Normal file
7
src/whatsapp/dto/chamaai.dto.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
export class ChamaaiDto {
|
||||
enabled: boolean;
|
||||
url: string;
|
||||
token: string;
|
||||
waNumber: string;
|
||||
answerByAudio: boolean;
|
||||
}
|
||||
@@ -1,14 +1,10 @@
|
||||
import {
|
||||
WAPrivacyOnlineValue,
|
||||
WAPrivacyValue,
|
||||
WAReadReceiptsValue,
|
||||
proto,
|
||||
} from '@whiskeysockets/baileys';
|
||||
import { proto, WAPresence, WAPrivacyOnlineValue, WAPrivacyValue, WAReadReceiptsValue } from '@whiskeysockets/baileys';
|
||||
|
||||
export class OnWhatsAppDto {
|
||||
constructor(
|
||||
public readonly jid: string,
|
||||
public readonly exists: boolean,
|
||||
public readonly number: string,
|
||||
public readonly name?: string,
|
||||
) {}
|
||||
}
|
||||
@@ -26,6 +22,23 @@ export class NumberDto {
|
||||
number: string;
|
||||
}
|
||||
|
||||
export class NumberBusiness {
|
||||
wid?: string;
|
||||
jid?: string;
|
||||
exists?: boolean;
|
||||
isBusiness: boolean;
|
||||
name?: string;
|
||||
message?: string;
|
||||
description?: string;
|
||||
email?: string;
|
||||
websites?: string[];
|
||||
website?: string[];
|
||||
address?: string;
|
||||
about?: string;
|
||||
vertical?: string;
|
||||
profilehandle?: string;
|
||||
}
|
||||
|
||||
export class ProfileNameDto {
|
||||
name: string;
|
||||
}
|
||||
@@ -46,16 +59,17 @@ class Key {
|
||||
remoteJid: string;
|
||||
}
|
||||
export class ReadMessageDto {
|
||||
readMessages: Key[];
|
||||
read_messages: Key[];
|
||||
}
|
||||
|
||||
class LastMessage {
|
||||
export class LastMessage {
|
||||
key: Key;
|
||||
messageTimestamp?: number;
|
||||
}
|
||||
|
||||
export class ArchiveChatDto {
|
||||
lastMessage: LastMessage;
|
||||
lastMessage?: LastMessage;
|
||||
chat?: string;
|
||||
archive: boolean;
|
||||
}
|
||||
|
||||
@@ -78,3 +92,31 @@ export class DeleteMessage {
|
||||
remoteJid: string;
|
||||
participant?: string;
|
||||
}
|
||||
export class Options {
|
||||
delay?: number;
|
||||
presence?: WAPresence;
|
||||
}
|
||||
class OptionsMessage {
|
||||
options: Options;
|
||||
}
|
||||
export class Metadata extends OptionsMessage {
|
||||
number: string;
|
||||
}
|
||||
|
||||
export class SendPresenceDto extends Metadata {
|
||||
options: {
|
||||
presence: WAPresence;
|
||||
delay: number;
|
||||
};
|
||||
}
|
||||
|
||||
export class UpdateMessageDto extends Metadata {
|
||||
number: string;
|
||||
key: proto.IMessageKey;
|
||||
text: string;
|
||||
}
|
||||
|
||||
export class BlockUserDto {
|
||||
number: string;
|
||||
status: 'block' | 'unblock';
|
||||
}
|
||||
|
||||
@@ -5,4 +5,12 @@ export class ChatwootDto {
|
||||
url?: string;
|
||||
name_inbox?: string;
|
||||
sign_msg?: boolean;
|
||||
sign_delimiter?: string;
|
||||
number?: string;
|
||||
reopen_conversation?: boolean;
|
||||
conversation_pending?: boolean;
|
||||
import_contacts?: boolean;
|
||||
import_messages?: boolean;
|
||||
days_limit_import_messages?: number;
|
||||
auto_create?: boolean;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
export class CreateGroupDto {
|
||||
subject: string;
|
||||
description?: string;
|
||||
participants: string[];
|
||||
description?: string;
|
||||
promoteParticipants?: boolean;
|
||||
}
|
||||
|
||||
export class GroupPictureDto {
|
||||
@@ -31,6 +32,10 @@ export class GroupInvite {
|
||||
inviteCode: string;
|
||||
}
|
||||
|
||||
export class AcceptGroupInvite {
|
||||
inviteCode: string;
|
||||
}
|
||||
|
||||
export class GroupSendInvite {
|
||||
groupJid: string;
|
||||
description: string;
|
||||
|
||||
@@ -1,12 +1,48 @@
|
||||
import { WAPresence } from "@whiskeysockets/baileys";
|
||||
|
||||
export class InstanceDto {
|
||||
instanceName: string;
|
||||
instanceId?: string;
|
||||
qrcode?: boolean;
|
||||
number?: string;
|
||||
integration?: string;
|
||||
token?: string;
|
||||
webhook?: string;
|
||||
webhook_by_events?: boolean;
|
||||
webhook_base64?: boolean;
|
||||
events?: string[];
|
||||
qrcode?: boolean;
|
||||
token?: string;
|
||||
reject_call?: boolean;
|
||||
msg_call?: string;
|
||||
groups_ignore?: boolean;
|
||||
always_online?: boolean;
|
||||
read_messages?: boolean;
|
||||
read_status?: boolean;
|
||||
sync_full_history?: boolean;
|
||||
chatwoot_account_id?: string;
|
||||
chatwoot_token?: string;
|
||||
chatwoot_url?: string;
|
||||
chatwoot_sign_msg?: boolean;
|
||||
chatwoot_reopen_conversation?: boolean;
|
||||
chatwoot_conversation_pending?: boolean;
|
||||
chatwoot_import_contacts?: boolean;
|
||||
chatwoot_import_messages?: boolean;
|
||||
chatwoot_days_limit_import_messages?: number;
|
||||
websocket_enabled?: boolean;
|
||||
websocket_events?: string[];
|
||||
rabbitmq_enabled?: boolean;
|
||||
rabbitmq_events?: string[];
|
||||
sqs_enabled?: boolean;
|
||||
sqs_events?: string[];
|
||||
typebot_url?: string;
|
||||
typebot?: string;
|
||||
typebot_expire?: number;
|
||||
typebot_keyword_finish?: string;
|
||||
typebot_delay_message?: number;
|
||||
typebot_unknown_message?: string;
|
||||
typebot_listening_from_me?: boolean;
|
||||
proxy?: string;
|
||||
}
|
||||
|
||||
export class SetPresenceDto {
|
||||
presence: WAPresence;
|
||||
}
|
||||
|
||||
5
src/whatsapp/dto/integration.dto.ts
Normal file
5
src/whatsapp/dto/integration.dto.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
export class IntegrationDto {
|
||||
integration: string;
|
||||
number: string;
|
||||
token: string;
|
||||
}
|
||||
12
src/whatsapp/dto/label.dto.ts
Normal file
12
src/whatsapp/dto/label.dto.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
export class LabelDto {
|
||||
id?: string;
|
||||
name: string;
|
||||
color: number;
|
||||
predefinedId?: string;
|
||||
}
|
||||
|
||||
export class HandleLabelDto {
|
||||
number: string;
|
||||
labelId: string;
|
||||
action: 'add' | 'remove';
|
||||
}
|
||||
12
src/whatsapp/dto/proxy.dto.ts
Normal file
12
src/whatsapp/dto/proxy.dto.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
class Proxy {
|
||||
host: string;
|
||||
port: string;
|
||||
protocol: string;
|
||||
username?: string;
|
||||
password?: string;
|
||||
}
|
||||
|
||||
export class ProxyDto {
|
||||
enabled: boolean;
|
||||
proxy: Proxy;
|
||||
}
|
||||
4
src/whatsapp/dto/rabbitmq.dto.ts
Normal file
4
src/whatsapp/dto/rabbitmq.dto.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export class RabbitmqDto {
|
||||
enabled: boolean;
|
||||
events?: string[];
|
||||
}
|
||||
@@ -15,6 +15,8 @@ export class Options {
|
||||
presence?: WAPresence;
|
||||
quoted?: Quoted;
|
||||
mentions?: Mentions;
|
||||
linkPreview?: boolean;
|
||||
encoding?: boolean;
|
||||
}
|
||||
class OptionsMessage {
|
||||
options: Options;
|
||||
@@ -28,10 +30,6 @@ class TextMessage {
|
||||
text: string;
|
||||
}
|
||||
|
||||
class linkPreviewMessage {
|
||||
text: string;
|
||||
}
|
||||
|
||||
export class StatusMessage {
|
||||
type: string;
|
||||
content: string;
|
||||
@@ -48,12 +46,12 @@ class PollMessage {
|
||||
values: string[];
|
||||
messageSecret?: Uint8Array;
|
||||
}
|
||||
|
||||
export class SendTextDto extends Metadata {
|
||||
textMessage: TextMessage;
|
||||
}
|
||||
|
||||
export class SendLinkPreviewDto extends Metadata {
|
||||
linkPreview: linkPreviewMessage;
|
||||
export class SendPresence extends Metadata {
|
||||
textMessage: TextMessage;
|
||||
}
|
||||
|
||||
export class SendStatusDto extends Metadata {
|
||||
@@ -67,6 +65,7 @@ export class SendPollDto extends Metadata {
|
||||
export type MediaType = 'image' | 'document' | 'video' | 'audio';
|
||||
export class MediaMessage {
|
||||
mediatype: MediaType;
|
||||
mimetype?: string;
|
||||
caption?: string;
|
||||
// for document
|
||||
fileName?: string;
|
||||
@@ -143,6 +142,16 @@ export class ContactMessage {
|
||||
email?: string;
|
||||
url?: string;
|
||||
}
|
||||
|
||||
export class TemplateMessage {
|
||||
name: string;
|
||||
language: string;
|
||||
components: any;
|
||||
}
|
||||
|
||||
export class SendTemplateDto extends Metadata {
|
||||
templateMessage: TemplateMessage;
|
||||
}
|
||||
export class SendContactDto extends Metadata {
|
||||
contactMessage: ContactMessage[];
|
||||
}
|
||||
|
||||
9
src/whatsapp/dto/settings.dto.ts
Normal file
9
src/whatsapp/dto/settings.dto.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
export class SettingsDto {
|
||||
reject_call?: boolean;
|
||||
msg_call?: string;
|
||||
groups_ignore?: boolean;
|
||||
always_online?: boolean;
|
||||
read_messages?: boolean;
|
||||
read_status?: boolean;
|
||||
sync_full_history?: boolean;
|
||||
}
|
||||
4
src/whatsapp/dto/sqs.dto.ts
Normal file
4
src/whatsapp/dto/sqs.dto.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export class SqsDto {
|
||||
enabled: boolean;
|
||||
events?: string[];
|
||||
}
|
||||
27
src/whatsapp/dto/typebot.dto.ts
Normal file
27
src/whatsapp/dto/typebot.dto.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
export class Session {
|
||||
remoteJid?: string;
|
||||
sessionId?: string;
|
||||
status?: string;
|
||||
createdAt?: number;
|
||||
updateAt?: number;
|
||||
prefilledVariables?: PrefilledVariables;
|
||||
}
|
||||
|
||||
export class PrefilledVariables {
|
||||
remoteJid?: string;
|
||||
pushName?: string;
|
||||
messageType?: string;
|
||||
additionalData?: { [key: string]: any };
|
||||
}
|
||||
|
||||
export class TypebotDto {
|
||||
enabled?: boolean;
|
||||
url: string;
|
||||
typebot?: string;
|
||||
expire?: number;
|
||||
keyword_finish?: string;
|
||||
delay_message?: number;
|
||||
unknown_message?: string;
|
||||
listening_from_me?: boolean;
|
||||
sessions?: Session[];
|
||||
}
|
||||
@@ -3,4 +3,5 @@ export class WebhookDto {
|
||||
url?: string;
|
||||
events?: string[];
|
||||
webhook_by_events?: boolean;
|
||||
webhook_base64?: boolean;
|
||||
}
|
||||
|
||||
4
src/whatsapp/dto/websocket.dto.ts
Normal file
4
src/whatsapp/dto/websocket.dto.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export class WebsocketDto {
|
||||
enabled: boolean;
|
||||
events?: string[];
|
||||
}
|
||||
@@ -1,12 +1,13 @@
|
||||
import { isJWT } from 'class-validator';
|
||||
import { NextFunction, Request, Response } from 'express';
|
||||
import jwt from 'jsonwebtoken';
|
||||
|
||||
import { name } from '../../../package.json';
|
||||
import { Auth, configService } from '../../config/env.config';
|
||||
import { Logger } from '../../config/logger.config';
|
||||
import { name } from '../../../package.json';
|
||||
import { ForbiddenException, UnauthorizedException } from '../../exceptions';
|
||||
import { InstanceDto } from '../dto/instance.dto';
|
||||
import { JwtPayload } from '../services/auth.service';
|
||||
import { ForbiddenException, UnauthorizedException } from '../../exceptions';
|
||||
import { repository } from '../whatsapp.module';
|
||||
|
||||
const logger = new Logger('GUARD');
|
||||
@@ -22,15 +23,8 @@ async function jwtGuard(req: Request, res: Response, next: NextFunction) {
|
||||
return next();
|
||||
}
|
||||
|
||||
if (
|
||||
(req.originalUrl.includes('/instance/create') ||
|
||||
req.originalUrl.includes('/instance/fetchInstances')) &&
|
||||
!key
|
||||
) {
|
||||
throw new ForbiddenException(
|
||||
'Missing global api key',
|
||||
'The global api key must be set',
|
||||
);
|
||||
if ((req.originalUrl.includes('/instance/create') || req.originalUrl.includes('/instance/fetchInstances')) && !key) {
|
||||
throw new ForbiddenException('Missing global api key', 'The global api key must be set');
|
||||
}
|
||||
|
||||
const jwtOpts = configService.get<Auth>('AUTHENTICATION').JWT;
|
||||
@@ -61,7 +55,7 @@ async function jwtGuard(req: Request, res: Response, next: NextFunction) {
|
||||
}
|
||||
}
|
||||
|
||||
async function apikey(req: Request, res: Response, next: NextFunction) {
|
||||
async function apikey(req: Request, _: Response, next: NextFunction) {
|
||||
const env = configService.get<Auth>('AUTHENTICATION').API_KEY;
|
||||
const key = req.get('apikey');
|
||||
|
||||
@@ -69,15 +63,8 @@ async function apikey(req: Request, res: Response, next: NextFunction) {
|
||||
return next();
|
||||
}
|
||||
|
||||
if (
|
||||
(req.originalUrl.includes('/instance/create') ||
|
||||
req.originalUrl.includes('/instance/fetchInstances')) &&
|
||||
!key
|
||||
) {
|
||||
throw new ForbiddenException(
|
||||
'Missing global api key',
|
||||
'The global api key must be set',
|
||||
);
|
||||
if ((req.originalUrl.includes('/instance/create') || req.originalUrl.includes('/instance/fetchInstances')) && !key) {
|
||||
throw new ForbiddenException('Missing global api key', 'The global api key must be set');
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -86,7 +73,9 @@ async function apikey(req: Request, res: Response, next: NextFunction) {
|
||||
if (instanceKey.apikey === key) {
|
||||
return next();
|
||||
}
|
||||
} catch (error) {}
|
||||
} catch (error) {
|
||||
logger.error(error);
|
||||
}
|
||||
|
||||
throw new UnauthorizedException();
|
||||
}
|
||||
|
||||
@@ -1,45 +1,47 @@
|
||||
import { NextFunction, Request, Response } from 'express';
|
||||
import { existsSync } from 'fs';
|
||||
import { join } from 'path';
|
||||
|
||||
import { configService, Database, Redis } from '../../config/env.config';
|
||||
import { INSTANCE_DIR } from '../../config/path.config';
|
||||
import { dbserver } from '../../db/db.connect';
|
||||
import {
|
||||
BadRequestException,
|
||||
ForbiddenException,
|
||||
InternalServerErrorException,
|
||||
NotFoundException,
|
||||
} from '../../exceptions';
|
||||
import { dbserver } from '../../libs/db.connect';
|
||||
import { InstanceDto } from '../dto/instance.dto';
|
||||
import { cache, waMonitor } from '../whatsapp.module';
|
||||
import { Database, Redis, configService } from '../../config/env.config';
|
||||
import { RedisCache } from '../../db/redis.client';
|
||||
|
||||
async function getInstance(instanceName: string) {
|
||||
const db = configService.get<Database>('DATABASE');
|
||||
const redisConf = configService.get<Redis>('REDIS');
|
||||
try {
|
||||
const db = configService.get<Database>('DATABASE');
|
||||
const redisConf = configService.get<Redis>('REDIS');
|
||||
|
||||
const exists = !!waMonitor.waInstances[instanceName];
|
||||
const exists = !!waMonitor.waInstances[instanceName];
|
||||
|
||||
if (redisConf.ENABLED) {
|
||||
const keyExists = await cache.keyExists();
|
||||
return exists || keyExists;
|
||||
if (redisConf.ENABLED) {
|
||||
const keyExists = await cache.keyExists();
|
||||
return exists || keyExists;
|
||||
}
|
||||
|
||||
if (db.ENABLED) {
|
||||
const collection = dbserver
|
||||
.getClient()
|
||||
.db(db.CONNECTION.DB_PREFIX_NAME + '-instances')
|
||||
.collection(instanceName);
|
||||
return exists || (await collection.find({}).toArray()).length > 0;
|
||||
}
|
||||
|
||||
return exists || existsSync(join(INSTANCE_DIR, instanceName));
|
||||
} catch (error) {
|
||||
throw new InternalServerErrorException(error?.toString());
|
||||
}
|
||||
|
||||
if (db.ENABLED) {
|
||||
const collection = dbserver
|
||||
.getClient()
|
||||
.db(db.CONNECTION.DB_PREFIX_NAME + '-instances')
|
||||
.collection(instanceName);
|
||||
return exists || (await collection.find({}).toArray()).length > 0;
|
||||
}
|
||||
|
||||
return exists || existsSync(join(INSTANCE_DIR, instanceName));
|
||||
}
|
||||
|
||||
export async function instanceExistsGuard(req: Request, _: Response, next: NextFunction) {
|
||||
if (
|
||||
req.originalUrl.includes('/instance/create') ||
|
||||
req.originalUrl.includes('/instance/fetchInstances')
|
||||
) {
|
||||
if (req.originalUrl.includes('/instance/create') || req.originalUrl.includes('/instance/fetchInstances')) {
|
||||
return next();
|
||||
}
|
||||
|
||||
@@ -59,12 +61,11 @@ export async function instanceLoggedGuard(req: Request, _: Response, next: NextF
|
||||
if (req.originalUrl.includes('/instance/create')) {
|
||||
const instance = req.body as InstanceDto;
|
||||
if (await getInstance(instance.instanceName)) {
|
||||
throw new ForbiddenException(
|
||||
`This name "${instance.instanceName}" is already in use.`,
|
||||
);
|
||||
throw new ForbiddenException(`This name "${instance.instanceName}" is already in use.`);
|
||||
}
|
||||
|
||||
if (waMonitor.waInstances[instance.instanceName]) {
|
||||
waMonitor.waInstances[instance.instanceName]?.removeRabbitmqQueues();
|
||||
delete waMonitor.waInstances[instance.instanceName];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +1,19 @@
|
||||
import { Schema } from 'mongoose';
|
||||
import { dbserver } from '../../db/db.connect';
|
||||
|
||||
import { dbserver } from '../../libs/db.connect';
|
||||
|
||||
export class AuthRaw {
|
||||
_id?: string;
|
||||
jwt?: string;
|
||||
apikey?: string;
|
||||
instanceId?: string;
|
||||
}
|
||||
|
||||
const authSchema = new Schema<AuthRaw>({
|
||||
_id: { type: String, _id: true },
|
||||
jwt: { type: String, minlength: 1 },
|
||||
apikey: { type: String, minlength: 1 },
|
||||
instanceId: { type: String, minlength: 1 },
|
||||
});
|
||||
|
||||
export const AuthModel = dbserver?.model(AuthRaw.name, authSchema, 'authentication');
|
||||
|
||||
24
src/whatsapp/models/chamaai.model.ts
Normal file
24
src/whatsapp/models/chamaai.model.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import { Schema } from 'mongoose';
|
||||
|
||||
import { dbserver } from '../../libs/db.connect';
|
||||
|
||||
export class ChamaaiRaw {
|
||||
_id?: string;
|
||||
enabled?: boolean;
|
||||
url?: string;
|
||||
token?: string;
|
||||
waNumber?: string;
|
||||
answerByAudio?: boolean;
|
||||
}
|
||||
|
||||
const chamaaiSchema = new Schema<ChamaaiRaw>({
|
||||
_id: { type: String, _id: true },
|
||||
enabled: { type: Boolean, required: true },
|
||||
url: { type: String, required: true },
|
||||
token: { type: String, required: true },
|
||||
waNumber: { type: String, required: true },
|
||||
answerByAudio: { type: Boolean, required: true },
|
||||
});
|
||||
|
||||
export const ChamaaiModel = dbserver?.model(ChamaaiRaw.name, chamaaiSchema, 'chamaai');
|
||||
export type IChamaaiModel = typeof ChamaaiModel;
|
||||
@@ -1,17 +1,25 @@
|
||||
import { Schema } from 'mongoose';
|
||||
import { dbserver } from '../../db/db.connect';
|
||||
|
||||
import { dbserver } from '../../libs/db.connect';
|
||||
|
||||
export class ChatRaw {
|
||||
_id?: string;
|
||||
id?: string;
|
||||
owner: string;
|
||||
lastMsgTimestamp?: number;
|
||||
labels?: string[];
|
||||
}
|
||||
|
||||
type ChatRawBoolean<T> = {
|
||||
[P in keyof T]?: 0 | 1;
|
||||
};
|
||||
export type ChatRawSelect = ChatRawBoolean<ChatRaw>;
|
||||
|
||||
const chatSchema = new Schema<ChatRaw>({
|
||||
_id: { type: String, _id: true },
|
||||
id: { type: String, required: true, minlength: 1 },
|
||||
owner: { type: String, required: true, minlength: 1 },
|
||||
labels: { type: [String], default: [] },
|
||||
});
|
||||
|
||||
export const ChatModel = dbserver?.model(ChatRaw.name, chatSchema, 'chats');
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Schema } from 'mongoose';
|
||||
import { dbserver } from '../../db/db.connect';
|
||||
|
||||
import { dbserver } from '../../libs/db.connect';
|
||||
|
||||
export class ChatwootRaw {
|
||||
_id?: string;
|
||||
@@ -9,6 +10,13 @@ export class ChatwootRaw {
|
||||
url?: string;
|
||||
name_inbox?: string;
|
||||
sign_msg?: boolean;
|
||||
sign_delimiter?: string;
|
||||
number?: string;
|
||||
reopen_conversation?: boolean;
|
||||
conversation_pending?: boolean;
|
||||
import_contacts?: boolean;
|
||||
import_messages?: boolean;
|
||||
days_limit_import_messages?: number;
|
||||
}
|
||||
|
||||
const chatwootSchema = new Schema<ChatwootRaw>({
|
||||
@@ -19,11 +27,14 @@ const chatwootSchema = new Schema<ChatwootRaw>({
|
||||
url: { type: String, required: true },
|
||||
name_inbox: { type: String, required: true },
|
||||
sign_msg: { type: Boolean, required: true },
|
||||
sign_delimiter: { type: String, required: false },
|
||||
number: { type: String, required: true },
|
||||
reopen_conversation: { type: Boolean, required: true },
|
||||
conversation_pending: { type: Boolean, required: true },
|
||||
import_contacts: { type: Boolean, required: true },
|
||||
import_messages: { type: Boolean, required: true },
|
||||
days_limit_import_messages: { type: Number, required: true },
|
||||
});
|
||||
|
||||
export const ChatwootModel = dbserver?.model(
|
||||
ChatwootRaw.name,
|
||||
chatwootSchema,
|
||||
'chatwoot',
|
||||
);
|
||||
export const ChatwootModel = dbserver?.model(ChatwootRaw.name, chatwootSchema, 'chatwoot');
|
||||
export type IChatwootModel = typeof ChatwootModel;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Schema } from 'mongoose';
|
||||
import { dbserver } from '../../db/db.connect';
|
||||
|
||||
import { dbserver } from '../../libs/db.connect';
|
||||
|
||||
export class ContactRaw {
|
||||
_id?: string;
|
||||
@@ -9,6 +10,11 @@ export class ContactRaw {
|
||||
owner: string;
|
||||
}
|
||||
|
||||
type ContactRawBoolean<T> = {
|
||||
[P in keyof T]?: 0 | 1;
|
||||
};
|
||||
export type ContactRawSelect = ContactRawBoolean<ContactRaw>;
|
||||
|
||||
const contactSchema = new Schema<ContactRaw>({
|
||||
_id: { type: String, _id: true },
|
||||
pushName: { type: String, minlength: 1 },
|
||||
|
||||
@@ -1,6 +1,15 @@
|
||||
export * from './chat.model';
|
||||
export * from './contact.model';
|
||||
export * from './message.model';
|
||||
export * from './auth.model';
|
||||
export * from './webhook.model';
|
||||
export * from './chamaai.model';
|
||||
export * from './chat.model';
|
||||
export * from './chatwoot.model';
|
||||
export * from './contact.model';
|
||||
export * from './integration.model';
|
||||
export * from './label.model';
|
||||
export * from './message.model';
|
||||
export * from './proxy.model';
|
||||
export * from './rabbitmq.model';
|
||||
export * from './settings.model';
|
||||
export * from './sqs.model';
|
||||
export * from './typebot.model';
|
||||
export * from './webhook.model';
|
||||
export * from './websocket.model';
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user