mirror of
https://github.com/EvolutionAPI/evolution-api.git
synced 2025-12-19 03:42:23 -06:00
Compare commits
365 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 | ||
|
|
04a6f7c954 |
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.
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -39,6 +39,7 @@ docker-compose.yaml
|
||||
/test/
|
||||
/src/env.yml
|
||||
/store
|
||||
*.env
|
||||
|
||||
/temp/*
|
||||
|
||||
|
||||
8
.vscode/settings.json
vendored
8
.vscode/settings.json
vendored
@@ -5,7 +5,9 @@
|
||||
"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"
|
||||
}
|
||||
157
CHANGELOG.md
157
CHANGELOG.md
@@ -1,3 +1,144 @@
|
||||
# 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
|
||||
@@ -43,7 +184,7 @@
|
||||
|
||||
### Fixed
|
||||
|
||||
* Fixed validation is set settings
|
||||
* Fixed validation is set settings
|
||||
* Adjusts in group validations
|
||||
* Ajusts in sticker message to chatwoot
|
||||
|
||||
@@ -78,7 +219,7 @@
|
||||
|
||||
### Integrations
|
||||
|
||||
- Chatwoot: v2.18.0 - v3.0.0 (Beta)
|
||||
* Chatwoot: v2.18.0 - v3.0.0 (Beta)
|
||||
|
||||
# 1.3.2 (2023-07-21 17:19)
|
||||
|
||||
@@ -94,7 +235,7 @@
|
||||
|
||||
### Integrations
|
||||
|
||||
- Chatwoot: v2.18.0
|
||||
* Chatwoot: v2.18.0
|
||||
|
||||
# 1.3.1 (2023-07-20 07:48)
|
||||
|
||||
@@ -104,7 +245,7 @@
|
||||
|
||||
### Integrations
|
||||
|
||||
- Chatwoot: v2.18.0
|
||||
* Chatwoot: v2.18.0
|
||||
|
||||
# 1.3.0 (2023-07-19 11:33)
|
||||
|
||||
@@ -120,7 +261,7 @@
|
||||
* 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
|
||||
@@ -141,7 +282,7 @@
|
||||
|
||||
### Integrations
|
||||
|
||||
- Chatwoot: v2.18.0
|
||||
* Chatwoot: v2.18.0
|
||||
|
||||
# 1.2.2 (2023-07-15 09:36)
|
||||
|
||||
@@ -152,7 +293,7 @@
|
||||
|
||||
### Integrations
|
||||
|
||||
- Chatwoot: v2.18.0
|
||||
* Chatwoot: v2.18.0
|
||||
|
||||
# 1.2.1 (2023-07-14 19:04)
|
||||
|
||||
@@ -308,4 +449,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.
|
||||
|
||||
@@ -31,7 +31,7 @@ CLEAN_STORE_CONTACTS=true
|
||||
CLEAN_STORE_CHATS=true
|
||||
|
||||
# Permanent data storage
|
||||
DATABASE_ENABLED=true
|
||||
DATABASE_ENABLED=false
|
||||
DATABASE_CONNECTION_URI=mongodb://root:root@mongodb:27017/?authSource=admin&readPreference=primary&ssl=false&directConnection=true
|
||||
DATABASE_CONNECTION_DB_PREFIX_NAME=evdocker
|
||||
|
||||
@@ -42,10 +42,21 @@ DATABASE_SAVE_MESSAGE_UPDATE=false
|
||||
DATABASE_SAVE_DATA_CONTACTS=false
|
||||
DATABASE_SAVE_DATA_CHATS=false
|
||||
|
||||
REDIS_ENABLED=true
|
||||
REDIS_ENABLED=false
|
||||
REDIS_URI=redis://redis:6379
|
||||
REDIS_PREFIX_KEY=evdocker
|
||||
|
||||
RABBITMQ_ENABLED=false
|
||||
RABBITMQ_URI=amqp://guest:guest@rabbitmq:5672
|
||||
|
||||
WEBSOCKET_ENABLED=false
|
||||
|
||||
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
|
||||
@@ -76,14 +87,27 @@ WEBHOOK_EVENTS_CONNECTION_UPDATE=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=EvolutionAPI
|
||||
# Browser Name = chrome | firefox | edge | opera | safari
|
||||
CONFIG_SESSION_PHONE_NAME=chrome
|
||||
# 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
|
||||
|
||||
# Defines an authentication type for the api
|
||||
# We recommend using the apikey because it will allow you to use a custom token,
|
||||
|
||||
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: davidsongomes/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:
|
||||
109
Docker/evolution-api-all-services/.env.example
Normal file
109
Docker/evolution-api-all-services/.env.example
Normal file
@@ -0,0 +1,109 @@
|
||||
# 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
|
||||
|
||||
# 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
|
||||
# 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: davidsongomes/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
|
||||
|
||||
@@ -35,7 +35,8 @@ volumes:
|
||||
evolution_mongodb_data:
|
||||
evolution_mongodb_configdb:
|
||||
|
||||
|
||||
networks:
|
||||
evolution-net:
|
||||
external: true
|
||||
|
||||
name: evolution-net
|
||||
driver: bridge
|
||||
|
||||
@@ -5,17 +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
|
||||
|
||||
|
||||
volumes:
|
||||
evolution_redis:
|
||||
|
||||
|
||||
networks:
|
||||
evolution-net:
|
||||
external: true
|
||||
name: evolution-net
|
||||
driver: bridge
|
||||
|
||||
51
Dockerfile
51
Dockerfile
@@ -1,18 +1,29 @@
|
||||
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.6.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 .
|
||||
|
||||
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=*
|
||||
@@ -50,12 +61,25 @@ ENV REDIS_ENABLED=false
|
||||
ENV REDIS_URI=redis://redis:6379
|
||||
ENV REDIS_PREFIX_KEY=evolution
|
||||
|
||||
ENV WEBHOOK_GLOBAL_URL=<url>
|
||||
ENV RABBITMQ_ENABLED=false
|
||||
ENV RABBITMQ_URI=amqp://guest:guest@rabbitmq:5672
|
||||
|
||||
ENV WEBSOCKET_ENABLED=false
|
||||
|
||||
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
|
||||
@@ -78,10 +102,21 @@ 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 CONFIG_SESSION_PHONE_NAME=Chrome
|
||||
|
||||
ENV QRCODE_LIMIT=30
|
||||
ENV QRCODE_COLOR=#198754
|
||||
|
||||
ENV TYPEBOT_API_VERSION=latest
|
||||
|
||||
ENV AUTHENTICATION_TYPE=apikey
|
||||
|
||||
@@ -99,10 +134,8 @@ ENV AUTHENTICATION_INSTANCE_CHATWOOT_ACCOUNT_ID=1
|
||||
ENV AUTHENTICATION_INSTANCE_CHATWOOT_TOKEN=123456
|
||||
ENV AUTHENTICATION_INSTANCE_CHATWOOT_URL=<url>
|
||||
|
||||
RUN npm install
|
||||
WORKDIR /evolution
|
||||
|
||||
COPY . .
|
||||
|
||||
RUN npm run build
|
||||
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,6 +4,7 @@ services:
|
||||
api:
|
||||
container_name: evolution_api
|
||||
image: evolution/api:local
|
||||
build: .
|
||||
restart: always
|
||||
ports:
|
||||
- 8080:8080
|
||||
@@ -24,5 +25,5 @@ volumes:
|
||||
|
||||
networks:
|
||||
evolution-net:
|
||||
external: true
|
||||
|
||||
name: evolution-net
|
||||
driver: bridge
|
||||
|
||||
@@ -4,6 +4,7 @@ services:
|
||||
api:
|
||||
container_name: evolution_api
|
||||
image: evolution/api:local
|
||||
build: .
|
||||
restart: always
|
||||
ports:
|
||||
- 8080:8080
|
||||
@@ -75,5 +76,5 @@ volumes:
|
||||
|
||||
networks:
|
||||
evolution-net:
|
||||
external: true
|
||||
|
||||
name: evolution-net
|
||||
driver: bridge
|
||||
|
||||
@@ -3,7 +3,7 @@ version: '3.3'
|
||||
services:
|
||||
api:
|
||||
container_name: evolution_api
|
||||
image: davidsongomes/evolution-api:latest
|
||||
image: atendai/evolution-api:latest
|
||||
restart: always
|
||||
ports:
|
||||
- 8080:8080
|
||||
@@ -24,5 +24,5 @@ volumes:
|
||||
|
||||
networks:
|
||||
evolution-net:
|
||||
external: true
|
||||
|
||||
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
|
||||
20
package.json
20
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "evolution-api",
|
||||
"version": "1.4.7",
|
||||
"version": "1.6.1",
|
||||
"description": "Rest api for communication with WhatsApp",
|
||||
"main": "./dist/src/main.js",
|
||||
"scripts": {
|
||||
@@ -43,10 +43,12 @@
|
||||
"dependencies": {
|
||||
"@adiwajshing/keyed-db": "^0.2.4",
|
||||
"@ffmpeg-installer/ffmpeg": "^1.1.0",
|
||||
"@figuro/chatwoot-sdk": "^1.1.14",
|
||||
"@figuro/chatwoot-sdk": "^1.1.16",
|
||||
"@hapi/boom": "^10.0.1",
|
||||
"@sentry/node": "^7.59.2",
|
||||
"@whiskeysockets/baileys": "github:EvolutionAPI/Baileys",
|
||||
"@whiskeysockets/baileys": "github:PurpShell/Baileys#combined",
|
||||
"amqplib": "^0.10.3",
|
||||
"aws-sdk": "^2.1499.0",
|
||||
"axios": "^1.3.5",
|
||||
"class-validator": "^0.13.2",
|
||||
"compression": "^1.7.4",
|
||||
@@ -54,6 +56,7 @@
|
||||
"cross-env": "^7.0.3",
|
||||
"dayjs": "^1.11.7",
|
||||
"eventemitter2": "^6.4.9",
|
||||
"evolution-manager": "^0.4.11",
|
||||
"exiftool-vendored": "^22.0.0",
|
||||
"express": "^4.18.2",
|
||||
"express-async-errors": "^3.1.1",
|
||||
@@ -63,17 +66,23 @@
|
||||
"js-yaml": "^4.1.0",
|
||||
"jsonschema": "^1.4.1",
|
||||
"jsonwebtoken": "^8.5.1",
|
||||
"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",
|
||||
"pino": "^8.11.0",
|
||||
"proxy-agent": "^6.2.1",
|
||||
"proxy-agent": "^6.3.0",
|
||||
"qrcode": "^1.5.1",
|
||||
"qrcode-terminal": "^0.12.0",
|
||||
"redis": "^4.6.5",
|
||||
"sharp": "^0.30.7",
|
||||
"uuid": "^9.0.0"
|
||||
"socket.io": "^4.7.1",
|
||||
"socks-proxy-agent": "^8.0.1",
|
||||
"swagger-ui-express": "^5.0.0",
|
||||
"uuid": "^9.0.0",
|
||||
"yamljs": "^0.3.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/compression": "^1.7.2",
|
||||
@@ -83,6 +92,7 @@
|
||||
"@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",
|
||||
|
||||
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 |
@@ -3,7 +3,13 @@ import { readFileSync } from 'fs';
|
||||
import { load } from 'js-yaml';
|
||||
import { join } from 'path';
|
||||
|
||||
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 = {
|
||||
@@ -61,8 +67,27 @@ export type Redis = {
|
||||
PREFIX_KEY: string;
|
||||
};
|
||||
|
||||
export type Rabbitmq = {
|
||||
ENABLED: boolean;
|
||||
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 EventsWebhook = {
|
||||
APPLICATION_STARTUP: boolean;
|
||||
INSTANCE_CREATE: boolean;
|
||||
INSTANCE_DELETE: boolean;
|
||||
QRCODE_UPDATED: boolean;
|
||||
MESSAGES_SET: boolean;
|
||||
MESSAGES_UPSERT: boolean;
|
||||
@@ -83,6 +108,11 @@ export type EventsWebhook = {
|
||||
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 };
|
||||
@@ -105,7 +135,8 @@ export type GlobalWebhook = {
|
||||
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 Production = boolean;
|
||||
|
||||
export interface Env {
|
||||
@@ -116,11 +147,15 @@ export interface Env {
|
||||
CLEAN_STORE: CleanStoreConf;
|
||||
DATABASE: Database;
|
||||
REDIS: Redis;
|
||||
RABBITMQ: Rabbitmq;
|
||||
SQS: Sqs;
|
||||
WEBSOCKET: Websocket;
|
||||
LOG: Log;
|
||||
DEL_INSTANCE: DelInstance;
|
||||
WEBHOOK: Webhook;
|
||||
CONFIG_SESSION_PHONE: ConfigSessionPhone;
|
||||
QRCODE: QrCode;
|
||||
TYPEBOT: Typebot;
|
||||
AUTHENTICATION: Auth;
|
||||
PRODUCTION?: Production;
|
||||
}
|
||||
@@ -142,8 +177,8 @@ 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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -154,18 +189,20 @@ export class ConfigService {
|
||||
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',
|
||||
@@ -184,8 +221,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: {
|
||||
@@ -198,11 +235,34 @@ export class ConfigService {
|
||||
},
|
||||
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',
|
||||
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',
|
||||
},
|
||||
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',
|
||||
},
|
||||
@@ -211,12 +271,14 @@ export class ConfigService {
|
||||
: Number.parseInt(process.env.DEL_INSTANCE) || false,
|
||||
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',
|
||||
@@ -237,26 +299,36 @@ export class ConfigService {
|
||||
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 || 'Evolution API',
|
||||
NAME: process.env?.CONFIG_SESSION_PHONE_NAME || 'chrome',
|
||||
NAME: process.env?.CONFIG_SESSION_PHONE_NAME || 'Chrome',
|
||||
},
|
||||
QRCODE: {
|
||||
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',
|
||||
},
|
||||
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',
|
||||
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,
|
||||
SECRET: process.env.AUTHENTICATION_JWT_SECRET || 'L=0YWt]b2w[WF>#>:&E`',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
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)
|
||||
@@ -74,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,9 @@ SERVER:
|
||||
TYPE: http # https
|
||||
PORT: 8080 # 443
|
||||
URL: localhost
|
||||
DISABLE_MANAGER: false
|
||||
DISABLE_DOCS: false
|
||||
|
||||
|
||||
CORS:
|
||||
ORIGIN:
|
||||
@@ -79,6 +82,20 @@ REDIS:
|
||||
URI: "redis://localhost:6379"
|
||||
PREFIX_KEY: "evolution"
|
||||
|
||||
RABBITMQ:
|
||||
ENABLED: false
|
||||
URI: "amqp://guest:guest@localhost:5672"
|
||||
|
||||
SQS:
|
||||
ENABLED: true
|
||||
ACCESS_KEY_ID: ""
|
||||
SECRET_ACCESS_KEY: ""
|
||||
ACCOUNT_ID: ""
|
||||
REGION: "us-east-1"
|
||||
|
||||
WEBSOCKET:
|
||||
ENABLED: false
|
||||
|
||||
# Global Webhook Settings
|
||||
# Each instance's Webhook URL and events will be requested at the time it is created
|
||||
WEBHOOK:
|
||||
@@ -113,15 +130,28 @@ WEBHOOK:
|
||||
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
|
||||
|
||||
# Defines an authentication type for the api
|
||||
# We recommend using the apikey because it will allow you to use a custom token,
|
||||
@@ -137,4 +167,4 @@ AUTHENTICATION:
|
||||
# Set the secret key to encrypt and decrypt your token and its expiration time.
|
||||
JWT:
|
||||
EXPIRIN_IN: 0 # seconds - 3600s === 1h | zero (0) - never expires
|
||||
SECRET: L=0YWt]b2w[WF>#>:&E`
|
||||
SECRET: L=0YWt]b2w[WF>#>:&E`
|
||||
|
||||
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',
|
||||
}),
|
||||
);
|
||||
2597
src/docs/swagger.yaml
Normal file
2597
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);
|
||||
};
|
||||
@@ -5,45 +5,55 @@ 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) {
|
||||
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`);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
});
|
||||
};
|
||||
158
src/main.ts
158
src/main.ts
@@ -1,90 +1,138 @@
|
||||
import 'express-async-errors';
|
||||
|
||||
import axios from 'axios';
|
||||
import compression from 'compression';
|
||||
import cors from 'cors';
|
||||
import express, { json, NextFunction, Request, Response, urlencoded } from 'express';
|
||||
import { join } from 'path';
|
||||
|
||||
import { configService, Cors, HttpServer } from './config/env.config';
|
||||
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 { 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();
|
||||
waMonitor.loadInstance();
|
||||
}
|
||||
|
||||
function bootstrap() {
|
||||
const logger = new Logger('SERVER');
|
||||
const app = express();
|
||||
const logger = new Logger('SERVER');
|
||||
const app = express();
|
||||
|
||||
app.use(
|
||||
cors({
|
||||
origin(requestOrigin, callback) {
|
||||
const { ORIGIN } = configService.get<Cors>('CORS');
|
||||
!requestOrigin ? (requestOrigin = '*') : undefined;
|
||||
if (ORIGIN.indexOf(requestOrigin) !== -1) {
|
||||
return callback(null, true);
|
||||
}
|
||||
return callback(new Error('Not allowed by CORS'));
|
||||
app.use(
|
||||
cors({
|
||||
origin(requestOrigin, callback) {
|
||||
const { ORIGIN } = configService.get<Cors>('CORS');
|
||||
if (ORIGIN.includes('*')) {
|
||||
return callback(null, true);
|
||||
}
|
||||
if (ORIGIN.indexOf(requestOrigin) !== -1) {
|
||||
return callback(null, true);
|
||||
}
|
||||
return callback(new Error('Not allowed by CORS'));
|
||||
},
|
||||
methods: [...configService.get<Cors>('CORS').METHODS],
|
||||
credentials: configService.get<Cors>('CORS').CREDENTIALS,
|
||||
}),
|
||||
urlencoded({ extended: true, limit: '136mb' }),
|
||||
json({ limit: '136mb' }),
|
||||
compression(),
|
||||
);
|
||||
|
||||
app.set('view engine', 'hbs');
|
||||
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) {
|
||||
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',
|
||||
},
|
||||
},
|
||||
methods: [...configService.get<Cors>('CORS').METHODS],
|
||||
credentials: configService.get<Cors>('CORS').CREDENTIALS,
|
||||
}),
|
||||
urlencoded({ extended: true, limit: '136mb' }),
|
||||
json({ limit: '136mb' }),
|
||||
compression(),
|
||||
);
|
||||
date_time: now,
|
||||
api_key: globalApiKey,
|
||||
server_url: serverUrl,
|
||||
};
|
||||
|
||||
app.set('view engine', 'hbs');
|
||||
app.set('views', join(ROOT_DIR, 'views'));
|
||||
app.use(express.static(join(ROOT_DIR, 'public')));
|
||||
logger.error(errorData);
|
||||
|
||||
app.use('/', router);
|
||||
const baseURL = webhook.EVENTS.ERRORS_WEBHOOK;
|
||||
const httpService = axios.create({ baseURL });
|
||||
|
||||
app.use(
|
||||
(err: Error, req: Request, res: Response, next: NextFunction) => {
|
||||
if (err) {
|
||||
return res.status(err['status'] || 500).json({
|
||||
status: 'ERROR',
|
||||
error: err['error'] || 'Internal Server Error',
|
||||
response: {
|
||||
message: err['message'] || 'Internal Server Error',
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
||||
httpService.post('', errorData);
|
||||
}
|
||||
|
||||
next();
|
||||
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,
|
||||
error: 'Not Found',
|
||||
response: {
|
||||
message: [`Cannot ${method.toUpperCase()} ${url}`],
|
||||
},
|
||||
(req: Request, res: Response, next: NextFunction) => {
|
||||
const { method, url } = req;
|
||||
});
|
||||
|
||||
res.status(HttpStatus.NOT_FOUND).json({
|
||||
status: HttpStatus.NOT_FOUND,
|
||||
error: 'Not Found',
|
||||
response: {
|
||||
message: `Cannot ${method.toUpperCase()} ${url}`,
|
||||
},
|
||||
});
|
||||
next();
|
||||
},
|
||||
);
|
||||
|
||||
next();
|
||||
},
|
||||
);
|
||||
const httpServer = configService.get<HttpServer>('SERVER');
|
||||
|
||||
const httpServer = configService.get<HttpServer>('SERVER');
|
||||
ServerUP.app = app;
|
||||
const server = ServerUP[httpServer.TYPE];
|
||||
|
||||
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();
|
||||
|
||||
initWA();
|
||||
initIO(server);
|
||||
|
||||
onUnexpectedError();
|
||||
if (configService.get<Rabbitmq>('RABBITMQ')?.ENABLED) initAMQP();
|
||||
|
||||
if (configService.get<Sqs>('SQS')?.ENABLED) initSQS();
|
||||
|
||||
onUnexpectedError();
|
||||
}
|
||||
|
||||
bootstrap();
|
||||
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
|
||||
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,
|
||||
@@ -25,7 +25,14 @@ 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)), {
|
||||
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) {
|
||||
@@ -36,7 +43,10 @@ export async function useMultiFileAuthStateDb(
|
||||
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 (error) {
|
||||
@@ -91,7 +101,7 @@ export async function useMultiFileAuthStateDb(
|
||||
},
|
||||
},
|
||||
saveCreds: async () => {
|
||||
return writeData(creds, 'creds');
|
||||
return await writeData(creds, 'creds');
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
} from '@whiskeysockets/baileys';
|
||||
|
||||
import { Logger } from '../config/logger.config';
|
||||
import { RedisCache } from '../db/redis.client';
|
||||
import { RedisCache } from '../libs/redis.client';
|
||||
|
||||
export async function useMultiFileAuthStateRedisDb(cache: RedisCache): Promise<{
|
||||
state: AuthenticationState;
|
||||
|
||||
@@ -55,6 +55,9 @@ export const instanceNameSchema: JSONSchema7 = {
|
||||
'CONNECTION_UPDATE',
|
||||
'CALL',
|
||||
'NEW_JWT_TOKEN',
|
||||
'TYPEBOT_START',
|
||||
'TYPEBOT_CHANGE_STATUS',
|
||||
'CHAMA_AI_ACTION',
|
||||
],
|
||||
},
|
||||
},
|
||||
@@ -146,6 +149,16 @@ export const textMessageSchema: JSONSchema7 = {
|
||||
required: ['textMessage', 'number'],
|
||||
};
|
||||
|
||||
export const presenceSchema: JSONSchema7 = {
|
||||
$id: v4(),
|
||||
type: 'object',
|
||||
properties: {
|
||||
number: { ...numberDefinition },
|
||||
options: { ...optionsSchema, required: ['presence', 'delay'] },
|
||||
},
|
||||
required: ['options', 'number'],
|
||||
};
|
||||
|
||||
export const pollMessageSchema: JSONSchema7 = {
|
||||
$id: v4(),
|
||||
type: 'object',
|
||||
@@ -508,6 +521,7 @@ export const archiveChatSchema: JSONSchema7 = {
|
||||
$id: v4(),
|
||||
type: 'object',
|
||||
properties: {
|
||||
chat: { type: 'string' },
|
||||
lastMessage: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
@@ -528,7 +542,7 @@ export const archiveChatSchema: JSONSchema7 = {
|
||||
},
|
||||
archive: { type: 'boolean', enum: [true, false] },
|
||||
},
|
||||
required: ['lastMessage', 'archive'],
|
||||
required: ['archive'],
|
||||
};
|
||||
|
||||
export const deleteMessageSchema: JSONSchema7 = {
|
||||
@@ -823,12 +837,86 @@ export const updateGroupDescriptionSchema: JSONSchema7 = {
|
||||
...isNotEmpty('groupJid', 'description'),
|
||||
};
|
||||
|
||||
// Webhook Schema
|
||||
export const webhookSchema: JSONSchema7 = {
|
||||
$id: v4(),
|
||||
type: 'object',
|
||||
properties: {
|
||||
url: { type: 'string' },
|
||||
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',
|
||||
'CALL',
|
||||
'NEW_JWT_TOKEN',
|
||||
'TYPEBOT_START',
|
||||
'TYPEBOT_CHANGE_STATUS',
|
||||
'CHAMA_AI_ACTION',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
required: ['url'],
|
||||
...isNotEmpty('url'),
|
||||
};
|
||||
|
||||
export const chatwootSchema: JSONSchema7 = {
|
||||
$id: v4(),
|
||||
type: 'object',
|
||||
properties: {
|
||||
enabled: { type: 'boolean', enum: [true, false] },
|
||||
account_id: { type: 'string' },
|
||||
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] },
|
||||
},
|
||||
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] },
|
||||
},
|
||||
required: ['reject_call', 'groups_ignore', 'always_online', 'read_messages', 'read_status'],
|
||||
...isNotEmpty('reject_call', 'groups_ignore', 'always_online', 'read_messages', 'read_status'),
|
||||
};
|
||||
|
||||
export const websocketSchema: JSONSchema7 = {
|
||||
$id: v4(),
|
||||
type: 'object',
|
||||
properties: {
|
||||
enabled: { type: 'boolean', enum: [true, false] },
|
||||
events: {
|
||||
type: 'array',
|
||||
@@ -857,41 +945,163 @@ export const webhookSchema: JSONSchema7 = {
|
||||
'CONNECTION_UPDATE',
|
||||
'CALL',
|
||||
'NEW_JWT_TOKEN',
|
||||
'TYPEBOT_START',
|
||||
'TYPEBOT_CHANGE_STATUS',
|
||||
'CHAMA_AI_ACTION',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
required: ['url', 'enabled'],
|
||||
...isNotEmpty('url'),
|
||||
required: ['enabled'],
|
||||
...isNotEmpty('enabled'),
|
||||
};
|
||||
|
||||
export const chatwootSchema: JSONSchema7 = {
|
||||
export const rabbitmqSchema: JSONSchema7 = {
|
||||
$id: v4(),
|
||||
type: 'object',
|
||||
properties: {
|
||||
enabled: { type: 'boolean', enum: [true, false] },
|
||||
account_id: { type: 'string' },
|
||||
token: { type: 'string' },
|
||||
url: { type: 'string' },
|
||||
sign_msg: { type: 'boolean', enum: [true, false] },
|
||||
reopen_conversation: { type: 'boolean', enum: [true, false] },
|
||||
conversation_pending: { 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',
|
||||
'CALL',
|
||||
'NEW_JWT_TOKEN',
|
||||
'TYPEBOT_START',
|
||||
'TYPEBOT_CHANGE_STATUS',
|
||||
'CHAMA_AI_ACTION',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
required: ['enabled', 'account_id', 'token', 'url', 'sign_msg', 'reopen_conversation', 'conversation_pending'],
|
||||
...isNotEmpty('account_id', 'token', 'url', 'sign_msg', 'reopen_conversation', 'conversation_pending'),
|
||||
required: ['enabled'],
|
||||
...isNotEmpty('enabled'),
|
||||
};
|
||||
|
||||
export const settingsSchema: JSONSchema7 = {
|
||||
export const sqsSchema: 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] },
|
||||
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',
|
||||
'CALL',
|
||||
'NEW_JWT_TOKEN',
|
||||
'TYPEBOT_START',
|
||||
'TYPEBOT_CHANGE_STATUS',
|
||||
'CHAMA_AI_ACTION',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
required: ['reject_call', 'groups_ignore', 'always_online', 'read_messages', 'read_status'],
|
||||
...isNotEmpty('reject_call', 'groups_ignore', 'always_online', 'read_messages', 'read_status'),
|
||||
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: 'string' },
|
||||
},
|
||||
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'),
|
||||
};
|
||||
|
||||
@@ -48,20 +48,21 @@ 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;
|
||||
// return {
|
||||
// property: property.replace('instance.', ''),
|
||||
// message,
|
||||
// };
|
||||
});
|
||||
logger.error([...message]);
|
||||
throw new BadRequestException(...message);
|
||||
logger.error(message);
|
||||
throw new BadRequestException(message);
|
||||
}
|
||||
|
||||
return await execute(instance, ref);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
ProfilePictureDto,
|
||||
ProfileStatusDto,
|
||||
ReadMessageDto,
|
||||
SendPresenceDto,
|
||||
WhatsAppNumberDto,
|
||||
} from '../dto/chat.dto';
|
||||
import { InstanceDto } from '../dto/instance.dto';
|
||||
@@ -77,6 +78,11 @@ 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();
|
||||
|
||||
@@ -5,13 +5,18 @@ import { Logger } from '../../config/logger.config';
|
||||
import { BadRequestException } from '../../exceptions';
|
||||
import { ChatwootDto } from '../dto/chatwoot.dto';
|
||||
import { InstanceDto } from '../dto/instance.dto';
|
||||
import { RepositoryBroker } from '../repository/repository.manager';
|
||||
import { ChatwootService } from '../services/chatwoot.service';
|
||||
import { waMonitor } from '../whatsapp.module';
|
||||
|
||||
const logger = new Logger('ChatwootController');
|
||||
|
||||
export class ChatwootController {
|
||||
constructor(private readonly chatwootService: ChatwootService, private readonly configService: ConfigService) {}
|
||||
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');
|
||||
@@ -32,6 +37,7 @@ export class ChatwootController {
|
||||
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) {
|
||||
@@ -40,19 +46,21 @@ export class ChatwootController {
|
||||
data.token = '';
|
||||
data.url = '';
|
||||
data.sign_msg = false;
|
||||
data.sign_delimiter = null;
|
||||
data.reopen_conversation = false;
|
||||
data.conversation_pending = false;
|
||||
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;
|
||||
@@ -64,7 +72,7 @@ export class ChatwootController {
|
||||
|
||||
const urlServer = this.configService.get<HttpServer>('SERVER').URL;
|
||||
|
||||
if (Object.keys(result).length === 0) {
|
||||
if (Object.keys(result || {}).length === 0) {
|
||||
return {
|
||||
enabled: false,
|
||||
url: '',
|
||||
@@ -78,7 +86,7 @@ export class ChatwootController {
|
||||
|
||||
const response = {
|
||||
...result,
|
||||
webhook_url: `${urlServer}/chatwoot/webhook/${instance.instanceName}`,
|
||||
webhook_url: `${urlServer}/chatwoot/webhook/${encodeURIComponent(instance.instanceName)}`,
|
||||
};
|
||||
|
||||
return response;
|
||||
@@ -86,14 +94,8 @@ export class ChatwootController {
|
||||
|
||||
public async receiveWebhook(instance: InstanceDto, data: any) {
|
||||
logger.verbose('requested receiveWebhook from ' + instance.instanceName + ' instance');
|
||||
const chatwootService = new ChatwootService(waMonitor, this.configService);
|
||||
const chatwootService = new ChatwootService(waMonitor, this.configService, this.repository);
|
||||
|
||||
return chatwootService.receiveWebhook(instance, data);
|
||||
}
|
||||
|
||||
public async newInstance(data: any) {
|
||||
const chatwootService = new ChatwootService(waMonitor, this.configService);
|
||||
|
||||
return chatwootService.newInstance(data);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,20 +1,26 @@
|
||||
import { delay } from '@whiskeysockets/baileys';
|
||||
import { isURL } from 'class-validator';
|
||||
import EventEmitter2 from 'eventemitter2';
|
||||
import { v4 } from 'uuid';
|
||||
|
||||
import { ConfigService, HttpServer } from '../../config/env.config';
|
||||
import { Logger } from '../../config/logger.config';
|
||||
import { RedisCache } from '../../db/redis.client';
|
||||
import { BadRequestException, InternalServerErrorException } from '../../exceptions';
|
||||
import { RedisCache } from '../../libs/redis.client';
|
||||
import { InstanceDto } from '../dto/instance.dto';
|
||||
import { RepositoryBroker } from '../repository/repository.manager';
|
||||
import { AuthService, OldToken } from '../services/auth.service';
|
||||
import { ChatwootService } from '../services/chatwoot.service';
|
||||
import { WAMonitoringService } from '../services/monitor.service';
|
||||
import { ProxyService } from '../services/proxy.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 { WAStartupService } from '../services/whatsapp.service';
|
||||
import { wa } from '../types/wa.types';
|
||||
import { Events, wa } from '../types/wa.types';
|
||||
|
||||
export class InstanceController {
|
||||
constructor(
|
||||
@@ -26,6 +32,11 @@ export class InstanceController {
|
||||
private readonly webhookService: WebhookService,
|
||||
private readonly chatwootService: ChatwootService,
|
||||
private readonly settingsService: SettingsService,
|
||||
private readonly websocketService: WebsocketService,
|
||||
private readonly rabbitmqService: RabbitmqService,
|
||||
private readonly proxyService: ProxyService,
|
||||
private readonly sqsService: SqsService,
|
||||
private readonly typebotService: TypebotService,
|
||||
private readonly cache: RedisCache,
|
||||
) {}
|
||||
|
||||
@@ -35,6 +46,7 @@ export class InstanceController {
|
||||
instanceName,
|
||||
webhook,
|
||||
webhook_by_events,
|
||||
webhook_base64,
|
||||
events,
|
||||
qrcode,
|
||||
number,
|
||||
@@ -51,6 +63,20 @@ export class InstanceController {
|
||||
always_online,
|
||||
read_messages,
|
||||
read_status,
|
||||
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,
|
||||
proxy,
|
||||
}: InstanceDto) {
|
||||
try {
|
||||
this.logger.verbose('requested createInstance from ' + instanceName + ' instance');
|
||||
@@ -62,6 +88,13 @@ export class InstanceController {
|
||||
const instance = new WAStartupService(this.configService, this.eventEmitter, this.repository, this.cache);
|
||||
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;
|
||||
@@ -71,13 +104,14 @@ 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 })) {
|
||||
@@ -86,14 +120,226 @@ export class InstanceController {
|
||||
|
||||
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',
|
||||
'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',
|
||||
'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',
|
||||
'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);
|
||||
}
|
||||
}
|
||||
|
||||
if (proxy) {
|
||||
this.logger.verbose('creating proxy');
|
||||
try {
|
||||
this.proxyService.create(
|
||||
instance,
|
||||
{
|
||||
enabled: true,
|
||||
proxy,
|
||||
},
|
||||
false,
|
||||
);
|
||||
} 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',
|
||||
'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);
|
||||
}
|
||||
@@ -103,7 +349,7 @@ export class InstanceController {
|
||||
const settings: wa.LocalSettings = {
|
||||
reject_call: reject_call || false,
|
||||
msg_call: msg_call || '',
|
||||
groups_ignore: groups_ignore || false,
|
||||
groups_ignore: groups_ignore || true,
|
||||
always_online: always_online || false,
|
||||
read_messages: read_messages || false,
|
||||
read_status: read_status || false,
|
||||
@@ -126,14 +372,41 @@ export class InstanceController {
|
||||
const result = {
|
||||
instance: {
|
||||
instanceName: instance.instanceName,
|
||||
instanceId: instanceId,
|
||||
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,
|
||||
qrcode: getQrcode,
|
||||
proxy,
|
||||
};
|
||||
|
||||
this.logger.verbose('instance created');
|
||||
@@ -179,19 +452,12 @@ 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,
|
||||
auto_create: true,
|
||||
});
|
||||
|
||||
this.chatwootService.initInstanceChatwoot(
|
||||
instance,
|
||||
instance.instanceName,
|
||||
`${urlServer}/chatwoot/webhook/${instance.instanceName}`,
|
||||
qrcode,
|
||||
number,
|
||||
);
|
||||
} catch (error) {
|
||||
this.logger.log(error);
|
||||
}
|
||||
@@ -199,12 +465,38 @@ export class InstanceController {
|
||||
return {
|
||||
instance: {
|
||||
instanceName: instance.instanceName,
|
||||
instanceId: instanceId,
|
||||
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,
|
||||
@@ -216,12 +508,13 @@ export class InstanceController {
|
||||
conversation_pending: chatwoot_conversation_pending || false,
|
||||
number,
|
||||
name_inbox: instance.instanceName,
|
||||
webhook_url: `${urlServer}/chatwoot/webhook/${instance.instanceName}`,
|
||||
webhook_url: `${urlServer}/chatwoot/webhook/${encodeURIComponent(instance.instanceName)}`,
|
||||
},
|
||||
proxy,
|
||||
};
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
return { error: true, message: error.toString() };
|
||||
this.logger.error(error.message[0]);
|
||||
throw new BadRequestException(error.message[0]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -234,6 +527,10 @@ export class InstanceController {
|
||||
|
||||
this.logger.verbose('state: ' + state);
|
||||
|
||||
if (!state) {
|
||||
throw new BadRequestException('The "' + instanceName + '" instance does not exist');
|
||||
}
|
||||
|
||||
if (state == 'open') {
|
||||
return await this.connectionState({ instanceName });
|
||||
}
|
||||
@@ -266,10 +563,19 @@ export class InstanceController {
|
||||
try {
|
||||
this.logger.verbose('requested restartInstance from ' + instanceName + ' instance');
|
||||
|
||||
this.logger.verbose('logging out instance: ' + instanceName);
|
||||
this.waMonitor.waInstances[instanceName]?.client?.ws?.close();
|
||||
const instance = this.waMonitor.waInstances[instanceName];
|
||||
const state = instance?.connectionStatus?.state;
|
||||
|
||||
return { error: false, message: 'Instance restarted' };
|
||||
switch (state) {
|
||||
case 'open':
|
||||
this.logger.verbose('logging out instance: ' + instanceName);
|
||||
await instance.reloadConnection();
|
||||
await delay(2000);
|
||||
|
||||
return await this.connectionState({ instanceName });
|
||||
default:
|
||||
return await this.connectionState({ instanceName });
|
||||
}
|
||||
} catch (error) {
|
||||
this.logger.error(error);
|
||||
}
|
||||
@@ -285,13 +591,16 @@ export class InstanceController {
|
||||
};
|
||||
}
|
||||
|
||||
public async fetchInstances({ instanceName }: InstanceDto) {
|
||||
this.logger.verbose('requested fetchInstances from ' + instanceName + ' instance');
|
||||
public async fetchInstances({ instanceName, instanceId }: InstanceDto) {
|
||||
if (instanceName) {
|
||||
this.logger.verbose('requested fetchInstances from ' + instanceName + ' instance');
|
||||
this.logger.verbose('instanceName: ' + instanceName);
|
||||
return this.waMonitor.instanceInfo(instanceName);
|
||||
} else if (instanceId) {
|
||||
return this.waMonitor.instanceInfoById(instanceId);
|
||||
}
|
||||
|
||||
this.logger.verbose('requested fetchInstances (all instances)');
|
||||
return this.waMonitor.instanceInfo();
|
||||
}
|
||||
|
||||
@@ -310,7 +619,7 @@ export class InstanceController {
|
||||
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());
|
||||
}
|
||||
@@ -324,19 +633,23 @@ export class InstanceController {
|
||||
throw new BadRequestException('The "' + instanceName + '" instance needs to be disconnected');
|
||||
}
|
||||
try {
|
||||
this.waMonitor.waInstances[instanceName]?.removeRabbitmqQueues();
|
||||
|
||||
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);
|
||||
|
||||
this.waMonitor.waInstances[instanceName].sendDataWebhook(Events.INSTANCE_DELETE, {
|
||||
instanceName,
|
||||
instanceId: (await this.repository.auth.find(instanceName))?.instanceId,
|
||||
});
|
||||
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());
|
||||
}
|
||||
|
||||
26
src/whatsapp/controllers/proxy.controller.ts
Normal file
26
src/whatsapp/controllers/proxy.controller.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import { Logger } from '../../config/logger.config';
|
||||
import { InstanceDto } from '../dto/instance.dto';
|
||||
import { ProxyDto } from '../dto/proxy.dto';
|
||||
import { ProxyService } from '../services/proxy.service';
|
||||
|
||||
const logger = new Logger('ProxyController');
|
||||
|
||||
export class ProxyController {
|
||||
constructor(private readonly proxyService: ProxyService) {}
|
||||
|
||||
public async createProxy(instance: InstanceDto, data: ProxyDto) {
|
||||
logger.verbose('requested createProxy from ' + instance.instanceName + ' instance');
|
||||
|
||||
if (!data.enabled) {
|
||||
logger.verbose('proxy disabled');
|
||||
data.proxy = '';
|
||||
}
|
||||
|
||||
return this.proxyService.create(instance, data);
|
||||
}
|
||||
|
||||
public async findProxy(instance: InstanceDto) {
|
||||
logger.verbose('requested findProxy from ' + instance.instanceName + ' instance');
|
||||
return this.proxyService.find(instance);
|
||||
}
|
||||
}
|
||||
56
src/whatsapp/controllers/rabbitmq.controller.ts
Normal file
56
src/whatsapp/controllers/rabbitmq.controller.ts
Normal file
@@ -0,0 +1,56 @@
|
||||
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',
|
||||
'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);
|
||||
}
|
||||
}
|
||||
@@ -19,6 +19,7 @@ export class SettingsController {
|
||||
|
||||
public async findSettings(instance: InstanceDto) {
|
||||
logger.verbose('requested findSettings from ' + instance.instanceName + ' instance');
|
||||
return this.settingsService.find(instance);
|
||||
const settings = this.settingsService.find(instance);
|
||||
return settings;
|
||||
}
|
||||
}
|
||||
|
||||
56
src/whatsapp/controllers/sqs.controller.ts
Normal file
56
src/whatsapp/controllers/sqs.controller.ts
Normal file
@@ -0,0 +1,56 @@
|
||||
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',
|
||||
'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,26 +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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14,14 +14,44 @@ export class WebhookController {
|
||||
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',
|
||||
'CALL',
|
||||
'NEW_JWT_TOKEN',
|
||||
'TYPEBOT_START',
|
||||
'TYPEBOT_CHANGE_STATUS',
|
||||
'CHAMA_AI_ACTION',
|
||||
];
|
||||
}
|
||||
|
||||
return this.webhookService.create(instance, data);
|
||||
|
||||
56
src/whatsapp/controllers/websocket.controller.ts
Normal file
56
src/whatsapp/controllers/websocket.controller.ts
Normal file
@@ -0,0 +1,56 @@
|
||||
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',
|
||||
'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,4 +1,4 @@
|
||||
import { proto, WAPrivacyOnlineValue, WAPrivacyValue, WAReadReceiptsValue } 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 name?: string) {}
|
||||
@@ -53,13 +53,14 @@ export class ReadMessageDto {
|
||||
read_messages: Key[];
|
||||
}
|
||||
|
||||
class LastMessage {
|
||||
export class LastMessage {
|
||||
key: Key;
|
||||
messageTimestamp?: number;
|
||||
}
|
||||
|
||||
export class ArchiveChatDto {
|
||||
lastMessage: LastMessage;
|
||||
lastMessage?: LastMessage;
|
||||
chat?: string;
|
||||
archive: boolean;
|
||||
}
|
||||
|
||||
@@ -82,3 +83,20 @@ 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;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -5,7 +5,9 @@ export class ChatwootDto {
|
||||
url?: string;
|
||||
name_inbox?: string;
|
||||
sign_msg?: boolean;
|
||||
sign_delimiter?: string;
|
||||
number?: string;
|
||||
reopen_conversation?: boolean;
|
||||
conversation_pending?: boolean;
|
||||
auto_create?: boolean;
|
||||
}
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
export class InstanceDto {
|
||||
instanceName: string;
|
||||
instanceId?: string;
|
||||
qrcode?: boolean;
|
||||
number?: string;
|
||||
token?: string;
|
||||
webhook?: string;
|
||||
webhook_by_events?: boolean;
|
||||
webhook_base64?: boolean;
|
||||
events?: string[];
|
||||
reject_call?: boolean;
|
||||
msg_call?: string;
|
||||
@@ -18,4 +20,18 @@ export class InstanceDto {
|
||||
chatwoot_sign_msg?: boolean;
|
||||
chatwoot_reopen_conversation?: boolean;
|
||||
chatwoot_conversation_pending?: boolean;
|
||||
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;
|
||||
}
|
||||
|
||||
4
src/whatsapp/dto/proxy.dto.ts
Normal file
4
src/whatsapp/dto/proxy.dto.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export class ProxyDto {
|
||||
enabled: boolean;
|
||||
proxy: string;
|
||||
}
|
||||
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[];
|
||||
}
|
||||
@@ -46,9 +46,13 @@ class PollMessage {
|
||||
values: string[];
|
||||
messageSecret?: Uint8Array;
|
||||
}
|
||||
|
||||
export class SendTextDto extends Metadata {
|
||||
textMessage: TextMessage;
|
||||
}
|
||||
export class SendPresence extends Metadata {
|
||||
textMessage: TextMessage;
|
||||
}
|
||||
|
||||
export class SendStatusDto extends Metadata {
|
||||
statusMessage: StatusMessage;
|
||||
@@ -61,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;
|
||||
|
||||
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[];
|
||||
}
|
||||
26
src/whatsapp/dto/typebot.dto.ts
Normal file
26
src/whatsapp/dto/typebot.dto.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
export class Session {
|
||||
remoteJid?: string;
|
||||
sessionId?: string;
|
||||
status?: string;
|
||||
createdAt?: number;
|
||||
updateAt?: number;
|
||||
prefilledVariables?: PrefilledVariables;
|
||||
}
|
||||
|
||||
export class PrefilledVariables {
|
||||
remoteJid?: string;
|
||||
pushName?: 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[];
|
||||
}
|
||||
@@ -55,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');
|
||||
|
||||
|
||||
@@ -4,31 +4,40 @@ 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, NotFoundException } from '../../exceptions';
|
||||
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';
|
||||
|
||||
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) {
|
||||
@@ -56,6 +65,7 @@ export async function instanceLoggedGuard(req: Request, _: Response, next: NextF
|
||||
}
|
||||
|
||||
if (waMonitor.waInstances[instance.instanceName]) {
|
||||
waMonitor.waInstances[instance.instanceName]?.removeRabbitmqQueues();
|
||||
delete waMonitor.waInstances[instance.instanceName];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,17 +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,6 +1,6 @@
|
||||
import { Schema } from 'mongoose';
|
||||
|
||||
import { dbserver } from '../../db/db.connect';
|
||||
import { dbserver } from '../../libs/db.connect';
|
||||
|
||||
export class ChatRaw {
|
||||
_id?: string;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Schema } from 'mongoose';
|
||||
|
||||
import { dbserver } from '../../db/db.connect';
|
||||
import { dbserver } from '../../libs/db.connect';
|
||||
|
||||
export class ChatwootRaw {
|
||||
_id?: string;
|
||||
@@ -10,6 +10,7 @@ export class ChatwootRaw {
|
||||
url?: string;
|
||||
name_inbox?: string;
|
||||
sign_msg?: boolean;
|
||||
sign_delimiter?: string;
|
||||
number?: string;
|
||||
reopen_conversation?: boolean;
|
||||
conversation_pending?: boolean;
|
||||
@@ -23,7 +24,10 @@ 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 },
|
||||
});
|
||||
|
||||
export const ChatwootModel = dbserver?.model(ChatwootRaw.name, chatwootSchema, 'chatwoot');
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Schema } from 'mongoose';
|
||||
|
||||
import { dbserver } from '../../db/db.connect';
|
||||
import { dbserver } from '../../libs/db.connect';
|
||||
|
||||
export class ContactRaw {
|
||||
_id?: string;
|
||||
|
||||
@@ -1,7 +1,13 @@
|
||||
export * from './auth.model';
|
||||
export * from './chamaai.model';
|
||||
export * from './chat.model';
|
||||
export * from './chatwoot.model';
|
||||
export * from './contact.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';
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Schema } from 'mongoose';
|
||||
|
||||
import { dbserver } from '../../db/db.connect';
|
||||
import { dbserver } from '../../libs/db.connect';
|
||||
import { wa } from '../types/wa.types';
|
||||
|
||||
class Key {
|
||||
@@ -10,6 +10,12 @@ class Key {
|
||||
participant?: string;
|
||||
}
|
||||
|
||||
class ChatwootMessage {
|
||||
messageId?: number;
|
||||
inboxId?: number;
|
||||
conversationId?: number;
|
||||
}
|
||||
|
||||
export class MessageRaw {
|
||||
_id?: string;
|
||||
key?: Key;
|
||||
@@ -20,6 +26,9 @@ export class MessageRaw {
|
||||
messageTimestamp?: number | Long.Long;
|
||||
owner: string;
|
||||
source?: 'android' | 'web' | 'ios';
|
||||
source_id?: string;
|
||||
source_reply_id?: string;
|
||||
chatwoot?: ChatwootMessage;
|
||||
}
|
||||
|
||||
const messageSchema = new Schema<MessageRaw>({
|
||||
@@ -37,8 +46,18 @@ const messageSchema = new Schema<MessageRaw>({
|
||||
source: { type: String, minlength: 3, enum: ['android', 'web', 'ios'] },
|
||||
messageTimestamp: { type: Number, required: true },
|
||||
owner: { type: String, required: true, minlength: 1 },
|
||||
chatwoot: {
|
||||
messageId: { type: Number },
|
||||
inboxId: { type: Number },
|
||||
conversationId: { type: Number },
|
||||
},
|
||||
});
|
||||
|
||||
messageSchema.index({ 'chatwoot.messageId': 1, owner: 1 });
|
||||
messageSchema.index({ 'key.id': 1 });
|
||||
messageSchema.index({ 'key.id': 1, owner: 1 });
|
||||
messageSchema.index({ owner: 1 });
|
||||
|
||||
export const MessageModel = dbserver?.model(MessageRaw.name, messageSchema, 'messages');
|
||||
export type IMessageModel = typeof MessageModel;
|
||||
|
||||
|
||||
18
src/whatsapp/models/proxy.model.ts
Normal file
18
src/whatsapp/models/proxy.model.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { Schema } from 'mongoose';
|
||||
|
||||
import { dbserver } from '../../libs/db.connect';
|
||||
|
||||
export class ProxyRaw {
|
||||
_id?: string;
|
||||
enabled?: boolean;
|
||||
proxy?: string;
|
||||
}
|
||||
|
||||
const proxySchema = new Schema<ProxyRaw>({
|
||||
_id: { type: String, _id: true },
|
||||
enabled: { type: Boolean, required: true },
|
||||
proxy: { type: String, required: true },
|
||||
});
|
||||
|
||||
export const ProxyModel = dbserver?.model(ProxyRaw.name, proxySchema, 'proxy');
|
||||
export type IProxyModel = typeof ProxyModel;
|
||||
18
src/whatsapp/models/rabbitmq.model.ts
Normal file
18
src/whatsapp/models/rabbitmq.model.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { Schema } from 'mongoose';
|
||||
|
||||
import { dbserver } from '../../libs/db.connect';
|
||||
|
||||
export class RabbitmqRaw {
|
||||
_id?: string;
|
||||
enabled?: boolean;
|
||||
events?: string[];
|
||||
}
|
||||
|
||||
const rabbitmqSchema = new Schema<RabbitmqRaw>({
|
||||
_id: { type: String, _id: true },
|
||||
enabled: { type: Boolean, required: true },
|
||||
events: { type: [String], required: true },
|
||||
});
|
||||
|
||||
export const RabbitmqModel = dbserver?.model(RabbitmqRaw.name, rabbitmqSchema, 'rabbitmq');
|
||||
export type IRabbitmqModel = typeof RabbitmqModel;
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Schema } from 'mongoose';
|
||||
|
||||
import { dbserver } from '../../db/db.connect';
|
||||
import { dbserver } from '../../libs/db.connect';
|
||||
|
||||
export class SettingsRaw {
|
||||
_id?: string;
|
||||
|
||||
18
src/whatsapp/models/sqs.model.ts
Normal file
18
src/whatsapp/models/sqs.model.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { Schema } from 'mongoose';
|
||||
|
||||
import { dbserver } from '../../libs/db.connect';
|
||||
|
||||
export class SqsRaw {
|
||||
_id?: string;
|
||||
enabled?: boolean;
|
||||
events?: string[];
|
||||
}
|
||||
|
||||
const sqsSchema = new Schema<SqsRaw>({
|
||||
_id: { type: String, _id: true },
|
||||
enabled: { type: Boolean, required: true },
|
||||
events: { type: [String], required: true },
|
||||
});
|
||||
|
||||
export const SqsModel = dbserver?.model(SqsRaw.name, sqsSchema, 'sqs');
|
||||
export type ISqsModel = typeof SqsModel;
|
||||
58
src/whatsapp/models/typebot.model.ts
Normal file
58
src/whatsapp/models/typebot.model.ts
Normal file
@@ -0,0 +1,58 @@
|
||||
import { Schema } from 'mongoose';
|
||||
|
||||
import { dbserver } from '../../libs/db.connect';
|
||||
|
||||
class Session {
|
||||
remoteJid?: string;
|
||||
sessionId?: string;
|
||||
status?: string;
|
||||
createdAt?: number;
|
||||
updateAt?: number;
|
||||
prefilledVariables?: {
|
||||
remoteJid?: string;
|
||||
pushName?: string;
|
||||
additionalData?: { [key: string]: any };
|
||||
};
|
||||
}
|
||||
|
||||
export class TypebotRaw {
|
||||
_id?: string;
|
||||
enabled?: boolean;
|
||||
url: string;
|
||||
typebot?: string;
|
||||
expire?: number;
|
||||
keyword_finish?: string;
|
||||
delay_message?: number;
|
||||
unknown_message?: string;
|
||||
listening_from_me?: boolean;
|
||||
sessions?: Session[];
|
||||
}
|
||||
|
||||
const typebotSchema = new Schema<TypebotRaw>({
|
||||
_id: { type: String, _id: true },
|
||||
enabled: { type: Boolean, required: true },
|
||||
url: { type: String, required: true },
|
||||
typebot: { type: String, required: true },
|
||||
expire: { type: Number, required: true },
|
||||
keyword_finish: { type: String, required: true },
|
||||
delay_message: { type: Number, required: true },
|
||||
unknown_message: { type: String, required: true },
|
||||
listening_from_me: { type: Boolean, required: true },
|
||||
sessions: [
|
||||
{
|
||||
remoteJid: { type: String, required: true },
|
||||
sessionId: { type: String, required: true },
|
||||
status: { type: String, required: true },
|
||||
createdAt: { type: Number, required: true },
|
||||
updateAt: { type: Number, required: true },
|
||||
prefilledVariables: {
|
||||
remoteJid: { type: String, required: false },
|
||||
pushName: { type: String, required: false },
|
||||
additionalData: { type: Schema.Types.Mixed, required: false },
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
export const TypebotModel = dbserver?.model(TypebotRaw.name, typebotSchema, 'typebot');
|
||||
export type ITypebotModel = typeof TypebotModel;
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Schema } from 'mongoose';
|
||||
|
||||
import { dbserver } from '../../db/db.connect';
|
||||
import { dbserver } from '../../libs/db.connect';
|
||||
|
||||
export class WebhookRaw {
|
||||
_id?: string;
|
||||
@@ -8,6 +8,7 @@ export class WebhookRaw {
|
||||
enabled?: boolean;
|
||||
events?: string[];
|
||||
webhook_by_events?: boolean;
|
||||
webhook_base64?: boolean;
|
||||
}
|
||||
|
||||
const webhookSchema = new Schema<WebhookRaw>({
|
||||
@@ -16,6 +17,7 @@ const webhookSchema = new Schema<WebhookRaw>({
|
||||
enabled: { type: Boolean, required: true },
|
||||
events: { type: [String], required: true },
|
||||
webhook_by_events: { type: Boolean, required: true },
|
||||
webhook_base64: { type: Boolean, required: true },
|
||||
});
|
||||
|
||||
export const WebhookModel = dbserver?.model(WebhookRaw.name, webhookSchema, 'webhook');
|
||||
|
||||
18
src/whatsapp/models/websocket.model.ts
Normal file
18
src/whatsapp/models/websocket.model.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { Schema } from 'mongoose';
|
||||
|
||||
import { dbserver } from '../../libs/db.connect';
|
||||
|
||||
export class WebsocketRaw {
|
||||
_id?: string;
|
||||
enabled?: boolean;
|
||||
events?: string[];
|
||||
}
|
||||
|
||||
const websocketSchema = new Schema<WebsocketRaw>({
|
||||
_id: { type: String, _id: true },
|
||||
enabled: { type: Boolean, required: true },
|
||||
events: { type: [String], required: true },
|
||||
});
|
||||
|
||||
export const WebsocketModel = dbserver?.model(WebsocketRaw.name, websocketSchema, 'websocket');
|
||||
export type IWebsocketModel = typeof WebsocketModel;
|
||||
@@ -19,6 +19,7 @@ export class AuthRepository extends Repository {
|
||||
public async create(data: AuthRaw, instance: string): Promise<IInsert> {
|
||||
try {
|
||||
this.logger.verbose('creating auth');
|
||||
|
||||
if (this.dbSettings.ENABLED) {
|
||||
this.logger.verbose('saving auth to db');
|
||||
const insert = await this.authModel.replaceOne({ _id: instance }, { ...data }, { upsert: true });
|
||||
@@ -62,4 +63,20 @@ export class AuthRepository extends Repository {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
public async findInstanceNameById(instanceId: string): Promise<string | null> {
|
||||
try {
|
||||
this.logger.verbose('finding auth by instanceId');
|
||||
if (this.dbSettings.ENABLED) {
|
||||
this.logger.verbose('finding auth in db');
|
||||
const response = await this.authModel.findOne({ instanceId });
|
||||
|
||||
return response._id;
|
||||
}
|
||||
|
||||
this.logger.verbose('finding auth in store is not supported');
|
||||
} catch (error) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
62
src/whatsapp/repository/chamaai.repository.ts
Normal file
62
src/whatsapp/repository/chamaai.repository.ts
Normal file
@@ -0,0 +1,62 @@
|
||||
import { readFileSync } from 'fs';
|
||||
import { join } from 'path';
|
||||
|
||||
import { ConfigService } from '../../config/env.config';
|
||||
import { Logger } from '../../config/logger.config';
|
||||
import { IInsert, Repository } from '../abstract/abstract.repository';
|
||||
import { ChamaaiRaw, IChamaaiModel } from '../models';
|
||||
|
||||
export class ChamaaiRepository extends Repository {
|
||||
constructor(private readonly chamaaiModel: IChamaaiModel, private readonly configService: ConfigService) {
|
||||
super(configService);
|
||||
}
|
||||
|
||||
private readonly logger = new Logger('ChamaaiRepository');
|
||||
|
||||
public async create(data: ChamaaiRaw, instance: string): Promise<IInsert> {
|
||||
try {
|
||||
this.logger.verbose('creating chamaai');
|
||||
if (this.dbSettings.ENABLED) {
|
||||
this.logger.verbose('saving chamaai to db');
|
||||
const insert = await this.chamaaiModel.replaceOne({ _id: instance }, { ...data }, { upsert: true });
|
||||
|
||||
this.logger.verbose('chamaai saved to db: ' + insert.modifiedCount + ' chamaai');
|
||||
return { insertCount: insert.modifiedCount };
|
||||
}
|
||||
|
||||
this.logger.verbose('saving chamaai to store');
|
||||
|
||||
this.writeStore<ChamaaiRaw>({
|
||||
path: join(this.storePath, 'chamaai'),
|
||||
fileName: instance,
|
||||
data,
|
||||
});
|
||||
|
||||
this.logger.verbose('chamaai saved to store in path: ' + join(this.storePath, 'chamaai') + '/' + instance);
|
||||
|
||||
this.logger.verbose('chamaai created');
|
||||
return { insertCount: 1 };
|
||||
} catch (error) {
|
||||
return error;
|
||||
}
|
||||
}
|
||||
|
||||
public async find(instance: string): Promise<ChamaaiRaw> {
|
||||
try {
|
||||
this.logger.verbose('finding chamaai');
|
||||
if (this.dbSettings.ENABLED) {
|
||||
this.logger.verbose('finding chamaai in db');
|
||||
return await this.chamaaiModel.findOne({ _id: instance });
|
||||
}
|
||||
|
||||
this.logger.verbose('finding chamaai in store');
|
||||
return JSON.parse(
|
||||
readFileSync(join(this.storePath, 'chamaai', instance + '.json'), {
|
||||
encoding: 'utf-8',
|
||||
}),
|
||||
) as ChamaaiRaw;
|
||||
} catch (error) {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -91,11 +91,13 @@ export class MessageRepository extends Repository {
|
||||
this.logger.verbose('finding messages');
|
||||
if (this.dbSettings.ENABLED) {
|
||||
this.logger.verbose('finding messages in db');
|
||||
if (query?.where?.key) {
|
||||
for (const [k, v] of Object.entries(query.where.key)) {
|
||||
query.where['key.' + k] = v;
|
||||
for (const [o, p] of Object.entries(query?.where)) {
|
||||
if (typeof p === 'object' && p !== null && !Array.isArray(p)) {
|
||||
for (const [k, v] of Object.entries(p)) {
|
||||
query.where[`${o}.${k}`] = v;
|
||||
}
|
||||
delete query.where[o];
|
||||
}
|
||||
delete query?.where?.key;
|
||||
}
|
||||
|
||||
return await this.messageModel
|
||||
@@ -144,4 +146,55 @@ export class MessageRepository extends Repository {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
public async update(data: MessageRaw[], instanceName: string, saveDb?: boolean): Promise<IInsert> {
|
||||
try {
|
||||
if (this.dbSettings.ENABLED && saveDb) {
|
||||
this.logger.verbose('updating messages in db');
|
||||
|
||||
const messages = data.map((message) => {
|
||||
return {
|
||||
updateOne: {
|
||||
filter: { 'key.id': message.key.id },
|
||||
update: { ...message },
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
const { nModified } = await this.messageModel.bulkWrite(messages);
|
||||
|
||||
this.logger.verbose('messages updated in db: ' + nModified + ' messages');
|
||||
return { insertCount: nModified };
|
||||
}
|
||||
|
||||
this.logger.verbose('updating messages in store');
|
||||
|
||||
const store = this.configService.get<StoreConf>('STORE');
|
||||
|
||||
if (store.MESSAGES) {
|
||||
this.logger.verbose('updating messages in store');
|
||||
data.forEach((message) => {
|
||||
this.writeStore({
|
||||
path: join(this.storePath, 'messages', instanceName),
|
||||
fileName: message.key.id,
|
||||
data: message,
|
||||
});
|
||||
this.logger.verbose(
|
||||
'messages updated in store in path: ' +
|
||||
join(this.storePath, 'messages', instanceName) +
|
||||
'/' +
|
||||
message.key.id,
|
||||
);
|
||||
});
|
||||
|
||||
this.logger.verbose('messages updated in store: ' + data.length + ' messages');
|
||||
return { insertCount: data.length };
|
||||
}
|
||||
|
||||
this.logger.verbose('messages not updated');
|
||||
return { insertCount: 0 };
|
||||
} catch (error) {
|
||||
this.logger.error(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
62
src/whatsapp/repository/proxy.repository.ts
Normal file
62
src/whatsapp/repository/proxy.repository.ts
Normal file
@@ -0,0 +1,62 @@
|
||||
import { readFileSync } from 'fs';
|
||||
import { join } from 'path';
|
||||
|
||||
import { ConfigService } from '../../config/env.config';
|
||||
import { Logger } from '../../config/logger.config';
|
||||
import { IInsert, Repository } from '../abstract/abstract.repository';
|
||||
import { IProxyModel, ProxyRaw } from '../models';
|
||||
|
||||
export class ProxyRepository extends Repository {
|
||||
constructor(private readonly proxyModel: IProxyModel, private readonly configService: ConfigService) {
|
||||
super(configService);
|
||||
}
|
||||
|
||||
private readonly logger = new Logger('ProxyRepository');
|
||||
|
||||
public async create(data: ProxyRaw, instance: string): Promise<IInsert> {
|
||||
try {
|
||||
this.logger.verbose('creating proxy');
|
||||
if (this.dbSettings.ENABLED) {
|
||||
this.logger.verbose('saving proxy to db');
|
||||
const insert = await this.proxyModel.replaceOne({ _id: instance }, { ...data }, { upsert: true });
|
||||
|
||||
this.logger.verbose('proxy saved to db: ' + insert.modifiedCount + ' proxy');
|
||||
return { insertCount: insert.modifiedCount };
|
||||
}
|
||||
|
||||
this.logger.verbose('saving proxy to store');
|
||||
|
||||
this.writeStore<ProxyRaw>({
|
||||
path: join(this.storePath, 'proxy'),
|
||||
fileName: instance,
|
||||
data,
|
||||
});
|
||||
|
||||
this.logger.verbose('proxy saved to store in path: ' + join(this.storePath, 'proxy') + '/' + instance);
|
||||
|
||||
this.logger.verbose('proxy created');
|
||||
return { insertCount: 1 };
|
||||
} catch (error) {
|
||||
return error;
|
||||
}
|
||||
}
|
||||
|
||||
public async find(instance: string): Promise<ProxyRaw> {
|
||||
try {
|
||||
this.logger.verbose('finding proxy');
|
||||
if (this.dbSettings.ENABLED) {
|
||||
this.logger.verbose('finding proxy in db');
|
||||
return await this.proxyModel.findOne({ _id: instance });
|
||||
}
|
||||
|
||||
this.logger.verbose('finding proxy in store');
|
||||
return JSON.parse(
|
||||
readFileSync(join(this.storePath, 'proxy', instance + '.json'), {
|
||||
encoding: 'utf-8',
|
||||
}),
|
||||
) as ProxyRaw;
|
||||
} catch (error) {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
}
|
||||
62
src/whatsapp/repository/rabbitmq.repository.ts
Normal file
62
src/whatsapp/repository/rabbitmq.repository.ts
Normal file
@@ -0,0 +1,62 @@
|
||||
import { readFileSync } from 'fs';
|
||||
import { join } from 'path';
|
||||
|
||||
import { ConfigService } from '../../config/env.config';
|
||||
import { Logger } from '../../config/logger.config';
|
||||
import { IInsert, Repository } from '../abstract/abstract.repository';
|
||||
import { IRabbitmqModel, RabbitmqRaw } from '../models';
|
||||
|
||||
export class RabbitmqRepository extends Repository {
|
||||
constructor(private readonly rabbitmqModel: IRabbitmqModel, private readonly configService: ConfigService) {
|
||||
super(configService);
|
||||
}
|
||||
|
||||
private readonly logger = new Logger('RabbitmqRepository');
|
||||
|
||||
public async create(data: RabbitmqRaw, instance: string): Promise<IInsert> {
|
||||
try {
|
||||
this.logger.verbose('creating rabbitmq');
|
||||
if (this.dbSettings.ENABLED) {
|
||||
this.logger.verbose('saving rabbitmq to db');
|
||||
const insert = await this.rabbitmqModel.replaceOne({ _id: instance }, { ...data }, { upsert: true });
|
||||
|
||||
this.logger.verbose('rabbitmq saved to db: ' + insert.modifiedCount + ' rabbitmq');
|
||||
return { insertCount: insert.modifiedCount };
|
||||
}
|
||||
|
||||
this.logger.verbose('saving rabbitmq to store');
|
||||
|
||||
this.writeStore<RabbitmqRaw>({
|
||||
path: join(this.storePath, 'rabbitmq'),
|
||||
fileName: instance,
|
||||
data,
|
||||
});
|
||||
|
||||
this.logger.verbose('rabbitmq saved to store in path: ' + join(this.storePath, 'rabbitmq') + '/' + instance);
|
||||
|
||||
this.logger.verbose('rabbitmq created');
|
||||
return { insertCount: 1 };
|
||||
} catch (error) {
|
||||
return error;
|
||||
}
|
||||
}
|
||||
|
||||
public async find(instance: string): Promise<RabbitmqRaw> {
|
||||
try {
|
||||
this.logger.verbose('finding rabbitmq');
|
||||
if (this.dbSettings.ENABLED) {
|
||||
this.logger.verbose('finding rabbitmq in db');
|
||||
return await this.rabbitmqModel.findOne({ _id: instance });
|
||||
}
|
||||
|
||||
this.logger.verbose('finding rabbitmq in store');
|
||||
return JSON.parse(
|
||||
readFileSync(join(this.storePath, 'rabbitmq', instance + '.json'), {
|
||||
encoding: 'utf-8',
|
||||
}),
|
||||
) as RabbitmqRaw;
|
||||
} catch (error) {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,13 +5,19 @@ import { join } from 'path';
|
||||
import { Auth, ConfigService, Database } from '../../config/env.config';
|
||||
import { Logger } from '../../config/logger.config';
|
||||
import { AuthRepository } from './auth.repository';
|
||||
import { ChamaaiRepository } from './chamaai.repository';
|
||||
import { ChatRepository } from './chat.repository';
|
||||
import { ChatwootRepository } from './chatwoot.repository';
|
||||
import { ContactRepository } from './contact.repository';
|
||||
import { MessageRepository } from './message.repository';
|
||||
import { MessageUpRepository } from './messageUp.repository';
|
||||
import { ProxyRepository } from './proxy.repository';
|
||||
import { RabbitmqRepository } from './rabbitmq.repository';
|
||||
import { SettingsRepository } from './settings.repository';
|
||||
import { SqsRepository } from './sqs.repository';
|
||||
import { TypebotRepository } from './typebot.repository';
|
||||
import { WebhookRepository } from './webhook.repository';
|
||||
import { WebsocketRepository } from './websocket.repository';
|
||||
export class RepositoryBroker {
|
||||
constructor(
|
||||
public readonly message: MessageRepository,
|
||||
@@ -21,6 +27,12 @@ export class RepositoryBroker {
|
||||
public readonly webhook: WebhookRepository,
|
||||
public readonly chatwoot: ChatwootRepository,
|
||||
public readonly settings: SettingsRepository,
|
||||
public readonly websocket: WebsocketRepository,
|
||||
public readonly rabbitmq: RabbitmqRepository,
|
||||
public readonly sqs: SqsRepository,
|
||||
public readonly typebot: TypebotRepository,
|
||||
public readonly proxy: ProxyRepository,
|
||||
public readonly chamaai: ChamaaiRepository,
|
||||
public readonly auth: AuthRepository,
|
||||
private configService: ConfigService,
|
||||
dbServer?: MongoClient,
|
||||
@@ -51,6 +63,12 @@ export class RepositoryBroker {
|
||||
const webhookDir = join(storePath, 'webhook');
|
||||
const chatwootDir = join(storePath, 'chatwoot');
|
||||
const settingsDir = join(storePath, 'settings');
|
||||
const websocketDir = join(storePath, 'websocket');
|
||||
const rabbitmqDir = join(storePath, 'rabbitmq');
|
||||
const sqsDir = join(storePath, 'sqs');
|
||||
const typebotDir = join(storePath, 'typebot');
|
||||
const proxyDir = join(storePath, 'proxy');
|
||||
const chamaaiDir = join(storePath, 'chamaai');
|
||||
const tempDir = join(storePath, 'temp');
|
||||
|
||||
if (!fs.existsSync(authDir)) {
|
||||
@@ -85,6 +103,30 @@ export class RepositoryBroker {
|
||||
this.logger.verbose('creating settings dir: ' + settingsDir);
|
||||
fs.mkdirSync(settingsDir, { recursive: true });
|
||||
}
|
||||
if (!fs.existsSync(websocketDir)) {
|
||||
this.logger.verbose('creating websocket dir: ' + websocketDir);
|
||||
fs.mkdirSync(websocketDir, { recursive: true });
|
||||
}
|
||||
if (!fs.existsSync(rabbitmqDir)) {
|
||||
this.logger.verbose('creating rabbitmq dir: ' + rabbitmqDir);
|
||||
fs.mkdirSync(rabbitmqDir, { recursive: true });
|
||||
}
|
||||
if (!fs.existsSync(sqsDir)) {
|
||||
this.logger.verbose('creating sqs dir: ' + sqsDir);
|
||||
fs.mkdirSync(sqsDir, { recursive: true });
|
||||
}
|
||||
if (!fs.existsSync(typebotDir)) {
|
||||
this.logger.verbose('creating typebot dir: ' + typebotDir);
|
||||
fs.mkdirSync(typebotDir, { recursive: true });
|
||||
}
|
||||
if (!fs.existsSync(proxyDir)) {
|
||||
this.logger.verbose('creating proxy dir: ' + proxyDir);
|
||||
fs.mkdirSync(proxyDir, { recursive: true });
|
||||
}
|
||||
if (!fs.existsSync(chamaaiDir)) {
|
||||
this.logger.verbose('creating chamaai dir: ' + chamaaiDir);
|
||||
fs.mkdirSync(chamaaiDir, { recursive: true });
|
||||
}
|
||||
if (!fs.existsSync(tempDir)) {
|
||||
this.logger.verbose('creating temp dir: ' + tempDir);
|
||||
fs.mkdirSync(tempDir, { recursive: true });
|
||||
|
||||
62
src/whatsapp/repository/sqs.repository.ts
Normal file
62
src/whatsapp/repository/sqs.repository.ts
Normal file
@@ -0,0 +1,62 @@
|
||||
import { readFileSync } from 'fs';
|
||||
import { join } from 'path';
|
||||
|
||||
import { ConfigService } from '../../config/env.config';
|
||||
import { Logger } from '../../config/logger.config';
|
||||
import { IInsert, Repository } from '../abstract/abstract.repository';
|
||||
import { ISqsModel, SqsRaw } from '../models';
|
||||
|
||||
export class SqsRepository extends Repository {
|
||||
constructor(private readonly sqsModel: ISqsModel, private readonly configService: ConfigService) {
|
||||
super(configService);
|
||||
}
|
||||
|
||||
private readonly logger = new Logger('SqsRepository');
|
||||
|
||||
public async create(data: SqsRaw, instance: string): Promise<IInsert> {
|
||||
try {
|
||||
this.logger.verbose('creating sqs');
|
||||
if (this.dbSettings.ENABLED) {
|
||||
this.logger.verbose('saving sqs to db');
|
||||
const insert = await this.sqsModel.replaceOne({ _id: instance }, { ...data }, { upsert: true });
|
||||
|
||||
this.logger.verbose('sqs saved to db: ' + insert.modifiedCount + ' sqs');
|
||||
return { insertCount: insert.modifiedCount };
|
||||
}
|
||||
|
||||
this.logger.verbose('saving sqs to store');
|
||||
|
||||
this.writeStore<SqsRaw>({
|
||||
path: join(this.storePath, 'sqs'),
|
||||
fileName: instance,
|
||||
data,
|
||||
});
|
||||
|
||||
this.logger.verbose('sqs saved to store in path: ' + join(this.storePath, 'sqs') + '/' + instance);
|
||||
|
||||
this.logger.verbose('sqs created');
|
||||
return { insertCount: 1 };
|
||||
} catch (error) {
|
||||
return error;
|
||||
}
|
||||
}
|
||||
|
||||
public async find(instance: string): Promise<SqsRaw> {
|
||||
try {
|
||||
this.logger.verbose('finding sqs');
|
||||
if (this.dbSettings.ENABLED) {
|
||||
this.logger.verbose('finding sqs in db');
|
||||
return await this.sqsModel.findOne({ _id: instance });
|
||||
}
|
||||
|
||||
this.logger.verbose('finding sqs in store');
|
||||
return JSON.parse(
|
||||
readFileSync(join(this.storePath, 'sqs', instance + '.json'), {
|
||||
encoding: 'utf-8',
|
||||
}),
|
||||
) as SqsRaw;
|
||||
} catch (error) {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
}
|
||||
68
src/whatsapp/repository/typebot.repository.ts
Normal file
68
src/whatsapp/repository/typebot.repository.ts
Normal file
@@ -0,0 +1,68 @@
|
||||
import { readFileSync } from 'fs';
|
||||
import { join } from 'path';
|
||||
|
||||
import { ConfigService } from '../../config/env.config';
|
||||
import { Logger } from '../../config/logger.config';
|
||||
import { IInsert, Repository } from '../abstract/abstract.repository';
|
||||
import { ITypebotModel, TypebotRaw } from '../models';
|
||||
|
||||
export class TypebotRepository extends Repository {
|
||||
constructor(private readonly typebotModel: ITypebotModel, private readonly configService: ConfigService) {
|
||||
super(configService);
|
||||
}
|
||||
|
||||
private readonly logger = new Logger('TypebotRepository');
|
||||
|
||||
public async create(data: TypebotRaw, instance: string): Promise<IInsert> {
|
||||
try {
|
||||
this.logger.verbose('creating typebot');
|
||||
if (this.dbSettings.ENABLED) {
|
||||
this.logger.verbose('saving typebot to db');
|
||||
const insert = await this.typebotModel.replaceOne({ _id: instance }, { ...data }, { upsert: true });
|
||||
|
||||
this.logger.verbose('typebot saved to db: ' + insert.modifiedCount + ' typebot');
|
||||
return { insertCount: insert.modifiedCount };
|
||||
}
|
||||
|
||||
this.logger.verbose('saving typebot to store');
|
||||
|
||||
this.writeStore<TypebotRaw>({
|
||||
path: join(this.storePath, 'typebot'),
|
||||
fileName: instance,
|
||||
data,
|
||||
});
|
||||
|
||||
this.logger.verbose('typebot saved to store in path: ' + join(this.storePath, 'typebot') + '/' + instance);
|
||||
|
||||
this.logger.verbose('typebot created');
|
||||
return { insertCount: 1 };
|
||||
} catch (error) {
|
||||
return error;
|
||||
}
|
||||
}
|
||||
|
||||
public async find(instance: string): Promise<TypebotRaw> {
|
||||
try {
|
||||
this.logger.verbose('finding typebot');
|
||||
if (this.dbSettings.ENABLED) {
|
||||
this.logger.verbose('finding typebot in db');
|
||||
return await this.typebotModel.findOne({ _id: instance });
|
||||
}
|
||||
|
||||
this.logger.verbose('finding typebot in store');
|
||||
return JSON.parse(
|
||||
readFileSync(join(this.storePath, 'typebot', instance + '.json'), {
|
||||
encoding: 'utf-8',
|
||||
}),
|
||||
) as TypebotRaw;
|
||||
} catch (error) {
|
||||
return {
|
||||
enabled: false,
|
||||
url: '',
|
||||
typebot: '',
|
||||
expire: 0,
|
||||
sessions: [],
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
62
src/whatsapp/repository/websocket.repository.ts
Normal file
62
src/whatsapp/repository/websocket.repository.ts
Normal file
@@ -0,0 +1,62 @@
|
||||
import { readFileSync } from 'fs';
|
||||
import { join } from 'path';
|
||||
|
||||
import { ConfigService } from '../../config/env.config';
|
||||
import { Logger } from '../../config/logger.config';
|
||||
import { IInsert, Repository } from '../abstract/abstract.repository';
|
||||
import { IWebsocketModel, WebsocketRaw } from '../models';
|
||||
|
||||
export class WebsocketRepository extends Repository {
|
||||
constructor(private readonly websocketModel: IWebsocketModel, private readonly configService: ConfigService) {
|
||||
super(configService);
|
||||
}
|
||||
|
||||
private readonly logger = new Logger('WebsocketRepository');
|
||||
|
||||
public async create(data: WebsocketRaw, instance: string): Promise<IInsert> {
|
||||
try {
|
||||
this.logger.verbose('creating websocket');
|
||||
if (this.dbSettings.ENABLED) {
|
||||
this.logger.verbose('saving websocket to db');
|
||||
const insert = await this.websocketModel.replaceOne({ _id: instance }, { ...data }, { upsert: true });
|
||||
|
||||
this.logger.verbose('websocket saved to db: ' + insert.modifiedCount + ' websocket');
|
||||
return { insertCount: insert.modifiedCount };
|
||||
}
|
||||
|
||||
this.logger.verbose('saving websocket to store');
|
||||
|
||||
this.writeStore<WebsocketRaw>({
|
||||
path: join(this.storePath, 'websocket'),
|
||||
fileName: instance,
|
||||
data,
|
||||
});
|
||||
|
||||
this.logger.verbose('websocket saved to store in path: ' + join(this.storePath, 'websocket') + '/' + instance);
|
||||
|
||||
this.logger.verbose('websocket created');
|
||||
return { insertCount: 1 };
|
||||
} catch (error) {
|
||||
return error;
|
||||
}
|
||||
}
|
||||
|
||||
public async find(instance: string): Promise<WebsocketRaw> {
|
||||
try {
|
||||
this.logger.verbose('finding websocket');
|
||||
if (this.dbSettings.ENABLED) {
|
||||
this.logger.verbose('finding websocket in db');
|
||||
return await this.websocketModel.findOne({ _id: instance });
|
||||
}
|
||||
|
||||
this.logger.verbose('finding websocket in store');
|
||||
return JSON.parse(
|
||||
readFileSync(join(this.storePath, 'websocket', instance + '.json'), {
|
||||
encoding: 'utf-8',
|
||||
}),
|
||||
) as WebsocketRaw;
|
||||
} catch (error) {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
}
|
||||
52
src/whatsapp/routers/chamaai.router.ts
Normal file
52
src/whatsapp/routers/chamaai.router.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
import { RequestHandler, Router } from 'express';
|
||||
|
||||
import { Logger } from '../../config/logger.config';
|
||||
import { chamaaiSchema, instanceNameSchema } from '../../validate/validate.schema';
|
||||
import { RouterBroker } from '../abstract/abstract.router';
|
||||
import { ChamaaiDto } from '../dto/chamaai.dto';
|
||||
import { InstanceDto } from '../dto/instance.dto';
|
||||
import { chamaaiController } from '../whatsapp.module';
|
||||
import { HttpStatus } from './index.router';
|
||||
|
||||
const logger = new Logger('ChamaaiRouter');
|
||||
|
||||
export class ChamaaiRouter extends RouterBroker {
|
||||
constructor(...guards: RequestHandler[]) {
|
||||
super();
|
||||
this.router
|
||||
.post(this.routerPath('set'), ...guards, async (req, res) => {
|
||||
logger.verbose('request received in setChamaai');
|
||||
logger.verbose('request body: ');
|
||||
logger.verbose(req.body);
|
||||
|
||||
logger.verbose('request query: ');
|
||||
logger.verbose(req.query);
|
||||
const response = await this.dataValidate<ChamaaiDto>({
|
||||
request: req,
|
||||
schema: chamaaiSchema,
|
||||
ClassRef: ChamaaiDto,
|
||||
execute: (instance, data) => chamaaiController.createChamaai(instance, data),
|
||||
});
|
||||
|
||||
res.status(HttpStatus.CREATED).json(response);
|
||||
})
|
||||
.get(this.routerPath('find'), ...guards, async (req, res) => {
|
||||
logger.verbose('request received in findChamaai');
|
||||
logger.verbose('request body: ');
|
||||
logger.verbose(req.body);
|
||||
|
||||
logger.verbose('request query: ');
|
||||
logger.verbose(req.query);
|
||||
const response = await this.dataValidate<InstanceDto>({
|
||||
request: req,
|
||||
schema: instanceNameSchema,
|
||||
ClassRef: InstanceDto,
|
||||
execute: (instance) => chamaaiController.findChamaai(instance),
|
||||
});
|
||||
|
||||
res.status(HttpStatus.OK).json(response);
|
||||
});
|
||||
}
|
||||
|
||||
public readonly router = Router();
|
||||
}
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
deleteMessageSchema,
|
||||
messageUpSchema,
|
||||
messageValidateSchema,
|
||||
presenceSchema,
|
||||
privacySettingsSchema,
|
||||
profileNameSchema,
|
||||
profilePictureSchema,
|
||||
@@ -26,6 +27,7 @@ import {
|
||||
ProfilePictureDto,
|
||||
ProfileStatusDto,
|
||||
ReadMessageDto,
|
||||
SendPresenceDto,
|
||||
WhatsAppNumberDto,
|
||||
} from '../dto/chat.dto';
|
||||
import { InstanceDto } from '../dto/instance.dto';
|
||||
@@ -228,6 +230,22 @@ export class ChatRouter extends RouterBroker {
|
||||
|
||||
return res.status(HttpStatus.OK).json(response);
|
||||
})
|
||||
.post(this.routerPath('sendPresence'), ...guards, async (req, res) => {
|
||||
logger.verbose('request received in sendPresence');
|
||||
logger.verbose('request body: ');
|
||||
logger.verbose(req.body);
|
||||
|
||||
logger.verbose('request query: ');
|
||||
logger.verbose(req.query);
|
||||
const response = await this.dataValidate<null>({
|
||||
request: req,
|
||||
schema: presenceSchema,
|
||||
ClassRef: SendPresenceDto,
|
||||
execute: (instance, data) => chatController.sendPresence(instance, data),
|
||||
});
|
||||
|
||||
return res.status(HttpStatus.CREATED).json(response);
|
||||
})
|
||||
// Profile routes
|
||||
.get(this.routerPath('fetchPrivacySettings'), ...guards, async (req, res) => {
|
||||
logger.verbose('request received in fetchPrivacySettings');
|
||||
|
||||
@@ -4,14 +4,20 @@ import fs from 'fs';
|
||||
import { Auth, configService } from '../../config/env.config';
|
||||
import { authGuard } from '../guards/auth.guard';
|
||||
import { instanceExistsGuard, instanceLoggedGuard } from '../guards/instance.guard';
|
||||
import { ChamaaiRouter } from './chamaai.router';
|
||||
import { ChatRouter } from './chat.router';
|
||||
import { ChatwootRouter } from './chatwoot.router';
|
||||
import { GroupRouter } from './group.router';
|
||||
import { InstanceRouter } from './instance.router';
|
||||
import { ProxyRouter } from './proxy.router';
|
||||
import { RabbitmqRouter } from './rabbitmq.router';
|
||||
import { MessageRouter } from './sendMessage.router';
|
||||
import { SettingsRouter } from './settings.router';
|
||||
import { SqsRouter } from './sqs.router';
|
||||
import { TypebotRouter } from './typebot.router';
|
||||
import { ViewsRouter } from './view.router';
|
||||
import { WebhookRouter } from './webhook.router';
|
||||
import { WebsocketRouter } from './websocket.router';
|
||||
|
||||
enum HttpStatus {
|
||||
OK = 200,
|
||||
@@ -25,24 +31,36 @@ enum HttpStatus {
|
||||
|
||||
const router = Router();
|
||||
const authType = configService.get<Auth>('AUTHENTICATION').TYPE;
|
||||
const serverConfig = configService.get('SERVER');
|
||||
const guards = [instanceExistsGuard, instanceLoggedGuard, authGuard[authType]];
|
||||
|
||||
const packageJson = JSON.parse(fs.readFileSync('./package.json', 'utf8'));
|
||||
|
||||
if (!serverConfig.DISABLE_MANAGER) router.use('/manager', new ViewsRouter().router);
|
||||
|
||||
router
|
||||
.get('/', (req, res) => {
|
||||
res.status(HttpStatus.OK).json({
|
||||
status: HttpStatus.OK,
|
||||
message: 'Welcome to the Evolution API, it is working!',
|
||||
version: packageJson.version,
|
||||
swagger: !serverConfig.DISABLE_DOCS ? `${req.protocol}://${req.get('host')}/docs` : undefined,
|
||||
manager: !serverConfig.DISABLE_MANAGER ? `${req.protocol}://${req.get('host')}/manager` : undefined,
|
||||
documentation: `https://doc.evolution-api.com`,
|
||||
});
|
||||
})
|
||||
.use('/instance', new InstanceRouter(configService, ...guards).router, new ViewsRouter(instanceExistsGuard).router)
|
||||
.use('/instance', new InstanceRouter(configService, ...guards).router)
|
||||
.use('/message', new MessageRouter(...guards).router)
|
||||
.use('/chat', new ChatRouter(...guards).router)
|
||||
.use('/group', new GroupRouter(...guards).router)
|
||||
.use('/webhook', new WebhookRouter(...guards).router)
|
||||
.use('/chatwoot', new ChatwootRouter(...guards).router)
|
||||
.use('/settings', new SettingsRouter(...guards).router);
|
||||
.use('/settings', new SettingsRouter(...guards).router)
|
||||
.use('/websocket', new WebsocketRouter(...guards).router)
|
||||
.use('/rabbitmq', new RabbitmqRouter(...guards).router)
|
||||
.use('/sqs', new SqsRouter(...guards).router)
|
||||
.use('/typebot', new TypebotRouter(...guards).router)
|
||||
.use('/proxy', new ProxyRouter(...guards).router)
|
||||
.use('/chamaai', new ChamaaiRouter(...guards).router);
|
||||
|
||||
export { HttpStatus, router };
|
||||
|
||||
@@ -2,7 +2,7 @@ import { RequestHandler, Router } from 'express';
|
||||
|
||||
import { Auth, ConfigService, Database } from '../../config/env.config';
|
||||
import { Logger } from '../../config/logger.config';
|
||||
import { dbserver } from '../../db/db.connect';
|
||||
import { dbserver } from '../../libs/db.connect';
|
||||
import { instanceNameSchema, oldTokenSchema } from '../../validate/validate.schema';
|
||||
import { RouterBroker } from '../abstract/abstract.router';
|
||||
import { InstanceDto } from '../dto/instance.dto';
|
||||
@@ -161,7 +161,9 @@ export class InstanceRouter extends RouterBroker {
|
||||
if (db.ENABLED) {
|
||||
try {
|
||||
await dbserver.dropDatabase();
|
||||
return res.status(HttpStatus.CREATED).json({ error: false, message: 'Database deleted' });
|
||||
return res
|
||||
.status(HttpStatus.CREATED)
|
||||
.json({ status: 'SUCCESS', error: false, response: { message: 'database deleted' } });
|
||||
} catch (error) {
|
||||
return res.status(HttpStatus.INTERNAL_SERVER_ERROR).json({ error: true, message: error.message });
|
||||
}
|
||||
|
||||
52
src/whatsapp/routers/proxy.router.ts
Normal file
52
src/whatsapp/routers/proxy.router.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
import { RequestHandler, Router } from 'express';
|
||||
|
||||
import { Logger } from '../../config/logger.config';
|
||||
import { instanceNameSchema, proxySchema } from '../../validate/validate.schema';
|
||||
import { RouterBroker } from '../abstract/abstract.router';
|
||||
import { InstanceDto } from '../dto/instance.dto';
|
||||
import { ProxyDto } from '../dto/proxy.dto';
|
||||
import { proxyController } from '../whatsapp.module';
|
||||
import { HttpStatus } from './index.router';
|
||||
|
||||
const logger = new Logger('ProxyRouter');
|
||||
|
||||
export class ProxyRouter extends RouterBroker {
|
||||
constructor(...guards: RequestHandler[]) {
|
||||
super();
|
||||
this.router
|
||||
.post(this.routerPath('set'), ...guards, async (req, res) => {
|
||||
logger.verbose('request received in setProxy');
|
||||
logger.verbose('request body: ');
|
||||
logger.verbose(req.body);
|
||||
|
||||
logger.verbose('request query: ');
|
||||
logger.verbose(req.query);
|
||||
const response = await this.dataValidate<ProxyDto>({
|
||||
request: req,
|
||||
schema: proxySchema,
|
||||
ClassRef: ProxyDto,
|
||||
execute: (instance, data) => proxyController.createProxy(instance, data),
|
||||
});
|
||||
|
||||
res.status(HttpStatus.CREATED).json(response);
|
||||
})
|
||||
.get(this.routerPath('find'), ...guards, async (req, res) => {
|
||||
logger.verbose('request received in findProxy');
|
||||
logger.verbose('request body: ');
|
||||
logger.verbose(req.body);
|
||||
|
||||
logger.verbose('request query: ');
|
||||
logger.verbose(req.query);
|
||||
const response = await this.dataValidate<InstanceDto>({
|
||||
request: req,
|
||||
schema: instanceNameSchema,
|
||||
ClassRef: InstanceDto,
|
||||
execute: (instance) => proxyController.findProxy(instance),
|
||||
});
|
||||
|
||||
res.status(HttpStatus.OK).json(response);
|
||||
});
|
||||
}
|
||||
|
||||
public readonly router = Router();
|
||||
}
|
||||
52
src/whatsapp/routers/rabbitmq.router.ts
Normal file
52
src/whatsapp/routers/rabbitmq.router.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
import { RequestHandler, Router } from 'express';
|
||||
|
||||
import { Logger } from '../../config/logger.config';
|
||||
import { instanceNameSchema, rabbitmqSchema } from '../../validate/validate.schema';
|
||||
import { RouterBroker } from '../abstract/abstract.router';
|
||||
import { InstanceDto } from '../dto/instance.dto';
|
||||
import { RabbitmqDto } from '../dto/rabbitmq.dto';
|
||||
import { rabbitmqController } from '../whatsapp.module';
|
||||
import { HttpStatus } from './index.router';
|
||||
|
||||
const logger = new Logger('RabbitmqRouter');
|
||||
|
||||
export class RabbitmqRouter extends RouterBroker {
|
||||
constructor(...guards: RequestHandler[]) {
|
||||
super();
|
||||
this.router
|
||||
.post(this.routerPath('set'), ...guards, async (req, res) => {
|
||||
logger.verbose('request received in setRabbitmq');
|
||||
logger.verbose('request body: ');
|
||||
logger.verbose(req.body);
|
||||
|
||||
logger.verbose('request query: ');
|
||||
logger.verbose(req.query);
|
||||
const response = await this.dataValidate<RabbitmqDto>({
|
||||
request: req,
|
||||
schema: rabbitmqSchema,
|
||||
ClassRef: RabbitmqDto,
|
||||
execute: (instance, data) => rabbitmqController.createRabbitmq(instance, data),
|
||||
});
|
||||
|
||||
res.status(HttpStatus.CREATED).json(response);
|
||||
})
|
||||
.get(this.routerPath('find'), ...guards, async (req, res) => {
|
||||
logger.verbose('request received in findRabbitmq');
|
||||
logger.verbose('request body: ');
|
||||
logger.verbose(req.body);
|
||||
|
||||
logger.verbose('request query: ');
|
||||
logger.verbose(req.query);
|
||||
const response = await this.dataValidate<InstanceDto>({
|
||||
request: req,
|
||||
schema: instanceNameSchema,
|
||||
ClassRef: InstanceDto,
|
||||
execute: (instance) => rabbitmqController.findRabbitmq(instance),
|
||||
});
|
||||
|
||||
res.status(HttpStatus.OK).json(response);
|
||||
});
|
||||
}
|
||||
|
||||
public readonly router = Router();
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user