Compare commits

..

584 Commits
1.5.4 ... 1.8.1

Author SHA1 Message Date
Davidson Gomes
dfb003fd72 Merge branch 'release/1.8.1' 2024-06-12 19:49:37 -03:00
Davidson Gomes
5239e431c2 feat: update server and monitor services
Updated server.module.ts and monitor.service.ts to improve service initialization and monitoring logic. Modified main.ts to integrate changes. This enhances the application's performance and reliability.
2024-06-12 19:48:56 -03:00
Davidson Gomes
53cc6132f5 Merge tag '1.8.1' into develop
v
2024-06-12 19:03:36 -03:00
Davidson Gomes
0bc1b78db9 Merge branch 'release/1.8.1' 2024-06-12 19:03:30 -03:00
Davidson Gomes
2a9412c81a chore: adjust socket configuration and update baileys version
Updated package.json to include the latest version of baileys for improved functionality. Modified whatsapp.baileys.service.ts to adjust socket configuration, enhancing the stability and performance of the service.
2024-06-12 19:02:36 -03:00
Davidson Gomes
f707cf4109 Merge tag '1.8.1' into develop
v
2024-06-09 14:22:20 -03:00
Davidson Gomes
410cfc8bcb Merge branch 'release/1.8.1' 2024-06-09 14:22:17 -03:00
Davidson Gomes
f1a3fd872f git actions v2 2024-06-09 14:20:48 -03:00
Davidson Gomes
fff0ca5c79 Merge tag '1.8.1' into develop
v
2024-06-08 21:33:12 -03:00
Davidson Gomes
bd069200ea Merge branch 'release/1.8.1' 2024-06-08 21:33:02 -03:00
Davidson Gomes
c898f1e62a v1.8.1 2024-06-08 21:32:50 -03:00
Davidson Gomes
2002e1c38d changelog 2024-06-08 21:31:02 -03:00
Davidson Gomes
1f817df5f6 fix: Correction of variables breaking lines in typebot 2024-06-08 21:27:58 -03:00
Davidson Gomes
8eced6c575 Merge branch 'develop' of github.com:EvolutionAPI/evolution-api into develop 2024-06-05 21:48:53 -03:00
Davidson Gomes
afcf6eab71 cacheGroupMetadata 2024-06-05 21:48:37 -03:00
Davidson Gomes
9633412ef6 cacheGroupMetadata 2024-06-05 21:39:33 -03:00
Davidson Gomes
8034e7f587 cacheGroupMetadata 2024-06-05 21:25:55 -03:00
Davidson Gomes
5be01e1c18 Merge pull request #629 from cark7/develop
fix: added validation when sending gif image
2024-06-04 16:59:36 -03:00
Carlos Chinchilla
6371773416 fix: added validation when sending gif image 2024-06-04 15:14:02 -03:00
Davidson Gomes
52230edc5c session worker compatibility 2024-06-03 19:06:00 -03:00
Davidson Gomes
dd123c6a99 Adjust to repository from session worker 2024-06-01 19:47:43 -03:00
Davidson Gomes
5b2a0fdcb1 Adjust to repository from session worker 2024-06-01 19:29:37 -03:00
Davidson Gomes
9354af3bc7 Adjust to repository from session worker 2024-06-01 19:20:59 -03:00
Davidson Gomes
f48f331d43 New method of saving sessions to a file using worker 2024-06-01 13:28:29 -03:00
Davidson Gomes
696261d749 New method of saving sessions to a file using worker 2024-06-01 09:54:56 -03:00
Davidson Gomes
0e9d036fe0 Merge pull request #622 from jrCleber/main
Adiciona templates para Relatório de Bug e Solicitação de Recursos
2024-05-29 10:34:23 -03:00
Cleber Wilson
8e19fe2fb5 labels para filtros adicionada 2024-05-29 10:33:23 -03:00
Cleber Wilson
861b690217 Adiciona templates para Relatório de Bug e Solicitação de Recursos
- Cria um template para Relatório de Bug com os seguintes campos:
  - Termos de aceite
  - Descrição detalhada do problema
  - Expectativa versus observação
  - Capturas de tela/vídeos
  - Versão da API
  - Ambiente
  - Especificações adicionais do ambiente
  - Logs (se aplicável)
  - Notas adicionais

- Cria um template para Solicitação de Recursos com os seguintes campos:
  - Termos de aceite
  - Tipo de recurso
  - Motivação para a solicitação
  - Exemplos de uso
  - Ideias de desenvolvimento
  - Notas adicionais
2024-05-29 10:08:42 -03:00
Davidson Gomes
f7c9541f5e Merge tag '1.8.0' into develop
v
2024-05-27 16:36:08 -03:00
Davidson Gomes
05b5ae8a84 Merge branch 'release/1.8.0' 2024-05-27 16:36:03 -03:00
Davidson Gomes
2e9c14a0a8 fix: security fix in fetch instance with client key when not connected to mongodb 2024-05-27 16:35:59 -03:00
Davidson Gomes
3350cec589 fix: security fix in fetch instance with client key when not connected to mongodb 2024-05-27 16:35:55 -03:00
Davidson Gomes
bd7a42646b Merge tag '1.8.0' into develop
v
2024-05-27 16:24:29 -03:00
Davidson Gomes
2ae4ddee18 Merge branch 'release/1.8.0' 2024-05-27 16:24:25 -03:00
Davidson Gomes
2fb6e2b209 fix: Build in docker for linux/amd64, linux/arm64 platforms 2024-05-27 16:24:21 -03:00
Davidson Gomes
91f869b136 Merge tag '1.8.0' into develop
v
2024-05-27 16:17:30 -03:00
Davidson Gomes
1284a97625 Merge branch 'release/1.8.0' 2024-05-27 16:17:27 -03:00
Davidson Gomes
3a5cfbf36f fix: Build in docker for linux/amd64, linux/arm64 platforms 2024-05-27 16:17:22 -03:00
Davidson Gomes
47b8ecd43b Merge tag '1.8.0' into develop
v
2024-05-27 16:09:16 -03:00
Davidson Gomes
25ce1a4689 Merge branch 'release/1.8.0' 2024-05-27 16:09:12 -03:00
Davidson Gomes
a5156709ab fix: Build in docker for linux/amd64, linux/arm64 platforms 2024-05-27 16:09:07 -03:00
Davidson Gomes
fd2d37d862 Merge tag '1.8.0' into develop
v
2024-05-27 16:07:19 -03:00
Davidson Gomes
87bf433e9a Merge branch 'release/1.8.0' 2024-05-27 16:07:15 -03:00
Davidson Gomes
b5ec79daae fix: Build in docker for linux/amd64, linux/arm64 platforms 2024-05-27 16:07:03 -03:00
Davidson Gomes
3b19200997 Merge tag '1.7.6' into develop
v
2024-05-27 15:54:45 -03:00
Davidson Gomes
0ab040080c Merge branch 'release/1.7.6' 2024-05-27 15:54:42 -03:00
Davidson Gomes
5719896d18 fix: git actions 2024-05-27 15:54:31 -03:00
Davidson Gomes
e9e1cb6ebc Merge tag '1.7.6' into develop
v
2024-05-27 15:51:22 -03:00
Davidson Gomes
ceee6a7e74 Merge branch 'release/1.7.6' 2024-05-27 15:51:19 -03:00
Davidson Gomes
ebfc6d4fa5 fix: git actions 2024-05-27 15:51:06 -03:00
Davidson Gomes
0d62557a15 Merge pull request #609 from stack-app-br/main
chore: build docker image to platforms: linux/amd64,linux/arm64
2024-05-27 15:45:28 -03:00
Davidson Gomes
097c09328f Merge pull request #615 from deivisonrpg/fix-merge-brazil-contacts
fix(chatwoot): add merge_brazil_contacts flag to ChannelStartupService and add logs
2024-05-27 15:34:56 -03:00
Davidson Gomes
732103292b Merge pull request #618 from edisoncm-ti/develop
fix: Correction to Postgres connection string in environment files
2024-05-27 15:34:29 -03:00
edisoncm-ti
3191e9b450 fix: Correction to Postgres connection string in environment files 2024-05-27 15:14:52 -03:00
Davidson Gomes
bf88fdbb31 fix: adjust send presence 2024-05-27 12:57:18 -03:00
Davidson Gomes
4c3fb5e762 correction in message formatting when generated by AI as markdown in typebot 2024-05-27 09:27:42 -03:00
Davidson Gomes
e4b6f4ff0d correction in message formatting when generated by AI as markdown in typebot 2024-05-27 09:23:53 -03:00
Deivison Lincoln
ca0b0d4602 Add merge_brazil_contacts flag to ChannelStartupService 2024-05-25 10:09:37 -03:00
Deivison Lincoln
d7c3779ff7 fix: Add merge_brazil_contacts flag to ChannelStartupService 2024-05-25 10:05:04 -03:00
Davidson Gomes
f0d40eea15 New global mode for rabbitmq events 2024-05-24 18:48:54 -03:00
Davidson Gomes
68dcc1c86a adjusts in create instance 2024-05-23 11:56:52 -03:00
Davidson Gomes
f7dcab3736 Now in the manager, when logging in with the client's apikey, the listing only shows the instance corresponding to the provided apikey (only with MongoDB) 2024-05-23 11:31:45 -03:00
Davidson Gomes
2fcb476c50 Now in the manager, when logging in with the client's apikey, the listing only shows the instance corresponding to the provided apikey (only with MongoDB) 2024-05-23 11:30:47 -03:00
Antonio Milesi Bastos
44a7cd62c8 Merge pull request #1 from stack-app-br/releases/1.7.5
chore: build docker image to platforms: linux/amd64,linux/arm64
2024-05-21 11:56:53 -03:00
@milesibastos
f7f0f8251b chore: build docker image to platforms: linux/amd64,linux/arm64 2024-05-21 11:48:13 -03:00
Davidson Gomes
395b81a6ac Merge tag '1.7.5' into develop
v
2024-05-21 08:53:58 -03:00
Davidson Gomes
da06ed1185 Merge branch 'release/1.7.5' 2024-05-21 08:53:53 -03:00
Davidson Gomes
67afbd6a77 v1.7.5 2024-05-21 08:53:35 -03:00
Davidson Gomes
8fce53b4af fixed version whatsapp web for baileys 2024-05-21 08:38:08 -03:00
Davidson Gomes
65bba23519 update baileys version 2024-05-20 19:53:23 -03:00
Davidson Gomes
283b497788 downgrade baileys version 2024-05-20 19:39:56 -03:00
Davidson Gomes
8cc1b8daa8 Merge pull request #590 from Ckk3/fix-envvar-CLEAN_STORE_CLEANING_INTERVAL
Corrigindo variável inexistente do arquivo env.config.ts e substituindo pela correta CLEAN_STORE_CLEANING_INTERVAL
2024-05-09 14:15:09 -03:00
Davidson Gomes
9ccdb45a7a Merge pull request #587 from deivisonrpg/chatwoot-merge-contact-with-nine
feature(chatwoot): add merge_brazil_contacts function to solve nine digit in brazilian numbers
2024-05-09 14:12:31 -03:00
Davidson Gomes
f78d360c38 Merge pull request #586 from deivisonrpg/chatwoot-update-contact
refactor(chatwoot): optimize ChatwootService method for updating contact inform…
2024-05-09 14:11:07 -03:00
Ckk3
6144fbe856 changing CLEAN_STORE_CLEANING_TERMINAL to CLEAN_STORE_CLEANING_INTERVAL 2024-05-09 13:21:28 -03:00
Deivison Lincoln
8446be7646 fix(chatwoot): fix grupos 2024-05-08 16:47:21 -03:00
Deivison Lincoln
8875ab1e93 feat: add merge_brazil_contacts flag to instance controller 2024-05-08 15:26:56 -03:00
Deivison Lincoln
7c207a50e1 feat: add merge_brazil_contacts to swagger doc 2024-05-08 15:11:37 -03:00
Deivison Lincoln
dcc32479ff feature(chatwoot): add merge_brazil_contacts function to solve nine digit in brazilian numbers 2024-05-08 15:03:37 -03:00
Deivison Lincoln
09911c472d refactor: optimize ChatwootService method for updating contact information 2024-05-08 14:34:03 -03:00
Davidson Gomes
7a0149ee23 Merge pull request #571 from stack-app-br/main
chore: build docker image to platforms: linux/amd64, linux/arm64
2024-05-08 11:02:11 -03:00
Davidson Gomes
3ae8cf32b0 Merge pull request #584 from Al1st1c/fix/swagger-auth
Fix/swagger auth
2024-05-08 09:56:59 -03:00
Davidson Gomes
11cf947bbb Merge pull request #583 from deivisonrpg/chore-update-aws-sdk-v3
chore(aws sqs): update aws sdk v3
2024-05-08 09:56:24 -03:00
Davidson Gomes
60a20f61af Merge pull request #582 from deivisonrpg/hotfix-getOpenConversationByContact
fix(chatwoot): getOpenConversationByContact and init queries error
2024-05-08 09:55:08 -03:00
Davidson Gomes
e65c7b6bcf Merge pull request #577 from neanderdev/mark-chat-as-unread
feat: method to mark chat as unread
2024-05-08 09:54:37 -03:00
Al1stic
80c892aca3 fix env 2024-05-06 19:31:37 -03:00
dev2
39ee266598 fix: correcao swagger sem authkey 2024-05-06 19:28:57 -03:00
Deivison Lincoln
aa58d7744e chore: update AWS SDK dependency to version 3.569.0 2024-05-06 17:28:41 -03:00
Deivison Lincoln
ea3b0b3712 fix: optimize ChatwootService method for retrieving open conversations 2024-05-06 16:30:16 -03:00
Deivison Lincoln
d9d8707123 chore: update baileys to version 6.7.2 2024-05-06 16:29:37 -03:00
Neander de Souza
8e9a1e2ba5 feat: method to mark chat as unread 2024-05-06 09:08:36 -03:00
Davidson Gomes
e29b4865e8 update baileys 2024-05-01 19:20:40 -03:00
@milesibastos
4b60ca175d chore: build docker image to platforms: linux/amd64,linux/arm64 2024-04-30 17:48:35 -03:00
Davidson Gomes
2fbbc7b5a9 Merge tag '1.7.4' into develop
v
2024-04-28 09:47:24 -03:00
Davidson Gomes
633dbb82d3 Merge branch 'release/1.7.4' 2024-04-28 09:47:19 -03:00
Davidson Gomes
95907b3cea v 1.7.4 2024-04-28 09:47:06 -03:00
Davidson Gomes
14c210c771 fix: new version baileys 2024-04-28 09:41:22 -03:00
Davidson Gomes
00e7fcc46b changelog 2024-04-27 13:13:43 -03:00
Davidson Gomes
720efcbcbf Merge pull request #566 from judsonjuniorr/feat/cw-inbox-name
Chatwoot inbox name
2024-04-27 13:13:06 -03:00
Davidson Gomes
d95791cc18 changelog 2024-04-27 13:12:29 -03:00
Davidson Gomes
d45b7af3b6 fix: recovering messages 2024-04-27 13:11:33 -03:00
Davidson Gomes
0ad3acaf07 Merge branch 'develop' of github.com:EvolutionAPI/evolution-api into develop 2024-04-24 18:55:49 -03:00
Davidson Gomes
e27818ecda adjusts in integration 2024-04-24 18:55:38 -03:00
Davidson Gomes
ff21cf4d4c Merge pull request #558 from jaison-x/pr2
feat(chatwoot): send private message on error message sent from chatwoot
2024-04-23 19:11:10 -03:00
Davidson Gomes
dc19c7fdec Merge pull request #555 from jaison-x/pr
fix(chatwoot): fix bug when chatwoot params reopen_conversation and conversation_pending are enabled
2024-04-23 19:10:57 -03:00
jaison-x
6c8ffd8ec6 feat(chatwoot): send private message on error message sent from chatwoot 2024-04-23 17:26:22 -03:00
jaison-x
96fdb210be feat(chatwoot): send private message on error message sent from chatwoot 2024-04-23 17:15:41 -03:00
jaison-x
84ad8e0d6e feat(chatwoot): send private message on error message sent from chatwoot 2024-04-23 15:09:28 -03:00
Davidson Gomes
53361682f4 Recovering messages 2024-04-23 08:01:59 -03:00
jaison-x
0ee243f284 fix(chatwoot): fix bug when chatwoot params reopen_conversation and conversation_pending are enabled 2024-04-22 13:41:06 -03:00
Judson Cairo
26ff0b634f Revert "Update current inbox if exists"
This reverts commit f44ab0f678.
2024-04-21 16:49:15 -03:00
Judson Cairo
f44ab0f678 Update current inbox if exists 2024-04-21 16:46:49 -03:00
Judson Cairo
1f128747bb Fix inbox name on cw config 2024-04-21 16:37:27 -03:00
Judson Cairo
3bf975d90f Set chatwoot custom inbox name 2024-04-21 16:33:20 -03:00
Davidson Gomes
92f8951be4 Merge tag '2.3.9' into develop
v
2024-04-18 21:24:30 -03:00
Davidson Gomes
e071f56767 Merge branch 'release/2.3.9' 2024-04-18 21:24:27 -03:00
Davidson Gomes
c85619efcf fix: Recovering messages lost with redis cache 2024-04-18 17:33:09 -03:00
Davidson Gomes
27f9ae1e56 fix: Recovering messages lost with redis cache 2024-04-18 17:31:10 -03:00
Davidson Gomes
7449102d95 fix: Recovering messages lost with redis cache 2024-04-18 17:31:05 -03:00
Davidson Gomes
234a2c71b5 Merge pull request #543 from PauloAK/history-set-message-status
Adicionado message.status ao webhook MESSAGES_SET
2024-04-18 16:05:44 -03:00
Davidson Gomes
4dd5533202 fix: Adjusts in proxy on fetchAgent 2024-04-18 12:54:00 -03:00
Davidson Gomes
a4d1740754 fix: Adjusts in proxy on fetchAgent 2024-04-18 12:53:55 -03:00
Davidson Gomes
1a2ea1c38a Merge tag '1.7.3' into develop
* Revert fix audio encoding
* Recovering messages lost with redis cache
* Adjusts in redis for save instances
* Adjusts in proxy
* Revert pull request #523
* Added instance name on logs
* Added support for Spanish
* Fix error: invalid operator. The allowed operators for identifier are equal_to,not_equal_to in chatwoot
2024-04-18 12:12:40 -03:00
Davidson Gomes
371ec9b8d5 Merge branch 'release/1.7.3' 2024-04-18 12:12:27 -03:00
Davidson Gomes
5e288d57ea changelog 2024-04-18 12:12:11 -03:00
Davidson Gomes
61bd5b3484 fix: Adjusts in redis for save instances 2024-04-18 10:39:24 -03:00
Paulo Kramer
bb974e10f5 Added message status to the messaging-history.set 2024-04-18 01:34:52 -03:00
Davidson Gomes
4ed1335f89 changelog 2024-04-17 19:58:31 -03:00
Davidson Gomes
4274c7afdb Merge branch 'develop' of github.com:EvolutionAPI/evolution-api into develop 2024-04-17 18:48:11 -03:00
Davidson Gomes
27f67142c8 changelog 2024-04-17 18:46:50 -03:00
Davidson Gomes
ac8aba6ba4 Merge pull request #542 from nestordavalos/develop
 feat: add support for Spanish
2024-04-17 18:45:38 -03:00
Davidson Gomes
07ff61c070 changelog 2024-04-17 18:25:35 -03:00
nestordavalos
3645ac6704 Merge branch 'develop' of https://github.com/EvolutionAPI/evolution-api into develop 2024-04-17 17:20:42 -04:00
nestordavalos
9764719245 feat: add support for Spanish 2024-04-17 17:20:37 -04:00
Davidson Gomes
b3aeed7fc1 fix: retry messages 2024-04-17 18:14:11 -03:00
Davidson Gomes
5ae5d8546e fix: retry messages 2024-04-17 18:10:43 -03:00
Davidson Gomes
d190d8b1af Merge branch 'develop' of github.com:EvolutionAPI/evolution-api into develop 2024-04-17 12:43:55 -03:00
Davidson Gomes
c45538684d fix: fix audio encoding 2024-04-17 12:43:49 -03:00
Davidson Gomes
575e7bf4c4 Merge pull request #539 from jaison-x/pr2
fix: this.localProxy.proxy can be undefined
2024-04-17 12:08:54 -03:00
jaison-x
a4338d8472 fix: this.localProxy.proxy can be undefined 2024-04-17 11:03:55 -03:00
Davidson Gomes
7d542cf115 Merge pull request #538 from jaison-x/pr
fix(chatwoot): error: invalid operator. The allowed operators for identifier are [equal_to,not_equal_to]
2024-04-17 11:03:35 -03:00
jaison-x
9c9a542bbe fix(chatwoot): error: invalid operator. The allowed operators for identifier are [equal_to,not_equal_to]
In chatwoot version 3.8 we can't use the filter operator 'contains' for the field 'identifier'
2024-04-17 10:50:25 -03:00
Davidson Gomes
b6c56551bc fix: Revert pull request #523 2024-04-16 19:13:25 -03:00
Davidson Gomes
1379228196 fix: Revert pull request #523 2024-04-16 19:12:03 -03:00
Davidson Gomes
d6e19b9273 adjusts in v1.7.3 2024-04-16 19:08:44 -03:00
Davidson Gomes
5e0e4051dc Merge tag '1.7.2' into develop
* Mobile connection via sms (test)

* Adjusts in redis
* Send global event in websocket
* Adjusts in proxy
* Fix audio encoding
* Fix conversation read on chatwoot version 3.7
* Fix when receiving/sending messages from whatsapp desktop with ephemeral messages enabled
* Changed returned sessions on typebot status change
* Reorganization of files and folders
2024-04-12 17:32:30 -03:00
Davidson Gomes
8caf3a0a7b Merge branch 'release/1.7.2' 2024-04-12 17:32:18 -03:00
Davidson Gomes
96ae97664c v 1.7.2 2024-04-12 17:31:58 -03:00
Davidson Gomes
901b121695 fix: reorganization of files and folders 2024-04-12 17:28:02 -03:00
Davidson Gomes
3e88c9f0c7 fix: reorganization of files and folders 2024-04-12 17:23:28 -03:00
Davidson Gomes
94f5c130bf fix: reorganization of files and folders 2024-04-12 17:22:11 -03:00
Davidson Gomes
eb04c3f113 fix: reorganization of files and folders 2024-04-12 17:15:13 -03:00
Davidson Gomes
8ece6fb998 fix: reorganization of files and folders 2024-04-12 17:13:15 -03:00
Davidson Gomes
794213b5c6 fix: quoted messages 2024-04-12 15:53:06 -03:00
Davidson Gomes
ef4a9ab66b fix: retry messages 2024-04-12 15:11:04 -03:00
Davidson Gomes
86e978faad fix: send contact.upsert via webhook 2024-04-12 12:51:13 -03:00
Davidson Gomes
d5d8f2a318 fix: send contact.upsert via webhook 2024-04-12 12:33:31 -03:00
Davidson Gomes
4ca1fad335 Merge branch 'develop' of github.com:EvolutionAPI/evolution-api into develop 2024-04-12 12:00:24 -03:00
Davidson Gomes
0edf6bdcb1 fix: audio encoding 2024-04-12 12:00:14 -03:00
Davidson Gomes
c2839ddd56 Merge pull request #528 from jaison-x/pr
fix(chatwoot): fix conversation read on chatwoot version 3.7
2024-04-12 11:48:37 -03:00
jaison-x
28581f88b2 fix(chatwoot): fix conversation read on chatwoot version 3.7
if contact has more than one conversation, we could get the wrong conversation.
2024-04-12 11:30:19 -03:00
Davidson Gomes
833c625d18 Merge pull request #527 from jaison-x/pr
fix(chatwoot): fix when receiving/sending messages from whatsapp desktop with ephemeral messages enabled
2024-04-12 11:22:13 -03:00
Davidson Gomes
6e2a3a410a fix: audio encoding 2024-04-12 11:20:18 -03:00
jaison-x
3e85af9589 fix(chatwoot): fix when receiving/sending messages from whatsapp desktop with ephemeral messages enabled 2024-04-12 10:59:28 -03:00
Davidson Gomes
589dec52a3 ajustes proxy 2024-04-09 16:52:22 -03:00
Davidson Gomes
a5477509bd adjusts in redis 2024-04-09 07:43:20 -03:00
Davidson Gomes
5d951a96b5 adjusts in redis 2024-04-09 07:40:45 -03:00
Davidson Gomes
76552f6ae5 Merge branch 'develop' of github.com:EvolutionAPI/evolution-api into develop 2024-04-09 07:31:46 -03:00
Davidson Gomes
e153515847 adjusts in redis 2024-04-09 07:31:37 -03:00
Davidson Gomes
3bc692d894 adjusts in redis 2024-04-09 07:30:15 -03:00
Davidson Gomes
73360d66cd Merge pull request #523 from gabrielgranado/main
changed returned sessions on typebot status change
2024-04-09 06:46:24 -03:00
Davidson Gomes
04575d8051 adjusts in websocket 2024-04-09 06:44:59 -03:00
Joao Gabriel
9aba51cb45 changed returned sessions on typebot status change 2024-04-08 22:55:12 -03:00
Davidson Gomes
1172b6c871 mobile connection 2024-04-08 19:09:59 -03:00
Davidson Gomes
240f01f378 Merge tag '1.7.1' into develop
v
2024-04-03 10:45:55 -03:00
Davidson Gomes
5975117636 Merge branch 'release/1.7.1' 2024-04-03 10:45:51 -03:00
Davidson Gomes
51b285f665 v 1.7.1 2024-04-03 10:45:29 -03:00
Davidson Gomes
b3958d4735 Merge tag '1.7.1' into develop
v
2024-04-03 10:20:14 -03:00
Davidson Gomes
128119d494 Merge branch 'release/1.7.1' 2024-04-03 10:20:09 -03:00
Davidson Gomes
324dc01699 v 1.7.1 2024-04-03 10:19:59 -03:00
Davidson Gomes
f11d468e31 ajustes wa business 2024-04-03 08:14:56 -03:00
Davidson Gomes
aa5ed13752 ajustes wa business 2024-04-03 07:36:51 -03:00
Davidson Gomes
10ce7da18d changelog 2024-04-03 07:15:23 -03:00
Davidson Gomes
73bd55dfac changelog 2024-04-03 07:06:41 -03:00
Davidson Gomes
db10f81d53 Merge pull request #507 from Azzybot/patch-7
Reconhecer tipos de mensagens
2024-04-03 07:06:23 -03:00
Davidson Gomes
717aac4438 Merge pull request #506 from Azzybot/patch-6
Recurso para coletar tipo de mensagem
2024-04-03 07:06:01 -03:00
Davidson Gomes
077a464481 Merge pull request #505 from Azzybot/patch-5
Removido obrigatoriedade de descrição dos rows do sendList
2024-04-03 07:05:09 -03:00
Davidson Gomes
9bf18592fc ajustes wa oficial 2024-04-03 07:01:35 -03:00
Luis-Fernando
677e196c96 Ajuste Metodo getMessage 2024-04-02 20:43:58 -03:00
Luis-Fernando
19e6896f0d Reconhecer tipos de mensagens
Adicionado metodos para capturar diferentes tipos de mensagens alem do tipo texto.

Incluido:
AUDIO, VIDEO, IMAGEM, DOCUMENTOS E VISUALIZAÇÃO ÚNICA, Etc...
2024-04-02 18:59:08 -03:00
Luis-Fernando
ba537baab2 Recurso para coletar tipo de mensagem
Variável {{messageType}} adicionada para ser usada dentro do fluxo e a partir disso usar condições para fluxo seguir de acordo com o tipo de mensagem recebida:

Exemplo:
if {{messageTyp}} = audioMessage
if {{messageTyp}} = videoMessage
if {{messageTyp}} = imageMessage
if {{messageTyp}} = document
if {{messageTyp}} = conversation

Entre outras...

Exemplo:

![VideoCapture_20240402-144703](https://github.com/EvolutionAPI/evolution-api/assets/136400011/60b74721-9f14-4d60-99f2-369c42cd60de)
![VideoCapture_20240402-144739](https://github.com/EvolutionAPI/evolution-api/assets/136400011/f4febd4b-c42e-4678-bfd4-f1aa076c92f5)
![VideoCapture_20240402-144724](https://github.com/EvolutionAPI/evolution-api/assets/136400011/7eb0ba56-a05a-4015-8863-76e29e0613f6)
2024-04-02 18:37:00 -03:00
Luis-Fernando
2b8b6b086b Removido obrigatoriedade de descrição dos rows do sendList
Removida a obrigatoriedade da descrição do rows para ser algo opcional.

Isso permite os "circulos" das opções ficarem mais alinhados com os titulos sem a necessidade de adicionar espaço em branco caso não queira adicionar a descrição.

{
"number": "559999999999 (Recipient number with Country Code)",
"options": {
"delay": 1200,
"presence": "composing"
},
"listMessage": {
"title": "List Title",
"description": "List description",
"buttonText": "Click Here",
"footerText": "footer list\nhttps://examplelink.com.br",
"sections": [
{
"title": "Row tilte 01",
"rows": [
{
"title": "Title row 01",
"description": "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s,",
"rowId": "rowId 001"
},
{
"title": "Title row 02",
"description": "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s,",
"rowId": "rowId 002"
}
]
}
}

Depois:

{
"number": "559999999999 (Recipient number with Country Code)",
"options": {
"delay": 1200,
"presence": "composing"
},
"listMessage": {
"title": "List Title",
"description": "List description",
"buttonText": "Click Here",
"footerText": "footer list\nhttps://examplelink.com.br",
"sections": [
{
"title": "Row tilte 01",
"rows": [
{
"title": "Title row 01",
// "description": "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s,", //podendo remover por completo como foi feito no Title row 02 abaixo ou apenas comentar a descrição
"rowId": "rowId 001"
},
{
"title": "Title row 02",
"rowId": "rowId 002"
}
]
}
}
2024-04-02 17:59:54 -03:00
Davidson Gomes
cf3ec2b601 ajustes 2024-04-02 16:05:14 -03:00
Davidson Gomes
35eaa7852c Merge tag '1.7.0' into develop
v
2024-03-27 16:36:29 -03:00
Davidson Gomes
b87558d301 Merge branch 'release/1.7.0' 2024-03-27 16:36:26 -03:00
Davidson Gomes
9f1003e94e rabbitmq 2024-03-27 15:05:00 -03:00
Davidson Gomes
73c003907b rabbitmq 2024-03-27 14:54:51 -03:00
Davidson Gomes
b65d292af4 Merge tag '1.7.0' into develop
v
2024-03-27 14:26:06 -03:00
Davidson Gomes
43bdbf1018 Merge branch 'release/1.7.0' 2024-03-27 14:26:03 -03:00
Davidson Gomes
ec7986420d rabbitmq 2024-03-27 13:21:39 -03:00
Davidson Gomes
0b23153316 Merge tag '1.7.0' into develop
v
2024-03-27 12:30:15 -03:00
Davidson Gomes
ad4f5e5e65 Merge branch 'release/1.7.0' 2024-03-27 12:30:11 -03:00
Davidson Gomes
1d48532146 Merge branch 'develop' of github.com:EvolutionAPI/evolution-api into develop 2024-03-27 09:54:22 -03:00
Davidson Gomes
950803b2aa rabbitmq 2024-03-27 09:54:05 -03:00
Davidson Gomes
a41d92f4da Merge pull request #479 from dv336699/develop
feat(endpoint): add setPresence endpoint
2024-03-24 06:44:15 -03:00
Davidson Gomes
70d4eb393f Merge tag '1.7.0' into develop
* Added update message endpoint
* Add translate capabilities to QRMessages in CW
* Join in Group by Invite Code
* Read messages from whatsapp in chatwoot
* Add support to use use redis in cacheservice
* Add support for labels
* Command to clearcache from chatwoot inbox
* Whatsapp Cloud API Oficial

* Proxy configuration improvements
* Correction in sending lists
* Adjust in webhook_base64
* Correction in typebot text formatting
* Correction in chatwoot text formatting and render list message
* Only use a axios request to get file mimetype if necessary
* When possible use the original file extension
* When receiving a file from whatsapp, use the original filename in chatwoot if possible
* Remove message ids cache in chatwoot to use chatwoot's api itself
* Adjusts the quoted message, now has contextInfo in the message Raw
* Collecting responses with text or numbers in Typebot
* Added sendList endpoint to swagger documentation
* Implemented a function to synchronize message deletions on WhatsApp, automatically reflecting in Chatwoot.
* Improvement on numbers validation
* Fix polls in message sending
* Sending status message
* Message 'connection successfully' spamming
* Invalidate the conversation cache if reopen_conversation is false and the conversation was resolved
* Fix looping when deleting a message in chatwoot
* When receiving a file from whatsapp, use the original filename in chatwoot if possible
* Correction in the sendList Function
* Implement contact upsert in messaging-history.set
* Improve proxy error handling
* Refactor fetching participants for group in WhatsApp service
* Fixed problem where the typebot final keyword did not work
* Typebot's wait now pauses the flow and composing is defined by the delay_message parameter in set typebot
* Composing over 20s now loops until finished
2024-03-19 14:10:33 -03:00
Davidson Gomes
9f162127d0 Merge branch 'release/1.7.0' 2024-03-19 14:10:15 -03:00
Davidson Gomes
4cd30dabc4 Merge branch 'develop' of github.com:EvolutionAPI/evolution-api into develop 2024-03-19 14:09:56 -03:00
Davidson Gomes
8f78728863 Merge tag '1.7.0' into develop
v
2024-03-19 14:09:44 -03:00
Davidson Gomes
e7c5d33d50 Merge branch 'release/1.7.0' 2024-03-19 14:09:40 -03:00
Diego Vieira
5400f31acb feat(endpoint): move setPresence endpoint to instance 2024-03-19 08:31:53 +00:00
Diego Vieira
e58f1d778e feat(endpoint): add setPresence endpoint 2024-03-19 00:58:45 +00:00
Davidson Gomes
5f5db2011e Merge pull request #478 from jaison-x/pr2
fix: add missing example env variables
2024-03-18 15:35:59 -03:00
Davidson Gomes
aea4d89f62 Merge pull request #477 from jaison-x/pr
fix(chatwoot): fix config name
2024-03-18 15:35:13 -03:00
jaison-x
ccda17d704 fix: add missing example env variables 2024-03-18 14:41:36 -03:00
jaison-x
4ab6c438cf fix(chatwoot): fix config name 2024-03-18 14:37:50 -03:00
Davidson Gomes
219e63c226 Merge pull request #475 from jaison-x/message-select
fix: add option to select specific fields on find message repository
2024-03-17 09:32:21 -03:00
Davidson Gomes
ee1e4623a3 Merge pull request #474 from jaison-x/edit-message
feat(chatwoot): show edited messages from WhatsApp in Chatwoot
2024-03-17 09:31:42 -03:00
Davidson Gomes
5982212903 Merge pull request #473 from jaison-x/pr
feat(chatwoot): read last message on WhatsApp when a message is sent from Chatwoot
2024-03-17 09:30:04 -03:00
jaison-x
32e6ecb12d fix: add option to select specific fields on find message repository 2024-03-15 14:09:07 -03:00
jaison-x
3b774558f4 feat(chatwoot): show edited messages from WhatsApp in Chatwoot 2024-03-15 14:01:21 -03:00
jaison-x
072171da18 feat(chatwoot): read last message on WhatsApp when a message is sent from Chatwoot 2024-03-15 13:46:04 -03:00
Davidson Gomes
8292221790 Merge tag '1.7.0' into develop
v
2024-03-11 18:24:11 -03:00
Davidson Gomes
901954de33 Merge branch 'release/1.7.0' 2024-03-11 18:24:06 -03:00
Davidson Gomes
286fe03500 v 1.7.0 2024-03-11 18:23:58 -03:00
Davidson Gomes
9c4f02634b test 2024-03-08 14:12:00 -03:00
Davidson Gomes
434f39de1f test 2024-03-08 13:52:12 -03:00
Davidson Gomes
e72b543f38 Merge branch 'develop' of github.com:EvolutionAPI/evolution-api into develop 2024-03-08 13:48:58 -03:00
Davidson Gomes
826e818802 test 2024-03-08 13:48:38 -03:00
Davidson Gomes
cbf846d32f Merge pull request #462 from lindenbergp/patch-1
Update variables  .env.example docker
2024-03-08 13:38:42 -03:00
Davidson Gomes
55811a39a5 ajuste connection 2024-03-08 13:35:27 -03:00
Davidson Gomes
a81f5953fc test 2024-03-08 12:58:23 -03:00
Davidson Gomes
bd09328ec2 test 2024-03-08 12:08:41 -03:00
Davidson Gomes
17ecc88f80 test 2024-03-08 12:02:13 -03:00
Davidson Gomes
7bc4c7b36e ajustes rabbitmq 2024-03-08 11:09:08 -03:00
Davidson Gomes
79189ac5d7 ajustes rabbitmq 2024-03-08 10:59:52 -03:00
Davidson Gomes
060c4e6e72 ajustes rabbitmq 2024-03-08 10:54:11 -03:00
Davidson Gomes
8f7c518487 ajustes rabbitmq 2024-03-08 10:46:43 -03:00
Davidson Gomes
12761cbfce ajustes rabbitmq 2024-03-08 10:10:40 -03:00
Davidson Gomes
2b30c273a4 ajustes rabbitmq 2024-03-08 09:47:04 -03:00
Davidson Gomes
03684a893d ajustes rabbitmq 2024-03-08 09:34:08 -03:00
Lindenberg Pinheiro
fef27111b9 Update variables .env.example docker
- Variáveis do Docker referente ao feat #395 
- Docker variables related to the feat #395
2024-03-07 20:50:50 -03:00
Davidson Gomes
b04ed66686 Merge pull request #451 from judsonjuniorr/feat/rabbit-per-events
RabbitMQ improvements
2024-03-07 20:00:04 -03:00
Davidson Gomes
f5df8bca20 Merge pull request #461 from judsonjuniorr/refactor/temp_delete_option
Refactor/temp delete option
2024-03-07 19:59:17 -03:00
Davidson Gomes
060af66c09 Merge pull request #460 from judsonjuniorr/fix/numbers-validation
Change the way numbers are validated on whatsapp
2024-03-07 19:59:05 -03:00
Judson Cairo
a6adbd61db Implemented an option to toggle temp instances deletion 2024-03-07 19:26:43 -03:00
Judson Cairo
bab58054f7 Change the way numbers are validated on whatsapp 2024-03-07 19:18:32 -03:00
Judson Cairo
e27e990cd6 Adapt to make compatible with current running services 2024-03-07 18:36:15 -03:00
Judson Cairo
196c2e0ed8 Removed logs & updated version 2024-03-07 18:28:56 -03:00
Davidson Gomes
499bd4328a Merge pull request #457 from gabrielporfiro/main
fix: incorrect images in project
2024-03-07 17:50:49 -03:00
Gabriel Porfiro
e389047aaf Update docker-compose.yaml 2024-03-06 18:52:01 -03:00
Gabriel Porfiro
da04ff284b Update docker-compose.yaml 2024-03-06 18:51:36 -03:00
Judson Cairo
10b48aed97 Update RABBITMQ_MODE to global in .env.example and Dockerfile 2024-03-04 20:27:34 -03:00
Judson Cairo
ac6e9ae994 Improvements in RabbitMQ, added 2 new modes 2024-03-04 20:24:15 -03:00
Judson Cairo
7dd589fb6c Make rabbitMQ per events 2024-03-04 12:16:26 -03:00
Davidson Gomes
6b930058d1 feat wa_business: send audio message 2024-03-02 12:03:11 -03:00
Davidson Gomes
aef3495a79 Merge branch 'develop' of github.com:EvolutionAPI/evolution-api into develop 2024-02-28 13:48:07 -03:00
Davidson Gomes
da341a95c6 fix: writing over 20s now loops until finished 2024-02-28 13:47:54 -03:00
Davidson Gomes
bca5ec6482 fix: writing over 20s now loops until finished 2024-02-28 13:47:32 -03:00
Davidson Gomes
f0a0cb7269 Merge pull request #434 from jaison-x/pr
feat(chatwoot): add some translations on chatwoot messages
2024-02-21 12:39:53 -03:00
jaison-x
238b7618b4 feat(chatwoot): add some translations on chatwoot messages
Add typescript to transpile .json files inside src folder.
2024-02-21 12:05:41 -03:00
Davidson Gomes
4f206f67c9 Merge pull request #433 from w3nder/develop
Add blockUser functionality
2024-02-20 18:26:46 -03:00
w3nder
fa513bf784 Update blockUserSchema in validate.schema.ts 2024-02-20 17:47:38 -03:00
w3nder
a68b0b3878 Add check number and ignore Group or Broadcast 2024-02-20 17:47:32 -03:00
w3nder
249489e697 Add blockUser functionality 2024-02-20 17:05:07 -03:00
Davidson Gomes
e2c67d7dae fix: integration 2024-02-19 16:04:48 -03:00
Davidson Gomes
703bc310a7 fix: create instance baileys 2024-02-18 11:57:44 -03:00
Davidson Gomes
4caecfa680 fix: adjusts in media 2024-02-18 10:40:51 -03:00
Davidson Gomes
fd4fde2543 fix: adjusts in media 2024-02-18 10:22:33 -03:00
Davidson Gomes
763c5de03f fix: adjusts in messageType 2024-02-18 09:57:14 -03:00
Davidson Gomes
4e160a46dd fix: adjusts in messageType 2024-02-18 09:41:23 -03:00
Davidson Gomes
56aaf18663 fix: adjusts in fetch instances 2024-02-18 09:22:54 -03:00
Davidson Gomes
df841aed27 fix: adjusts in fetch instances 2024-02-18 09:06:59 -03:00
Davidson Gomes
d4372a0332 fix: adjusts in messageType 2024-02-18 08:55:14 -03:00
Davidson Gomes
1595da789d fix: adjusts in messageType 2024-02-18 08:43:40 -03:00
Davidson Gomes
7e65cb1d19 fix: find intance by number 2024-02-18 08:11:24 -03:00
Davidson Gomes
a931ee7afb fix: logout wa business instance 2024-02-18 07:52:21 -03:00
Davidson Gomes
bc6944736e fix: expose integration in fetch instance 2024-02-18 07:44:42 -03:00
Davidson Gomes
16c2e28e9c fix: remve console.log 2024-02-18 07:27:24 -03:00
Davidson Gomes
ab89ef8db3 feat: whatsapp cloud api components in template 2024-02-17 18:41:35 -03:00
Davidson Gomes
9ef14d11f8 feat: whatsapp cloud api webhooks 2024-02-17 18:02:00 -03:00
Davidson Gomes
e753990da3 feat: whatsapp cloud api 2024-02-17 17:54:23 -03:00
Davidson Gomes
f469c2e65d feat: whatsapp cloud api 2024-02-17 17:43:25 -03:00
Davidson Gomes
0525501b87 feat: whatsapp cloud api 2024-02-17 17:42:49 -03:00
Davidson Gomes
3a37fd9d32 fix: typebot's wait now pauses the flow and composing is defined by the delay_message parameter in set typebot 2024-02-16 10:31:15 -03:00
Davidson Gomes
b095c9dfb9 fix: keyword_finish dont work 2024-02-16 10:15:31 -03:00
Davidson Gomes
cd00effcfe Merge pull request #421 from judsonjuniorr/Groups-participant-names
Refactor fetching participants for group in WhatsApp service
2024-02-16 08:29:21 -03:00
Davidson Gomes
1cd0334ccd Merge pull request #420 from judsonjuniorr/proxy-error-handler
Improve proxy error handling
2024-02-16 08:27:49 -03:00
Davidson Gomes
3b1a16844e fix: update create jid to BR numbers 2024-02-15 18:50:08 -03:00
Judson Cairo
6995e8a451 Refactor fetching participants for group in WhatsApp service 2024-02-14 16:25:38 -03:00
Judson Cairo
1dd0f319fc Fix error handling in testProxy method 2024-02-14 16:08:32 -03:00
Judson Cairo
5ce96369cf Refactor testProxy error handling and logging 2024-02-14 16:05:52 -03:00
Judson Cairo
0791d78e28 Improve proxy error handling 2024-02-14 15:27:34 -03:00
Davidson Gomes
cecbb7c34e Merge branch 'develop' of github.com:EvolutionAPI/evolution-api into develop 2024-02-13 08:33:08 -03:00
Davidson Gomes
525daff5fe Merge pull request #419 from judsonjuniorr/improvement/contacts-saving
Implement contact upsert in messaging-history.set
2024-02-12 19:59:21 -03:00
Judson Cairo
b2c51b4b8c Implement contact upsert in messaging-history.set 2024-02-12 11:46:51 -03:00
Davidson Gomes
ffddb05ba3 gitignore .vscode 2024-02-10 16:30:54 -03:00
Davidson Gomes
4bb81b9a41 Merge pull request #411 from judsonjuniorr/feat/labels
Changed label update to chat instead of contacts
2024-02-09 09:09:25 -03:00
Judson Cairo
3df4e8d2ba Changed label update to chat instead of contacts 2024-02-08 15:38:27 -03:00
Davidson Gomes
3edef873bc Merge pull request #409 from judsonjuniorr/feat/labels
Feat/labels
2024-02-08 14:49:01 -03:00
Davidson Gomes
c04b5cb851 Merge pull request #410 from jaison-x/clear-cache
feat(chatwoot): command to clearcache from chatwoot inbox
2024-02-08 14:47:12 -03:00
Davidson Gomes
fabd717d92 Merge pull request #408 from jaison-x/pr
fix(chatwoot): fix qrcode filename
2024-02-08 14:46:56 -03:00
jaison-x
945bcf5fad feat(chatwoot): command to clearcache from chatwoot inbox 2024-02-08 13:49:16 -03:00
jaison-x
d35d755379 feat(chatwoot): command to clearcache from chatwoot inbox 2024-02-08 13:34:50 -03:00
Judson Cairo
a2622cb38e Remove unused classes and imports from label dto 2024-02-08 13:33:53 -03:00
jaison-x
b58fd78450 fix(chatwoot): fix qrcode filename 2024-02-08 13:33:16 -03:00
Judson Cairo
e8d32066b4 Add 'add' field to the 'label' object in swagger.yaml and update event handling in whatsapp.service.ts 2024-02-08 13:31:56 -03:00
Judson Cairo
6d3779eb83 Merge branch 'develop' into feat/labels 2024-02-08 13:10:23 -03:00
Judson Cairo
c7bed04c80 Add labels property to ContactRaw model and update labels in LABELS_ASSOCIATION event 2024-02-08 13:05:06 -03:00
Davidson Gomes
b0e956cfa9 Merge pull request #406 from judsonjuniorr/contact-upsert-improvement
Extracted profile picture request from contacts.upsert function
2024-02-08 12:52:09 -03:00
Davidson Gomes
8608b7cded Merge pull request #404 from judsonjuniorr/instance-deletion
Temp instance deletion
2024-02-08 12:51:36 -03:00
Judson Cairo
23f1b4ac03 Implementation of Whatsapp labels management 2024-02-08 09:16:24 -03:00
Judson Cairo
27900c214f Extracted profile picture request from contacts.upsert function 2024-02-07 18:42:12 -03:00
Judson Cairo
704701e251 Add list method to AuthRepository and delete temp instances in WAMonitoringService 2024-02-07 16:34:26 -03:00
Judson Cairo
34c53d352a Refactor code and remove commented out sections 2024-02-07 15:16:37 -03:00
Davidson Gomes
32026d1fd4 Merge pull request #402 from judsonjuniorr/improv-name-validation
Improvement on whatsapp number validation
2024-02-07 10:37:33 -03:00
Judson Cairo
35cdce0d52 Check multiple numbers only once in the database 2024-02-07 08:53:47 -03:00
Judson Cairo
e59098cf61 Changed whatsapp number response to 200
Nothing is being created so the code 201 is not correct
2024-02-07 08:44:54 -03:00
Davidson Gomes
d8ca480b19 Merge pull request #395 from jaison-x/import-messages-chatwoot
feat(chatwoot): import history messages to chatwoot on whatsapp connection
2024-02-05 15:26:21 -03:00
Davidson Gomes
803b123ade Merge pull request #397 from deivisonrpg/fix/chatwoot-add-identifier-to-contact-filter
Fix(chatwoot): Include identifier to contact filter
2024-02-04 15:04:08 -03:00
Deivison Lincoln
cffb673fba Include identifier to contact filter in chatwoot 2024-02-02 18:20:24 -03:00
jaison-x
1f6535d61b fix: param for Object.entries cant be undefined 2024-02-02 16:46:29 -03:00
jaison-x
8a5ebe83a3 feat(chatwoot): import history messages to chatwoot on whatsapp connection
Messages are imported direct to chatwoot database. Media and group messages are ignored.

New env.yml variables:
CHATWOOT_IMPORT_DATABASE_CONNECTION_URI: URI to connect direct on chatwoot database.
CHATWOOT_IMPORT_PLACEHOLDER_MEDIA_MESSAGE: Indicates to use a text placeholder on media messages.

New instance setting:
sync_full_history: Indicates to request a full history sync to baileys.

New chatwoot options:
import_contacts: Indicates to import contacts.
import_messages: Indicates to import messages.
days_limit_import_messages: Number of days to limit history messages to import.
2024-02-02 15:32:34 -03:00
Davidson Gomes
cc17d61016 Merge pull request #394 from deivisonrpg/feat/chatwoot-reject-message-wpp-not-exists
Feat Chatwoot - Reject Message If is not a valid wpp number
2024-02-02 14:05:45 -03:00
Davidson Gomes
0547ff719c Merge pull request #393 from deivisonrpg/fix/chatwoot-validate-9-digit
Fix/chatwoot validate 9 digit
2024-02-02 14:05:02 -03:00
Deivison Lincoln
c130846fe8 Change message_type
Change message_type to 'outgoing'
2024-02-02 08:57:44 -03:00
Deivison Lincoln
b3adde3a7a Feat Reject Message If is not a valid wpp number 2024-02-01 23:42:52 -03:00
Deivison Lincoln
54603002a6 Fix for brazil 9 digit 2024-02-01 22:13:19 -03:00
Deivison Lincoln
b995cdfc32 Fix for contats find payload 2024-02-01 18:53:36 -03:00
Davidson Gomes
b09546577a Merge pull request #390 from leandrosroc/develop
feat: If contact stored with name, name added in return
2024-02-01 17:45:53 -03:00
Davidson Gomes
6e84c1dc4c Merge pull request #392 from jaison-x/hotfix
hotfix: bug on chatwoot sdk
2024-02-01 17:44:48 -03:00
jaison-x
f41f3aaba8 Update chatwoot.service.ts
hotfix: bug on chatwoot sdk
2024-02-01 17:31:50 -03:00
Leandro Santos Rocha
8fa61e4632 Merge branch 'EvolutionAPI:develop' into develop 2024-02-01 16:20:04 -03:00
Davidson Gomes
7dacd752d3 Merge pull request #382 from yvescleuder/fix-with-nine-number-brazil
Fix with nine number brazil
2024-01-31 11:51:45 -03:00
Leandro Santos Rocha
7439d2401d Fix error ; 2024-01-31 11:39:04 -03:00
Leandro Santos Rocha
dfb1ee0c56 Adjust to use the same jid 2024-01-31 11:37:19 -03:00
Leandro Santos Rocha
3da73b821d Removed console.log 2024-01-31 10:55:37 -03:00
Leandro Rocha
66b82ac10a If contact stored with name, name added in return 2024-01-31 10:50:04 -03:00
Davidson Gomes
058acc5042 changelog 2024-01-29 11:43:42 -03:00
Davidson Gomes
cdf822291f lint 2024-01-29 11:35:30 -03:00
Davidson Gomes
3f9e872e1c Merge pull request #381 from edisoncm-ti/develop
fix: collecting responses with text or numbers in Typebot
2024-01-29 11:30:22 -03:00
Davidson Gomes
94aa3067f6 Merge pull request #380 from drauber/qr_translation
Add translate capabilities to QRMessages in CW
2024-01-29 11:30:00 -03:00
Davidson Gomes
bff8064597 Merge pull request #379 from judsonjuniorr/sendList-swagger-docs
Added sendList endpoint to swagger documentation
2024-01-29 11:29:15 -03:00
Davidson Gomes
6b2d4e2585 Merge pull request #378 from moraisamilton/develop
Implemented a function to synchronize message deletions on WhatsApp, automatically reflecting in Chatwoot.
2024-01-29 11:28:59 -03:00
Davidson Gomes
56dfc2ae82 Merge pull request #377 from judsonjuniorr/validate-numbers
Improvement on numbers validation
2024-01-29 11:27:48 -03:00
Yves Clêuder Lima de Jesus
535d5ee47f fix: change method search for filter 2024-01-28 10:36:42 -03:00
Yves Clêuder Lima de Jesus
838905f3dd fix: position splice 2024-01-27 22:37:58 -03:00
Yves Clêuder Lima de Jesus
59f5208c5c fix: search number without 9 in number from brazil 2024-01-27 22:29:04 -03:00
Edison Martins
32a1a715ea Merge branch 'EvolutionAPI:develop' into develop 2024-01-26 19:11:34 -03:00
edisoncm-ti
3755d3870e fix: collecting responses with text or numbers in Typebot 2024-01-26 19:10:20 -03:00
Douglas Rauber at Nitro
0edc8a9284 Add translate capabilities to QRMessages in CW 2024-01-25 16:19:08 -03:00
Amilton Morais
5449d63602 Merge branch 'develop' of https://github.com/moraisamilton/evolution-api into develop 2024-01-24 13:46:02 -03:00
Amilton Morais
dfa72fd6af Update start.sh 2024-01-24 13:43:19 -03:00
Amilton Morais
0e50da324b Implemented a function to synchronize message deletions on WhatsApp, automatically reflecting in Chatwoot
A new variable has been created, and a function has been implemented to manage the deletion of messages on WhatsApp. Now, when deleting a message for everyone on WhatsApp, the same action will be automatically performed on Chatwoot, ensuring consistency across platforms
2024-01-24 13:38:29 -03:00
Judson Junior
679f8118f6 Added sendList endpoint to swagger documentation 2024-01-23 23:59:12 -03:00
Amilton Morais
804d177782 Udate "@whiskeysockets/baileys": "6.6.0" and models
The message.model.ts class was updated to version 6.6.0 of baileys. 'unknown','desktop' was included to avoid errors using version 6.6.0 of baileys
2024-01-23 19:34:36 -03:00
Judson Junior
eb96c9fece Added new validation on the 9 digit
This is to prevent returning another jid when only one digit is different and not the 9 digit
2024-01-23 18:37:31 -03:00
Judson Junior
ed6c50621c Improved the method of numbers validation 2024-01-23 18:16:15 -03:00
Davidson Gomes
7f74de07ed Merge pull request #375 from judsonjuniorr/poll-sending
Fix polls in message sending
2024-01-22 13:44:49 -03:00
Judson Junior
9e5bf93580 Fix polls in message sending 2024-01-22 13:39:11 -03:00
Davidson Gomes
9d685da12d Merge pull request #374 from judsonjuniorr/proxy-improvements
Proxy improvements
2024-01-21 18:16:15 -03:00
Davidson Gomes
6bb40eb502 Merge pull request #373 from leandrosroc/develop
Join in Group by Invite Code
2024-01-21 18:14:40 -03:00
Davidson Gomes
1ceee572cf Merge pull request #372 from edisoncm-ti/develop
Typebot: Remove quebra de linha no index final do array | Obtem a resposta de um sendList (Lista)
2024-01-21 18:13:26 -03:00
Davidson Gomes
391ffea4ab Merge pull request #371 from judsonjuniorr/develop
Add number parameter to OnWhatsAppDto constructor
2024-01-21 18:12:31 -03:00
Judson Junior
5292e569d9 Remove unused dependencies and refactor proxy handling
In some tests made on the testProxy function, the `new ProxyAgent()` was not working, even adding it the IP result was the same.

I've change it to the `new HttpsProxyAgent()` and it worked.

Refactored the proxy agent to center the same function/creation the same where it is used
2024-01-21 13:54:42 -03:00
Judson Junior
82e111f1be Fix proxy handling in WAStartupService preventing instance to be created 2024-01-21 13:01:35 -03:00
Judson Junior
1e9b3a1e42 Update proxy controller to use a different IP lookup service 2024-01-21 13:01:06 -03:00
Judson Junior
35520d85a2 Add https-proxy-agent dependency and handle NotFoundException in ProxyController 2024-01-21 13:00:23 -03:00
Leandro Rocha
e19e37eef4 Join in Group by Invite Code 2024-01-21 02:13:24 -03:00
edisoncm-ti
814795f566 Merge branch 'develop' of https://github.com/edisoncm-ti/evolution-api into develop 2024-01-20 18:42:40 -03:00
edisoncm-ti
1a633dcf10 feat: obter a resposta de um sendList (Lista) e prosseguir o fluxo Typebot 2024-01-20 18:31:26 -03:00
edisoncm-ti
c9b0e66641 feat: Obter a resposta de um sendList (Lista) e prosseguir o fluxo Typebot 2024-01-20 18:28:00 -03:00
Judson Junior
440ff2f3ea Add number parameter to OnWhatsAppDto constructor 2024-01-20 11:44:10 -03:00
edisoncm-ti
96be63f50b fix: remove quebra de linha no index final do array quando usado variavel no Typebot 2024-01-20 11:09:05 -03:00
Davidson Gomes
69a323691f Merge pull request #370 from leandrosroc/main
fix: Sending status message
2024-01-19 16:38:59 -03:00
Leandro Santos Rocha
4357fcf7ef Adjusted so that when it is "list", it sends correctly 2024-01-18 21:30:49 -03:00
Leandro Rocha
5f79513617 fix: Sending status message 2024-01-18 20:25:58 -03:00
Davidson Gomes
ba3ccbbc9a Merge pull request #369 from jaison-x/pr
fix: message 'connection successfully' spamming
2024-01-18 11:55:07 -03:00
jaison-x
f182436673 fix: message 'connection successfully' spamming 2024-01-17 19:56:26 -03:00
Davidson Gomes
793b907fe6 Merge pull request #363 from jaison-x/read_messages_chatwoot
feat(chatwoot): read messages from whatsapp in chatwoot
2024-01-16 09:39:17 -03:00
jaison-x
c75dfcd499 feat(chatwoot): read messages from whatsapp in chatwoot
It works only with messages read from whatsapp to chatwoot.
2024-01-15 20:40:29 -03:00
Davidson Gomes
29a48f7914 Merge pull request #360 from jaison-x/cache
feat(cacheservice): add support to use use redis in cacheservice
2024-01-15 10:21:35 -03:00
jaison-x
095a8aa9dd fix: process.env can be null 2024-01-12 16:15:44 -03:00
jaison-x
2d8b5f04e9 feat(cacheservice): add suport to use use redis in cacheservice 2024-01-12 16:04:55 -03:00
Davidson Gomes
ba9f97bc3e Merge pull request #355 from AlanMartines/main
Fix: Corrects the generation of the hash variable name
2024-01-11 12:57:17 -03:00
Davidson Gomes
ed51f44ee8 Merge pull request #351 from jaison-x/cache-chatwoot
fix(chatwoot): invalidate the conversation cache if reopen_conversation is false and the conversation was resolved
2024-01-11 12:56:37 -03:00
Alan Martines
933d787108 Fix: Corrects the generation of the hash variable name, error when generating with white spaces and special characters. 2024-01-10 09:27:25 -04:00
jaison-x
0bc12733a3 fix(chatwoot): invalidate the conversation cache if reopen_conversation is false and the conversation was resolved 2024-01-08 22:20:16 -03:00
Davidson Gomes
8cfd9c44ab Merge branch 'develop' of github.com:EvolutionAPI/evolution-api into develop 2024-01-07 19:11:07 -03:00
Davidson Gomes
3ddff84be0 test: docker with platform arm 2024-01-07 19:10:56 -03:00
Davidson Gomes
afb5361a1b Merge pull request #342 from jaison-x/cache-chatwoot
perf(chatwoot): create cache for the most used/expensive functions in chatwoot
2024-01-04 17:05:39 -03:00
jaison-x
f376047632 perf(chatwoot): create cache for the most used/expensive functions in chatwoot 2024-01-03 18:42:54 -03:00
Davidson Gomes
7373eea842 feat: Added update message endpoint 2024-01-03 10:18:20 -03:00
Davidson Gomes
a446df4620 fix: Adjusts the quoted message, now has contextInfo in the message Raw 2024-01-02 14:04:54 -03:00
Davidson Gomes
306c6dd21d fix: Adjusts the quoted message, now has contextInfo in the message Raw 2024-01-02 13:49:31 -03:00
Davidson Gomes
380ba8860c Merge branch 'develop' of github.com:EvolutionAPI/evolution-api into develop 2024-01-02 12:03:22 -03:00
Davidson Gomes
6c8c8ddcfc version: baileys 6.5.0 2024-01-02 12:03:11 -03:00
Davidson Gomes
0c09c5e140 Merge pull request #338 from jaison-x/pr
fix(chatwoot): fix looping when deleting a message in chatwoot
2024-01-01 21:47:35 -03:00
jaison-x
b761fba8cb fix(chatwoot): fix looping when deleting a message in chatwoot 2024-01-01 18:11:25 -03:00
Davidson Gomes
dfceda3942 version: 1.6.2 2023-12-29 19:15:51 -03:00
Davidson Gomes
85d1825236 version: 1.6.2 2023-12-29 19:12:29 -03:00
Davidson Gomes
8f8c7e26c7 Merge branch 'develop' of github.com:EvolutionAPI/evolution-api into develop 2023-12-29 19:11:28 -03:00
Davidson Gomes
181768d91f Merge pull request #336 from jaison-x/adjust-filename-chatwoot
perf(chatwoot): only use a axios request to get file mimetype if necessary
2023-12-29 19:11:22 -03:00
Davidson Gomes
2dcd4d8fd3 fix: Correction in chatwoot text formatting and render list message 2023-12-29 19:10:59 -03:00
jaison-x
d909550134 fix(chatwoot): add file extension to var fileName 2023-12-29 18:42:07 -03:00
jaison-x
c564ec41e2 perf(chatwoot): Only use a axios request to get mimetype file if necessary 2023-12-29 18:15:05 -03:00
Davidson Gomes
af94a0e174 Merge pull request #334 from jaison-x/adjust-filename-chatwoot
fix(chatwoot): when possible use the original file extension
2023-12-29 17:10:43 -03:00
jaison-x
fcd8815fca fix(chatwoot): when possible use the original file extension
In some cases mimeTypes.extension() return false to csv and other file types
2023-12-29 14:52:16 -03:00
Davidson Gomes
5bd3f28117 Merge pull request #332 from jaison-x/adjust-filename-chatwoot
fix(chatwoot): when receiving a file from whatsapp, use the original filename in chatwoot if possible
2023-12-29 10:36:17 -03:00
Davidson Gomes
384bde333e Merge pull request #331 from jaison-x/pr
refactor(chatwoot): remove message ids cache in chatwoot to use chatwoot's api itself
2023-12-29 10:36:01 -03:00
Davidson Gomes
d06ec604b9 version: 1.6.2 2023-12-29 10:34:23 -03:00
Davidson Gomes
d8ce23f2a0 fix: exchange rabbitmq 2023-12-29 09:46:59 -03:00
Davidson Gomes
3ccb983377 fix: chatwoot service 2023-12-28 18:24:29 -03:00
Davidson Gomes
19fb9fcd31 fix: webhook 2023-12-28 18:20:38 -03:00
Davidson Gomes
5f4a1b96ce fix: correction in typebot text formatting 2023-12-28 18:08:30 -03:00
Davidson Gomes
6983f385fc fix: correction in typebot text formatting 2023-12-28 18:07:24 -03:00
Davidson Gomes
2aadd1cac5 fix: Adjust in webhook_base64 2023-12-28 17:32:19 -03:00
jaison-x
bfa7d429bd refactor(chatwoot): remove message ids cache in chatwoot to use chatwoot's api itself.
Remove use of disc cache to optimize performance.

BREAKING CHANGE: to make this, we need to use the param `source_id` from message in chatwoot.
This param is only available from api in chatwoot version => 3.4.0.
2023-12-28 14:43:50 -03:00
Davidson Gomes
3238150b92 fix: Proxy configuration improvements 2023-12-28 10:36:23 -03:00
Davidson Gomes
3603571967 Merge branch 'develop' of github.com:EvolutionAPI/evolution-api into develop 2023-12-28 10:36:01 -03:00
Davidson Gomes
7c2a8c0abb fix: Proxy configuration improvements 2023-12-28 10:35:41 -03:00
jaison-x
8b5f73badd fix: added support to use source_id to check chatwoot's webhook needs to be ignored.
With this, messageCache is used to support Chatwoot version <= 3.3.1.

After this version we can remove use of message cache and use only the source_id field to check chatwoot's webhook needs to be ignored. It's have much better performance.
2023-12-27 13:56:32 -03:00
Davidson Gomes
ff57fd3d23 Merge pull request #322 from w3nder/develop
fix: Correção na Função sendList
2023-12-27 10:09:14 -03:00
jaison-x
49aa1ea17c fix: when receiving a file from whatsapp, use the original filename in chatwoot when possible 2023-12-26 01:21:05 -03:00
jaison-x
7430897085 fix: when receiving a file from whatsapp, use the original filename in chatwoot when possible 2023-12-25 23:58:07 -03:00
jaison-x
82894a1c4f refactor: change the message ids cache in chatwoot to use a in memory cache
Remove use of disc cache for optimize performance.
To make this, we need change to use only one instance of ChatwootService in entire application.
2023-12-25 22:29:03 -03:00
w3nder
3bb4217a45 fix: Correção na Função sendList
Ao disparar uma lista de mensagens, agora enviamos elas com o tipo 'PRODUCT_LIST' e realizamos a correção necessária na função patchMessageBeforeSending para o tipo 'SINGLE_SELECT'
2023-12-24 14:32:05 -03:00
Davidson Gomes
dfc8330035 Merge tag '1.6.1' into develop
* 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
2023-12-22 11:44:21 -03:00
Davidson Gomes
45e03d87c7 Merge branch 'release/1.6.1' 2023-12-22 11:44:12 -03:00
Davidson Gomes
3e358e5d26 version: 1.6.1 2023-12-22 11:43:58 -03:00
Davidson Gomes
dc5dae04eb fix: when deleting a message in whatsapp, delete the message in chatwoot too 2023-12-22 11:43:10 -03:00
Davidson Gomes
9128b1f47d fix: when deleting a message in whatsapp, delete the message in chatwoot too 2023-12-22 11:43:03 -03:00
Davidson Gomes
16a8226ba7 Merge pull request #317 from jaison-x/pr
fix: when deleting a message in whatsapp, delete the message in chatwoot too
2023-12-21 16:37:01 -03:00
jaison-x
9ecaf3199d Merge branch 'pr' of https://github.com/jaison-x/evolution-api into pr 2023-12-21 07:42:37 -03:00
jaison-x
a44cd0373e fix: simple adjust in key object 2023-12-21 07:42:33 -03:00
jaison-x
2c0b629302 Update whatsapp.service.ts 2023-12-21 00:23:33 -03:00
jaison-x
89a37a1771 fix: show message Connection successfully established after a sucessfull connection on chatwoot 2023-12-20 18:11:41 -03:00
jaison-x
cadc038966 Merge remote-tracking branch 'upstream/develop' into pr 2023-12-20 17:54:52 -03:00
jaison-x
07e8449379 fix: when deleting a message in whatsapp, delete the message in chatwoot too
The message model schema was changed. Old format in message model was field chatwootMessageId. Now we have a document chatwoot with new properties.

I cant find a simple way to create a migration function up then the old field was no migrate to new format.
2023-12-20 17:53:37 -03:00
Davidson Gomes
71e908ad1a Merge pull request #312 from stgcompany/develop
Exclude all .env files on .gitignore and renamed one to .env.example
2023-12-20 12:22:03 -03:00
Davidson Gomes
035c85b775 Merge pull request #313 from gabrielpastori1/issue-template
Add issue templates
2023-12-20 12:21:06 -03:00
Gabriel Pastori
a87b753151 Update issue templates 2023-12-20 12:13:24 -03:00
Eduardo Chaves
edaa4aff7e Add any .env file to .gitignore 2023-12-20 10:31:14 -03:00
Eduardo Chaves
be02610349 Renamed .env to .env.example 2023-12-20 10:29:32 -03:00
Davidson Gomes
1f65731165 fix: Add options to disable docs and manager 2023-12-20 10:03:44 -03:00
Davidson Gomes
fb61fb7849 Merge pull request #310 from gabrielpastori1/disable-docs-manager
Add options to disable docs and manager
2023-12-20 10:02:46 -03:00
Davidson Gomes
060a945aea fix: Fix the problem when disconnecting the instance and connecting again using mongodb 2023-12-20 09:56:53 -03:00
Davidson Gomes
2797250f34 Merge branch 'develop' of github.com:EvolutionAPI/evolution-api into develop 2023-12-20 09:56:32 -03:00
Davidson Gomes
97b3f9b3c7 fix: Fix the problem when disconnecting the instance and connecting again using mongodb 2023-12-20 09:55:51 -03:00
Gabriel Pastori
9363301d2a Add DISABLE_DOCS and DISABLE_MANAGER 2023-12-19 21:18:40 -03:00
Davidson Gomes
d93a826e28 Merge pull request #309 from jaison-x/pr
fix: message can be undefined
2023-12-19 18:59:59 -03:00
jaison-x
244fe0835e Update chatwoot.service.ts
fix: message can be undefined
2023-12-19 18:49:31 -03:00
Davidson Gomes
5f1a5d6589 Merge pull request #308 from PurpShell/fix/message-retry
Fix: message retry mechanism
2023-12-19 17:34:39 -03:00
Rajeh Taher
4e27c22292 fix: message retry 2023-12-19 22:28:50 +02:00
Davidson Gomes
53ee270096 Merge pull request #307 from gabrielpastori1/chatwoot-format
Chatwoot Agent Name
2023-12-19 17:15:32 -03:00
Gabriel Pastori
a00ad20c08 chatwoot agent available_name 2023-12-19 16:41:52 -03:00
Davidson Gomes
e8764dd1c6 fix: Fix the problem when disconnecting the instance and connecting again using mongodb 2023-12-19 14:55:16 -03:00
Davidson Gomes
a869d38499 Merge branch 'develop' of github.com:EvolutionAPI/evolution-api into develop 2023-12-19 14:27:47 -03:00
Davidson Gomes
7bf7c96587 Merge pull request #301 from drauber/addLogInfo
Add connected number and instance name to connected log
2023-12-19 14:27:41 -03:00
Davidson Gomes
72b857a92f Merge pull request #299 from gabrielpastori1/chatwoot-format
Chatwoot format
2023-12-19 14:27:05 -03:00
Douglas Rauber at Nitro
380d6a43a5 Add connected number and instance name to connected log 2023-12-19 08:02:29 -03:00
Gabriel Pastori
076f2b492e fix: chatwoot media process 2023-12-18 21:23:56 -03:00
Davidson Gomes
547f981c47 fix: adjusts in typebot 2023-12-18 16:46:04 -03:00
Davidson Gomes
8bfc62a3b2 fix: adjusts in typebot 2023-12-18 16:27:37 -03:00
Davidson Gomes
d39776a314 fix: fixed the pairing code 2023-12-18 15:27:22 -03:00
Davidson Gomes
35641d0543 fix: fixed the pairing code 2023-12-18 15:24:19 -03:00
Davidson Gomes
038cd6f149 Merge pull request #293 from gabrielpastori1/chatwoot-format
Chatwoot format
2023-12-18 12:15:00 -03:00
Davidson Gomes
38978dd447 test: adjusts baileys lids 2023-12-18 12:07:02 -03:00
Gabriel Pastori
fb24b7eaa7 Merge branch 'develop' into chatwoot-format 2023-12-17 13:30:54 -03:00
Gabriel Pastori
b4ce45bc4b Add new env and fix message formatting in Chatwoot to changelog 2023-12-17 13:25:03 -03:00
Gabriel Pastori
8d04198309 fix: monospace and bold 2023-12-17 13:24:43 -03:00
Davidson Gomes
da796347c4 fix: include instance Id field in the instance configuration 2023-12-17 09:37:18 -03:00
Davidson Gomes
2d6a29664a fix: include instance Id field in the instance configuration 2023-12-17 07:59:24 -03:00
Davidson Gomes
4ba5cfceaf fix: include instance Id field in the instance configuration 2023-12-17 07:50:17 -03:00
Davidson Gomes
7cc324e1c0 fix: include instance Id field in the instance configuration 2023-12-17 07:04:33 -03:00
Davidson Gomes
cf89601269 fix: include instance Id field in the instance configuration 2023-12-17 06:59:05 -03:00
Davidson Gomes
c07e23bf8d Merge pull request #290 from gabrielpastori1/chatwoot-format
Chatwoot format
2023-12-17 06:32:37 -03:00
Gabriel Pastori
5aa89d85f3 fix: remove comments 2023-12-16 18:39:46 -03:00
Gabriel Pastori
4ed1edf53d chatwoot_sign_delimiter 2023-12-16 17:23:39 -03:00
Gabriel Pastori
1be1326b52 Refactor message formatting in ChatwootService (Bold, italic, etc) 2023-12-16 15:58:01 -03:00
Davidson Gomes
42ae7d1568 Merge pull request #286 from gomessguii/feature/dockerfile-optimization
Dockerfile modified to use multi-stage build
2023-12-15 14:51:49 -03:00
Guilherme Oliveira
b781c83545 Dockerfile modified to use multi-stage build 2023-12-15 14:46:31 -03:00
Davidson Gomes
e48cea18e7 Merge pull request #283 from gabrielpastori1/typeboot-keep-open
simple add keep open
2023-12-15 13:53:06 -03:00
Gabriel Pastori
ff82987144 simple add keep open 2023-12-15 12:39:07 -03:00
Davidson Gomes
f612a45550 fix: correction sending s3/minio media to chatwoot and typebot 2023-12-14 17:20:37 -03:00
Davidson Gomes
182dce4840 fix: correction sending s3/minio media to chatwoot and typebot 2023-12-14 16:42:03 -03:00
Davidson Gomes
4e41e072d6 fix: correction sending s3/minio media to chatwoot and typebot 2023-12-14 16:13:15 -03:00
Davidson Gomes
a369c16db8 fix: correction sending s3/minio media to chatwoot and typebot 2023-12-14 15:59:06 -03:00
Davidson Gomes
1fc820787a fix: correction sending s3/minio media to chatwoot and typebot 2023-12-14 15:57:34 -03:00
Davidson Gomes
d3a83ba89e fix: correction sending s3/minio media to chatwoot and typebot 2023-12-14 15:35:17 -03:00
Davidson Gomes
20fb66e2f7 fix: correction sending s3/minio media to chatwoot and typebot 2023-12-14 11:50:05 -03:00
Davidson Gomes
a44646161b fix: variables in typebot 2023-12-14 08:35:45 -03:00
Davidson Gomes
87027ea2d0 update: manager 2023-12-12 18:16:02 -03:00
Davidson Gomes
c9757cbb4b fix: fixed lids messages 2023-12-12 17:45:12 -03:00
Davidson Gomes
9a8f4aefe0 Merge tag '1.6.0' into develop
* 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

* 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 *
* Adjusts in proxy
* 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

- Chatwoot: v3.3.1
- Typebot: v2.20.0
2023-12-12 17:44:42 -03:00
Davidson Gomes
3545b80050 Merge branch 'release/1.6.0' 2023-12-12 17:44:24 -03:00
Davidson Gomes
379855714e version: 1.6.0 2023-12-12 17:44:00 -03:00
Davidson Gomes
ff06cd7643 feat: Added support for new typebot API 2023-12-12 16:55:46 -03:00
Davidson Gomes
ade3952016 fix: Fixed issue sending contact to Chatwoot via iOS 2023-12-12 16:28:01 -03:00
Davidson Gomes
f246516a6e fix: fixed issue where CSAT opened a new ticket when reopen_conversation was disabled 2023-12-12 16:08:09 -03:00
Davidson Gomes
c296bf4178 fix: Fixed problem that did not generate qrcode with the chatwoot_conversation_pending option enabled 2023-12-12 15:46:10 -03:00
Davidson Gomes
1568554a1c feat: added reply, delete and message reaction in chatwoot v3.3.1 2023-12-12 15:24:54 -03:00
Davidson Gomes
ae66be197e feat: added reply, delete and message reaction in chatwoot v3.3.1 2023-12-12 15:16:09 -03:00
Davidson Gomes
3e904aa160 fix: correction of chatwoot functioning with admin flows 2023-12-12 13:56:47 -03:00
Davidson Gomes
64c1440c46 fix: correction of chatwoot functioning with admin flows 2023-12-12 13:56:41 -03:00
Davidson Gomes
f069a41390 fix: added restart instance when update profile pricture 2023-12-11 18:17:18 -03:00
Davidson Gomes
d4a33e2290 fix: added restart instance when update profile pricture 2023-12-11 18:16:11 -03:00
Davidson Gomes
7ee5bcecff fix: added restart instance when update profile pricture 2023-12-11 18:15:24 -03:00
Davidson Gomes
48f6ee8846 feat: added auto_create to the chatwoot set to create the inbox automatically or not 2023-12-11 17:32:19 -03:00
Davidson Gomes
7a24f52782 feat: added auto_create to the chatwoot set to create the inbox automatically or not 2023-12-11 17:32:07 -03:00
Davidson Gomes
324d46120b feat: new manager 2023-12-11 15:31:43 -03:00
Davidson Gomes
87baec5ff8 Merge pull request #250 from gabrielpastori1/manager
Add Manager
2023-12-11 15:27:43 -03:00
Gabriel Pastori
b2e144f35c add manager 2023-12-11 15:06:42 -03:00
Davidson Gomes
87a8e25662 Merge pull request #249 from gabrielpastori1/typebot-auto-create-session
fix: only create if is paused
2023-12-11 12:57:51 -03:00
Gabriel Pastori
8e88f00fb2 fix: only create if is paused 2023-12-11 12:42:09 -03:00
Davidson Gomes
d14505d59a Merge pull request #248 from gabrielpastori1/fix-chatwoot-find
Fix chatwoot find
2023-12-11 12:32:19 -03:00
Davidson Gomes
1e6d4347fa Merge branch 'develop' of github.com:EvolutionAPI/evolution-api into develop 2023-12-11 12:09:08 -03:00
Davidson Gomes
b8d9a8c072 fix: adjusted return from queries in mongodb 2023-12-11 12:08:58 -03:00
Gabriel Pastori
fd15ae5e8c Fix condition to check for empty result object 2023-12-11 12:00:33 -03:00
Davidson Gomes
fb6377414b Merge pull request #237 from gabrielpastori1/add-send-presence
Add sendPresence
2023-12-11 10:47:26 -03:00
Davidson Gomes
9a5dbe055e fix: adjusts in connection 2023-12-08 18:44:52 -03:00
Gabriel Pastori
42dd280aca change sendPresence from sendMessage to chat 2023-12-08 18:40:48 -03:00
Davidson Gomes
41b2946cdc Merge branch 'develop' of github.com:EvolutionAPI/evolution-api into develop 2023-12-06 15:22:08 -03:00
Davidson Gomes
a90f0f2c59 Fixes in mongodb and chatwoot 2023-12-06 15:21:50 -03:00
Gabriel Pastori
4222c0e53b Fix logger message in sendPresence route 2023-12-06 00:32:27 -03:00
Gabriel Pastori
ee0f0f0be0 add send presence router 2023-12-06 00:31:35 -03:00
Davidson Gomes
f8d874453c Merge pull request #233 from gabrielpastori1/typebot-auto-create-session
Add session creation for typebot service
2023-12-05 18:49:31 -03:00
Gabriel Pastori
aa891489f0 Add session creation for typebot service 2023-12-03 21:00:26 -03:00
Davidson Gomes
4c69b059d4 proxy 2023-12-01 21:24:38 -03:00
Davidson Gomes
359bd9f762 fix: adjusts in proxy 2023-12-01 20:09:22 -03:00
Davidson Gomes
2de0b61726 fix: adjusts in proxy 2023-12-01 20:09:19 -03:00
Davidson Gomes
d75163aa57 test: adjusts wasocket 2023-12-01 15:04:57 -03:00
Davidson Gomes
e49f30641e test: adjusts wasocket 2023-12-01 14:30:20 -03:00
Davidson Gomes
1631c2c342 test: adjusts wasocket 2023-12-01 09:42:56 -03:00
Davidson Gomes
cf7de369b2 test: adjusts wasocket 2023-12-01 08:22:40 -03:00
Davidson Gomes
78c03d8f2f test: adjusts wasocket 2023-12-01 08:21:36 -03:00
Davidson Gomes
876320b849 feat: added compatibility with typebot v2 2023-11-30 17:13:05 -03:00
Davidson Gomes
a1d13f8ff3 changelog 2023-11-30 07:12:14 -03:00
Davidson Gomes
3c19bdfaa9 fix: Removed await from webhook when sending a message 2023-11-30 07:09:11 -03:00
Davidson Gomes
4fa895086e fix: adjusts in validation to messages.upsert 2023-11-30 07:02:25 -03:00
Davidson Gomes
9945d8debb Merge branch 'develop' of github.com:EvolutionAPI/evolution-api into develop 2023-11-30 07:01:48 -03:00
Davidson Gomes
4362de2198 fix: adjusts in validation to messages.upsert 2023-11-30 07:01:26 -03:00
Davidson Gomes
a5c5879e4f fix: adjusts in validation to messages.upsert 2023-11-30 07:01:08 -03:00
Davidson Gomes
1a57f4f33d Merge pull request #215 from suissa/patch-3
Update typebot.service.ts - element.underline change ~ for *
2023-11-29 08:47:52 -03:00
Davidson Gomes
a99e173168 Merge pull request #224 from jaison-x/pr
fix: workaround to manage param data as an array in mongodb
2023-11-29 08:46:12 -03:00
Davidson Gomes
e02a28f61e Merge pull request #228 from raimartinsb/develop
fix messages not received: error handling when updating contact
2023-11-29 08:45:24 -03:00
raimartinsb
26d3ff97ce fix messages not received: error handling when updating contact 2023-11-28 11:10:29 -03:00
Davidson Gomes
57fb3c9785 fix: fixed lids messages 2023-11-27 19:58:23 -03:00
Davidson Gomes
edeb970a82 fix: fixed lids messages 2023-11-27 19:57:59 -03:00
jaison-x
e17baddf01 fix: workaround to manage param data as an array in mongodb
When saving data from a group session (sender-key-xxxxx@g.us::xxxxx::xx) we receive the data param as an array.
In mongodb we can't save an array as a root document. Bacause this, in this case we save the array in a property called content_array.
2023-11-24 18:59:19 -03:00
Davidson Gomes
a277d36696 feat: sqs 2023-11-20 17:53:29 -03:00
Davidson Gomes
6c9e86e17a feat: sqs 2023-11-20 17:52:36 -03:00
Davidson Gomes
e75ef21eb6 Merge pull request #216 from craines/main
fix: Removed await from webhook when sending a message
2023-11-18 16:38:32 -03:00
craines
04e5443b82 fix: send reaction 2023-11-15 21:52:56 -03:00
craines
8b4cdf3b9b fix: Removed await from webhook when sending a message 2023-11-14 16:47:25 -03:00
Jean Carlo Nascimento
37f1620f7c Update typebot.service.ts - element.underline change ~ for *
There's no underline in WhatsApp ~ use strikethrough instead, I switched to * bold.
2023-11-12 19:11:47 -03:00
Davidson Gomes
b0a0e805cf Merge pull request #187 from jaison-x/deleting-instances
Deleting instances
2023-11-09 14:41:43 -03:00
Davidson Gomes
c619e253a2 Merge pull request #198 from gabrielpastori1/fix-typebot-error-handle
Handle erros in Typebot
2023-11-09 14:32:30 -03:00
Davidson Gomes
2bd111f1e2 Merge pull request #197 from vitorogen/develop
Handle optional chaining for 'settings.msg_call', this change prevent…
2023-11-05 19:44:27 -03:00
Davidson Gomes
40174b50eb Merge pull request #190 from w3nder/develop
fix: size of group participants
2023-11-05 19:43:45 -03:00
Davidson Gomes
e0fe28717f Merge pull request #186 from jaison-x/adjust-mongo-deletion
-> Adjusting function cleaningStoreFiles to remove itens from missing…
2023-11-05 19:42:24 -03:00
Gabriel Pastori
ac5fc22043 try catch entire sendTypebot 2023-10-30 18:05:18 -03:00
Gabriel Pastori
e30f196dad remove duplicate 2023-10-29 13:22:41 -03:00
Gabriel Pastori
9e4e1ce8ec Merge branch 'fix-typebot-error-handle' of https://github.com/gabrielpastori1/ticketme-evolution-api into fix-typebot-error-handle 2023-10-29 13:01:37 -03:00
Gabriel Pastori
f710898844 fix: in error catch in createNewSession 2023-10-29 12:59:29 -03:00
Gabriel Pastori
94633484ca Merge branch 'develop' into fix-typebot-error-handle 2023-10-29 12:54:05 -03:00
Gabriel Pastori
0817c2589f expand try catch for all function 2023-10-29 12:49:26 -03:00
Gabriel Pastori
8a99386b33 handle erros 2023-10-28 21:21:12 -03:00
Vitor
52d6a563d6 Handle optional chaining for 'settings.msg_call', this change prevents 'TypeError: Cannot read properties of undefined' errors when 'msg_call' is undefined. 2023-10-27 22:49:57 -03:00
Davidson Gomes
d0a5ae1da4 fix: Removed senderKeyDistributionMessage 2023-10-26 18:01:17 -03:00
Davidson Gomes
783c00a1d9 fix: Added mimetype field when sending media 2023-10-25 10:29:24 -03:00
Davidson Gomes
bc70ec8b07 fix: start session 2023-10-25 08:19:44 -03:00
Davidson Gomes
50e1efe5d7 fix: start session 2023-10-25 07:00:58 -03:00
Davidson Gomes
d8629e53f1 fix: start session 2023-10-24 13:16:35 -03:00
Davidson Gomes
cc9df1dabb fix: start session 2023-10-24 12:06:20 -03:00
Davidson Gomes
cd6cb8182e fix: start session 2023-10-24 12:05:58 -03:00
Wender Teixeira
5b0e90e5b9 Update whatsapp.service.ts 2023-10-19 22:41:16 -03:00
jaison-x
3c3bbc84b3 -> When deleting a instance in connecting state, the event remove.instance was not triggered; 2023-10-18 19:52:33 -03:00
Jaison
23615cff4f -> Adjusting function cleaningStoreFiles to remove itens from missing mongo collections;
-> AuthModel was using a wrong filter;
2023-10-18 18:57:11 -03:00
Davidson Gomes
daadc6cb68 fix: adjusts in proxy 2023-10-18 18:09:26 -03:00
Davidson Gomes
e157a2a36b fix: adjusts in proxy 2023-10-18 18:09:01 -03:00
Davidson Gomes
d8d7debfee fix: lid messages 2023-10-11 17:27:49 -03:00
Davidson Gomes
a82b206fe6 test: messages.upsert 2023-10-10 10:00:11 -03:00
Davidson Gomes
a4416214c8 Merge tag '1.5.4' into develop
* Baileys logger typing issue resolved
* Solved problem with duplicate messages in chatwoot
2023-10-09 20:43:50 -03:00
169 changed files with 13031 additions and 4404 deletions

BIN
.DS_Store vendored

Binary file not shown.

View File

@@ -0,0 +1,107 @@
name: Bug Report
description: Create a report to help us improve.
labels:
- bug
- en
# - help wanted
# Automatically assign the issue to:
# assignees: DavidsonGomes
body:
- type: checkboxes
id: terms
attributes:
label: Welcome!
description: |
The issue tracker is only for reporting bugs and feature requests.
For user-related support questions, please visit:
- [Discord](https://evolution-api.com/discord)
- [WhatsApp Group](https://evolution-api.com/whatsapp)
<br/>
**DO NOT OPEN AN ISSUE FOR GENERAL SUPPORT QUESTIONS.**
options:
- label: Yes, I have searched for similar issues on [GitHub](https://github.com/EvolutionAPI/evolution-api/issues) and found none.
required: true
- type: textarea
attributes:
label: What did you do?
description: |
How to write a good bug report?
- Respect the issue template as much as possible.
- The title should be short and descriptive.
- Explain the conditions that led you to report this issue: the context.
- The context should lead to something, an idea or problem you are facing.
- Be clear and concise.
- Format your messages to help the reader focus on what matters and understand the structure of your message, use [Markdown syntax](https://help.github.com/articles/github-flavored-markdown)
placeholder: Describe the problem you encountered in detail.
validations:
required: true
- type: textarea
attributes:
label: What did you expect?
placeholder: Describe what you expected to happen.
validations:
required: true
- type: textarea
attributes:
label: What did you observe instead of what you expected?
placeholder: Explain what actually happens when you follow the steps above.
validations:
required: true
- type: textarea
attributes:
label: Screenshots/Videos
placeholder: |
If possible, add screenshots or videos that illustrate the problem. This can be extremely helpful in understanding the issue better.
- type: textarea
attributes:
label: Which version of the API are you using?
description: |
Enter the version number found in the `version` property of the **package.json**.
placeholder: Paste the version here.
validations:
required: true
- type: dropdown
id: select
attributes:
label: What is your environment?
options:
- Windows
- Linux
- Mac
- Docker
- Other
validations:
required: true
- type: textarea
attributes:
label: Other environment specifications
placeholder: 'Hardware/Software: [e.g., CPU, GPU]'
validations:
required: false
- type: textarea
attributes:
label: If applicable, paste the log output
description: |
Please attach any logs that might be related to the issue.
If the logs contain sensitive information, consider sending them privately to one of the project maintainers.
placeholder: Paste the application log here.
validations:
required: false
- type: textarea
attributes:
label: Additional Notes
description: Include any other information you think might be useful to understand or resolve the bug.
validations:
required: false

View File

@@ -0,0 +1,86 @@
name: Feature Request
description: Suggest ideas for the project.
labels:
- enhancement
- en
# Automatically assign the issue to:
# assignees: DavidsonGomes
body:
- type: checkboxes
id: terms
attributes:
label: Welcome!
description: '**DO NOT OPEN FOR GENERAL SUPPORT QUESTIONS.**'
options:
- label: Yes, I have searched for similar requests on [GitHub](https://github.com/code-chat-br/whatsapp-api/issues) and found none.
required: true
- type: dropdown
attributes:
label: What type of feature?
description: |
How to write a good feature request?
- Respect the issue template as much as possible.
- The title should be short and descriptive.
- Explain the conditions that led you to suggest this feature: the context.
- The context should lead to something, an idea or problem you are facing.
- Be clear and concise.
- Format your messages to help the reader focus on what matters and understand the structure of your message, use [Markdown syntax](https://help.github.com/articles/github-flavored-markdown)
options:
- Integration
- Functionality
- Endpoint
- Database adjustment
- Other
validations:
required: true
- type: textarea
attributes:
label: What is the motivation for the request?
description: |
What problem does the feature seek to solve?
Clearly and in detail describe the functionality you want to be implemented.
Explain how it fits into the context of the project.
placeholder: Detailed description
validations:
required: true
- type: textarea
attributes:
label: Usage Examples
description: |
Provide specific examples of how this functionality could be used.
This can include scenarios or use cases where the feature would be particularly beneficial.
placeholder: text - image - video - flowcharts
validations:
required: false
- type: textarea
attributes:
label: How should the feature be developed?
description: |
Should it be inserted directly into the code?
Should it be built as a different application that acts as an extension of the API?
For example: a `worker`?
Discuss how this new functionality could impact other parts of the project, if applicable.
---
If you have ideas on how this functionality could be implemented, please share them here.
This is not mandatory, but it can be helpful for the development team.
placeholder: Insert feature ideas here
validations:
required: false
- type: textarea
attributes:
label: Additional Notes
description: Any other information you believe is relevant to your request.
placeholder: Insert your observations here.
validations:
required: false

View File

@@ -0,0 +1,108 @@
name: Relatório de bug
description: Crie um relatório para nos ajudar a melhorar.
labels:
- bug
- pt-br
# - help wanted
# Atrubuir automaticamente a issue a:
# assignees: DavidsonGomes
body:
- type: checkboxes
id: termos
attributes:
label: Bem-vido!
description: |
O rastreador de problemas é apenas para relatar bugs e solicitações de recursos.
Para perguntas de suporte relacionadas ao usuário final, acesse:
- [Discord](https://evolution-api.com/discord)
- [Grupo do WhatsApp](https://evolution-api.com/whatsapp)
<br/>
**NÃO ABRA UM PROBLEMA PARA PERGUNTAS GERAIS DE SUPORTE.**
options:
- label: Sim, pesquisei problemas semelhantes no [GitHub](https://github.com/EvolutionAPI/evolution-api/issues) e não encontrei nenhum.
required: true
- type: textarea
attributes:
label: O que você fez?
description: |
Como escrever um bom relatório de bug?
- Respeite o modelo de problema tanto quanto possível.
- O título deve ser curto e descritivo.
- Explique as condições que o levaram a reportar este problema: o contexto.
- O contexto deve levar a algo, ideia ou problema que você está enfrentando.
- Seja claro e conciso.
- Formate suas mensagens para ajudar o leitor a se concentrar no que importa e entender a estrutura da sua mensagem, use [sintaxe Markdown](https://help.github.com/articles/github-flavored-markdown)
placeholder: Descreva detalhadamente o problema que você encontrou.
validations:
required: true
- type: textarea
attributes:
label: O que você esperava?
placeholder: Descreva o que você esperava que acontecesse.
validations:
required: true
- type: textarea
attributes:
label: O que vc observou ao invés do que esperava?
placeholder: Explique o que realmente acontece quando você segue os passos acima.
validations:
required: true
- type: textarea
attributes:
label: Capturas de Tela/Vídeos
placeholder: |
Se possível, adicione capturas de tela ou vídeos que ilustrem o problema. Isso pode ser extremamente útil para entender melhor o problema.
- type: textarea
attributes:
label: Qual versão da API você está usando?
description: |
Insira o número da sua versão que está na propriedade `version` do **package.json**.
placeholder: Cole aqui a versão.
validations:
required: true
- type: dropdown
id: select
attributes:
label: Qual é o seu ambiente?
options:
- Windows
- Linux
- Mac
- Docker
- Outro
validations:
required: true
- type: textarea
attributes:
label: Outras expecificações do ambiente
placeholder: 'Hardware/Software: [ex: CPU, GPU]'
validations:
required: false
- type: textarea
attributes:
label: Se aplicável, cole a saída do log
description: |
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.
placeholder: Cole aqui o log da aplicação
validations:
required: false
- type: textarea
attributes:
label: Notas Adicionais
description: Inclua aqui qualquer outra informação que você ache que possa ser útil para entender ou resolver o bug.
validations:
required: false

View File

@@ -0,0 +1,86 @@
name: Solicitação de recursos
description: Sugira ideias para o projeto.
labels:
- enhancement
- pt-br
# Automatically assign the issue to:
# assignees: DavidsonGomes
body:
- type: checkboxes
id: termos
attributes:
label: Bem-vindo!
description: '**NÃO ABRA PARA PERGUNTAS GERAIS DE SUPORTE.**'
options:
- label: Sim, pesquisei solicitações semelhantes no [GitHub](https://github.com/code-chat-br/whatsapp-api/issues) e não encontrei nenhum.
required: true
- type: dropdown
attributes:
label: Qual tipo de recurso?
description: |
Como escrever uma boa solicitação de bug?
- Respeite o modelo de problema tanto quanto possível.
- O título deve ser curto e descritivo.
- Explique as condições que o levaram a reportar este problema: o contexto.
- O contexto deve levar a algo, ideia ou problema que você está enfrentando.
- Seja claro e conciso.
- Formate suas mensagens para ajudar o leitor a se concentrar no que importa e entender a estrutura da sua mensagem, use [sintaxe Markdown](https://help.github.com/articles/github-flavored-markdown)
options:
- Integração
- Funcionalidade
- Endpoint
- Ajuste de banco de dados
- Outro
validations:
required: true
- type: textarea
attributes:
label: Qual a motivação para a solicitação?
description: |
Qual problema o recurso busca resolver?
Descreva claramente e em detalhes a funcionalidade que você deseja que seja implementada.
Explique como isso se encaixa no contexto do projeto.
placeholder: Descrição detalhada
validations:
required: true
- type: textarea
attributes:
label: Exemplos de Uso
description: |
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.
placeholder: texto - imagem - video - fluxogramas
validations:
required: false
- type: textarea
attributes:
label: Como o recurso deve ser desenvolvido?
description: |
Deve ser inserido diretamente no código?
Deve ser construído uma aplicação diferente que atuará como uma extenção da api?
Por exemplo: um `worker`?
Discuta como essa nova funcionalidade poderia impactar outras partes do projeto, se aplicável.
---
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.
placeholder: Insira ideias para o recurso
validations:
required: false
- type: textarea
attributes:
label: Notas Adicionais
description: Qualquer outra informação que você acredita ser relevante para a sua solicitação.
placeholder: Insira aqui as sua observções.
validations:
required: false

View File

@@ -0,0 +1,48 @@
name: Build Docker image
on:
push:
tags:
- "*.*.*"
jobs:
build_deploy:
name: Build and Deploy
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Docker meta
id: meta
uses: docker/metadata-action@v5
with:
images: atendai/evolution-api
tags: type=semver,pattern=v{{version}}
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to GitHub Container Registry
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Build and push
id: docker_build
uses: docker/build-push-action@v5
with:
platforms: linux/amd64,linux/arm64
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
- name: Image digest
run: echo ${{ steps.docker_build.outputs.digest }}

View File

@@ -0,0 +1,48 @@
name: Build Docker image
on:
push:
branches:
- develop
jobs:
build_deploy:
name: Build and Deploy
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Docker meta
id: meta
uses: docker/metadata-action@v5
with:
images: atendai/evolution-api
tags: homolog
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Build and push
id: docker_build
uses: docker/build-push-action@v5
with:
platforms: linux/amd64,linux/arm64
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
- name: Image digest
run: echo ${{ steps.docker_build.outputs.digest }}

View File

@@ -0,0 +1,48 @@
name: Build Docker image
on:
push:
branches:
- v2.0.0
jobs:
build_deploy:
name: Build and Deploy
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Docker meta
id: meta
uses: docker/metadata-action@v5
with:
images: atendai/evolution-api
tags: v2.0.0-alpha
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Build and push
id: docker_build
uses: docker/build-push-action@v5
with:
platforms: linux/amd64,linux/arm64
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
- name: Image digest
run: echo ${{ steps.docker_build.outputs.digest }}

4
.gitignore vendored
View File

@@ -4,6 +4,8 @@
/Docker/.env
.vscode
# Logs
logs/**.json
*.log
@@ -39,8 +41,10 @@ docker-compose.yaml
/test/
/src/env.yml
/store
*.env
/temp/*
.DS_Store
*.DS_Store
.tool-versions

11
.vscode/settings.json vendored
View File

@@ -5,7 +5,12 @@
"editor.smoothScrolling": true,
"editor.tabSize": 2,
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true,
"source.fixAll": true
}
"source.fixAll.eslint": "explicit",
"source.fixAll": "explicit"
},
"prisma-smart-formatter.typescript.defaultFormatter": "esbenp.prettier-vscode",
"prisma-smart-formatter.prisma.defaultFormatter": "Prisma.prisma",
"i18n-ally.localesPaths": [
"store/messages"
]
}

View File

@@ -1,3 +1,187 @@
# 1.8.1 (2024-06-08 21:32)
### Feature
* New method of saving sessions to a file using worker, made in partnership with [codechat](https://github.com/code-chat-br/whatsapp-api)
### Fixed
* Correction of variables breaking lines in typebot
# 1.8.0 (2024-05-27 16:10)
### Feature
* Now in the manager, when logging in with the client's apikey, the listing only shows the instance corresponding to the provided apikey (only with MongoDB)
* New global mode for rabbitmq events
* Build in docker for linux/amd64, linux/arm64 platforms
### Fixed
* Correction in message formatting when generated by AI as markdown in typebot
* Security fix in fetch instance with client key when not connected to mongodb
# 1.7.5 (2024-05-21 08:50)
### Fixed
* Add merge_brazil_contacts function to solve nine digit in brazilian numbers
* Optimize ChatwootService method for updating contact
* Fix swagger auth
* Update aws sdk v3
* Fix getOpenConversationByContact and init queries error
* Method to mark chat as unread
* Added environment variable to manually select the WhatsApp web version for the baileys lib (optional)
# 1.7.4 (2024-04-28 09:46)
### Fixed
* Adjusts in proxy on fetchAgent
* Recovering messages lost with redis cache
* Log when init redis cache service
* Recovering messages lost with redis cache
* Chatwoot inbox name
* Update Baileys version
# 1.7.3 (2024-04-18 12:07)
### Fixed
* Revert fix audio encoding
* Recovering messages lost with redis cache
* Adjusts in redis for save instances
* Adjusts in proxy
* Revert pull request #523
* Added instance name on logs
* Added support for Spanish
* Fix error: invalid operator. The allowed operators for identifier are equal_to,not_equal_to in chatwoot
# 1.7.2 (2024-04-12 17:31)
### Feature
* Mobile connection via sms (test)
### Fixed
* Adjusts in redis
* Send global event in websocket
* Adjusts in proxy
* Fix audio encoding
* Fix conversation read on chatwoot version 3.7
* Fix when receiving/sending messages from whatsapp desktop with ephemeral messages enabled
* Changed returned sessions on typebot status change
* Reorganization of files and folders
# 1.7.1 (2024-04-03 10:19)
### Fixed
* Correction when sending files with captions on Whatsapp Business
* Correction in receiving messages with response on WhatsApp Business
* Correction when sending a reaction to a message on WhatsApp Business
* Correction of receiving reactions on WhatsApp business
* Removed mandatory description of rows from sendList
* Feature to collect message type in typebot
# 1.7.0 (2024-03-11 18:23)
### Feature
* Added update message endpoint
* Add translate capabilities to QRMessages in CW
* Join in Group by Invite Code
* Read messages from whatsapp in chatwoot
* Add support to use use redis in cacheservice
* Add support for labels
* Command to clearcache from chatwoot inbox
* Whatsapp Cloud API Oficial
### Fixed
* Proxy configuration improvements
* Correction in sending lists
* Adjust in webhook_base64
* Correction in typebot text formatting
* Correction in chatwoot text formatting and render list message
* Only use a axios request to get file mimetype if necessary
* When possible use the original file extension
* When receiving a file from whatsapp, use the original filename in chatwoot if possible
* Remove message ids cache in chatwoot to use chatwoot's api itself
* Adjusts the quoted message, now has contextInfo in the message Raw
* Collecting responses with text or numbers in Typebot
* Added sendList endpoint to swagger documentation
* Implemented a function to synchronize message deletions on WhatsApp, automatically reflecting in Chatwoot.
* Improvement on numbers validation
* Fix polls in message sending
* Sending status message
* Message 'connection successfully' spamming
* Invalidate the conversation cache if reopen_conversation is false and the conversation was resolved
* Fix looping when deleting a message in chatwoot
* When receiving a file from whatsapp, use the original filename in chatwoot if possible
* Correction in the sendList Function
* Implement contact upsert in messaging-history.set
* Improve proxy error handling
* Refactor fetching participants for group in WhatsApp service
* Fixed problem where the typebot final keyword did not work
* Typebot's wait now pauses the flow and composing is defined by the delay_message parameter in set typebot
* Composing over 20s now loops until finished
# 1.6.1 (2023-12-22 11:43)
### Fixed
* Fixed Lid Messages
* Fixed sending variables to typebot
* Fixed sending variables from typebot
* Correction sending s3/minio media to chatwoot and typebot
* Fixed the problem with typebot closing at the end of the flow, now this is optional with the TYPEBOT_KEEP_OPEN variable
* Fixed chatwoot Bold, Italic and Underline formatting using Regex
* Added the sign_delimiter property to the Chatwoot configuration, allowing you to set a different delimiter for the signature. Default when not defined \n
* Include instance Id field in the instance configuration
* Fixed the pairing code
* Adjusts in typebot
* Fix the problem when disconnecting the instance and connecting again using mongodb
* Options to disable docs and manager
* When deleting a message in whatsapp, delete the message in chatwoot too
# 1.6.0 (2023-12-12 17:24)
### Feature
* Added AWS SQS Integration
* Added support for new typebot API
* Added endpoint sendPresence
* New Instance Manager
* Added auto_create to the chatwoot set to create the inbox automatically or not
* Added reply, delete and message reaction in chatwoot v3.3.1
### Fixed
* Adjusts in proxy
* Adjusts in start session for Typebot
* Added mimetype field when sending media
* Ajusts in validations to messages.upsert
* Fixed messages not received: error handling when updating contact in chatwoot
* Fix workaround to manage param data as an array in mongodb
* Removed await from webhook when sending a message
* Update typebot.service.ts - element.underline change ~ for *
* Removed api restart on receiving an error
* Fixes in mongodb and chatwoot
* Adjusted return from queries in mongodb
* Added restart instance when update profile picture
* Correction of chatwoot functioning with admin flows
* Fixed problem that did not generate qrcode with the chatwoot_conversation_pending option enabled
* Fixed issue where CSAT opened a new ticket when reopen_conversation was disabled
* Fixed issue sending contact to Chatwoot via iOS
### Integrations
* Chatwoot: v3.3.1
* Typebot: v2.20.0
# 1.5.4 (2023-10-09 20:43)
### Fixed
@@ -75,9 +259,9 @@
### Integrations
- Chatwoot: v2.18.0 - v3.0.0
- Typebot: v2.16.0
- Manager Evolution API
* Chatwoot: v2.18.0 - v3.0.0
* Typebot: v2.16.0
* Manager Evolution API
# 1.4.8 (2023-07-27 10:27)
@@ -165,7 +349,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)
@@ -181,7 +365,7 @@
### Integrations
- Chatwoot: v2.18.0
* Chatwoot: v2.18.0
# 1.3.1 (2023-07-20 07:48)
@@ -191,7 +375,7 @@
### Integrations
- Chatwoot: v2.18.0
* Chatwoot: v2.18.0
# 1.3.0 (2023-07-19 11:33)
@@ -228,7 +412,7 @@
### Integrations
- Chatwoot: v2.18.0
* Chatwoot: v2.18.0
# 1.2.2 (2023-07-15 09:36)
@@ -239,7 +423,7 @@
### Integrations
- Chatwoot: v2.18.0
* Chatwoot: v2.18.0
# 1.2.1 (2023-07-14 19:04)

View File

@@ -16,6 +16,7 @@ LOG_BAILEYS=error
# Default time: 5 minutes
# If you don't even want an expiration, enter the value false
DEL_INSTANCE=false
DEL_TEMP_INSTANCES=true # Delete instances with status closed on start
# Temporary data storage
STORE_MESSAGES=true
@@ -42,14 +43,48 @@ DATABASE_SAVE_MESSAGE_UPDATE=false
DATABASE_SAVE_DATA_CONTACTS=false
DATABASE_SAVE_DATA_CHATS=false
REDIS_ENABLED=false
REDIS_URI=redis://redis:6379
REDIS_PREFIX_KEY=evdocker
RABBITMQ_ENABLED=false
RABBITMQ_URI=amqp://guest:guest@rabbitmq:5672
RABBITMQ_EXCHANGE_NAME=evolution_exchange
RABBITMQ_GLOBAL_ENABLED=false
RABBITMQ_EVENTS_APPLICATION_STARTUP=false
RABBITMQ_EVENTS_QRCODE_UPDATED=true
RABBITMQ_EVENTS_MESSAGES_SET=true
RABBITMQ_EVENTS_MESSAGES_UPSERT=true
RABBITMQ_EVENTS_MESSAGES_UPDATE=true
RABBITMQ_EVENTS_MESSAGES_DELETE=true
RABBITMQ_EVENTS_SEND_MESSAGE=true
RABBITMQ_EVENTS_CONTACTS_SET=true
RABBITMQ_EVENTS_CONTACTS_UPSERT=true
RABBITMQ_EVENTS_CONTACTS_UPDATE=true
RABBITMQ_EVENTS_PRESENCE_UPDATE=true
RABBITMQ_EVENTS_CHATS_SET=true
RABBITMQ_EVENTS_CHATS_UPSERT=true
RABBITMQ_EVENTS_CHATS_UPDATE=true
RABBITMQ_EVENTS_CHATS_DELETE=true
RABBITMQ_EVENTS_GROUPS_UPSERT=true
RABBITMQ_EVENTS_GROUPS_UPDATE=true
RABBITMQ_EVENTS_GROUP_PARTICIPANTS_UPDATE=true
RABBITMQ_EVENTS_CONNECTION_UPDATE=true
RABBITMQ_EVENTS_LABELS_EDIT=true
RABBITMQ_EVENTS_LABELS_ASSOCIATION=true
RABBITMQ_EVENTS_CALL=true
RABBITMQ_EVENTS_TYPEBOT_START=false
RABBITMQ_EVENTS_TYPEBOT_CHANGE_STATUS=false
WEBSOCKET_ENABLED=false
WEBSOCKET_GLOBAL_EVENTS=false
WA_BUSINESS_TOKEN_WEBHOOK=evolution
WA_BUSINESS_URL=https://graph.facebook.com
WA_BUSINESS_VERSION=v18.0
WA_BUSINESS_LANGUAGE=pt_BR
SQS_ENABLED=false
SQS_ACCESS_KEY_ID=
SQS_SECRET_ACCESS_KEY=
SQS_ACCOUNT_ID=
SQS_REGION=
# Global Webhook Settings
# Each instance's Webhook URL and events will be requested at the time it is created
@@ -78,6 +113,8 @@ WEBHOOK_EVENTS_GROUPS_UPSERT=true
WEBHOOK_EVENTS_GROUPS_UPDATE=true
WEBHOOK_EVENTS_GROUP_PARTICIPANTS_UPDATE=true
WEBHOOK_EVENTS_CONNECTION_UPDATE=true
WEBHOOK_EVENTS_LABELS_EDIT=true
WEBHOOK_EVENTS_LABELS_ASSOCIATION=true
WEBHOOK_EVENTS_CALL=true
# This event fires every time a new token is requested via the refresh route
WEBHOOK_EVENTS_NEW_JWT_TOKEN=false
@@ -97,7 +134,28 @@ CONFIG_SESSION_PHONE_NAME=Chrome
# Set qrcode display limit
QRCODE_LIMIT=30
QRCODE_COLOR=#198754
QRCODE_COLOR='#198754'
# old | latest
TYPEBOT_API_VERSION=latest
TYPEBOT_KEEP_OPEN=false
#Chatwoot
# If you leave this option as false, when deleting the message for everyone on WhatsApp, it will not be deleted on Chatwoot.
CHATWOOT_MESSAGE_DELETE=false # false | true
# If you leave this option as true, when sending a message in Chatwoot, the client's last message will be marked as read on WhatsApp.
CHATWOOT_MESSAGE_READ=false # false | true
# This db connection is used to import messages from whatsapp to chatwoot database
CHATWOOT_IMPORT_DATABASE_CONNECTION_URI=postgres://user:password@hostname:port/dbname?sslmode=disable
CHATWOOT_IMPORT_DATABASE_PLACEHOLDER_MEDIA_MESSAGE=true
CACHE_REDIS_ENABLED=false
CACHE_REDIS_URI=redis://redis:6379
CACHE_REDIS_PREFIX_KEY=evolution
CACHE_REDIS_TTL=604800
CACHE_REDIS_SAVE_INSTANCES=false
CACHE_LOCAL_ENABLED=false
CACHE_LOCAL_TTL=604800
# Defines an authentication type for the api
# We recommend using the apikey because it will allow you to use a custom token,
@@ -112,3 +170,5 @@ AUTHENTICATION_EXPOSE_IN_FETCH_INSTANCES=true
# seconds - 3600s ===1h | zero (0) - never expires
AUTHENTICATION_JWT_EXPIRIN_IN=0
AUTHENTICATION_JWT_SECRET='L=0YWt]b2w[WF>#>:&E`'
LANGUAGE=en # pt-BR, en

View File

@@ -4,7 +4,7 @@ services:
api:
container_name: evolution_api
image: davidsongomes/evolution-api
image: atendai/evolution-api
restart: always
ports:
- 8080:8080

View File

@@ -16,6 +16,7 @@ LOG_BAILEYS=error
# Default time: 5 minutes
# If you don't even want an expiration, enter the value false
DEL_INSTANCE=false
DEL_TEMP_INSTANCES=true # Delete instances with status closed on start
# Temporary data storage
STORE_MESSAGES=true
@@ -32,7 +33,10 @@ 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_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
@@ -42,10 +46,6 @@ 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
@@ -73,6 +73,8 @@ WEBHOOK_EVENTS_GROUPS_UPSERT=true
WEBHOOK_EVENTS_GROUPS_UPDATE=true
WEBHOOK_EVENTS_GROUP_PARTICIPANTS_UPDATE=true
WEBHOOK_EVENTS_CONNECTION_UPDATE=true
WEBHOOK_EVENTS_LABELS_EDIT=true
WEBHOOK_EVENTS_LABELS_ASSOCIATION=true
# This event fires every time a new token is requested via the refresh route
WEBHOOK_EVENTS_NEW_JWT_TOKEN=false
@@ -84,6 +86,14 @@ CONFIG_SESSION_PHONE_NAME=chrome
# Set qrcode display limit
QRCODE_LIMIT=30
CACHE_REDIS_ENABLED=false
CACHE_REDIS_URI=redis://redis:6379
CACHE_REDIS_PREFIX_KEY=evolution
CACHE_REDIS_TTL=604800
CACHE_REDIS_SAVE_INSTANCES=false
CACHE_LOCAL_ENABLED=false
CACHE_LOCAL_TTL=604800
# 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

View File

@@ -62,7 +62,7 @@ services:
api:
container_name: evolution_api
image: davidsongomes/evolution-api
image: atendai/evolution-api
restart: always
depends_on:
- mongodb

View File

@@ -1,6 +1,6 @@
FROM node:20.7.0-alpine
FROM node:20.7.0-alpine AS builder
LABEL version="1.5.4" description="Api to control whatsapp features through http requests."
LABEL version="1.8.0" description="Api to control whatsapp features through http requests."
LABEL maintainer="Davidson Gomes" git="https://github.com/DavidsonGomes"
LABEL contact="contato@agenciadgcode.com"
@@ -11,9 +11,19 @@ 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=*
@@ -25,6 +35,7 @@ ENV LOG_COLOR=true
ENV LOG_BAILEYS=error
ENV DEL_INSTANCE=false
ENV DEL_TEMP_INSTANCES=true
ENV STORE_MESSAGES=true
ENV STORE_MESSAGE_UP=true
@@ -47,14 +58,50 @@ ENV DATABASE_SAVE_MESSAGE_UPDATE=false
ENV DATABASE_SAVE_DATA_CONTACTS=false
ENV DATABASE_SAVE_DATA_CHATS=false
ENV REDIS_ENABLED=false
ENV REDIS_URI=redis://redis:6379
ENV REDIS_PREFIX_KEY=evolution
ENV RABBITMQ_ENABLED=false
ENV RABBITMQ_URI=amqp://guest:guest@rabbitmq:5672
ENV RABBITMQ_EXCHANGE_NAME=evolution_exchange
ENV RABBITMQ_GLOBAL_ENABLED=false
ENV RABBITMQ_EVENTS_APPLICATION_STARTUP=false
ENV RABBITMQ_EVENTS_INSTANCE_CREATE=false
ENV RABBITMQ_EVENTS_INSTANCE_DELETE=false
ENV RABBITMQ_EVENTS_QRCODE_UPDATED=true
ENV RABBITMQ_EVENTS_MESSAGES_SET=true
ENV RABBITMQ_EVENTS_MESSAGES_UPSERT=true
ENV RABBITMQ_EVENTS_MESSAGES_UPDATE=true
ENV RABBITMQ_EVENTS_MESSAGES_DELETE=true
ENV RABBITMQ_EVENTS_SEND_MESSAGE=true
ENV RABBITMQ_EVENTS_CONTACTS_SET=true
ENV RABBITMQ_EVENTS_CONTACTS_UPSERT=true
ENV RABBITMQ_EVENTS_CONTACTS_UPDATE=true
ENV RABBITMQ_EVENTS_PRESENCE_UPDATE=true
ENV RABBITMQ_EVENTS_CHATS_SET=true
ENV RABBITMQ_EVENTS_CHATS_UPSERT=true
ENV RABBITMQ_EVENTS_CHATS_UPDATE=true
ENV RABBITMQ_EVENTS_CHATS_DELETE=true
ENV RABBITMQ_EVENTS_GROUPS_UPSERT=true
ENV RABBITMQ_EVENTS_GROUPS_UPDATE=true
ENV RABBITMQ_EVENTS_GROUP_PARTICIPANTS_UPDATE=true
ENV RABBITMQ_EVENTS_CONNECTION_UPDATE=true
ENV RABBITMQ_EVENTS_LABELS_EDIT=true
ENV RABBITMQ_EVENTS_LABELS_ASSOCIATION=true
ENV RABBITMQ_EVENTS_CALL=true
ENV RABBITMQ_EVENTS_TYPEBOT_START=false
ENV RABBITMQ_EVENTS_TYPEBOT_CHANGE_STATUS=false
ENV WEBSOCKET_ENABLED=false
ENV WEBSOCKET_GLOBAL_EVENTS=false
ENV WA_BUSINESS_TOKEN_WEBHOOK=evolution
ENV WA_BUSINESS_URL=https://graph.facebook.com
ENV WA_BUSINESS_VERSION=v18.0
ENV WA_BUSINESS_LANGUAGE=pt_BR
ENV SQS_ENABLED=false
ENV SQS_ACCESS_KEY_ID=
ENV SQS_SECRET_ACCESS_KEY=
ENV SQS_ACCOUNT_ID=
ENV SQS_REGION=
ENV WEBHOOK_GLOBAL_URL=
ENV WEBHOOK_GLOBAL_ENABLED=false
@@ -62,6 +109,8 @@ 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
@@ -80,6 +129,8 @@ ENV WEBHOOK_EVENTS_GROUPS_UPSERT=true
ENV WEBHOOK_EVENTS_GROUPS_UPDATE=true
ENV WEBHOOK_EVENTS_GROUP_PARTICIPANTS_UPDATE=true
ENV WEBHOOK_EVENTS_CONNECTION_UPDATE=true
ENV WEBHOOK_EVENTS_LABELS_EDIT=true
ENV WEBHOOK_EVENTS_LABELS_ASSOCIATION=true
ENV WEBHOOK_EVENTS_CALL=true
ENV WEBHOOK_EVENTS_NEW_JWT_TOKEN=false
@@ -98,6 +149,16 @@ ENV CONFIG_SESSION_PHONE_NAME=Chrome
ENV QRCODE_LIMIT=30
ENV QRCODE_COLOR=#198754
ENV TYPEBOT_API_VERSION=latest
ENV CACHE_REDIS_ENABLED=false
ENV CACHE_REDIS_URI=redis://redis:6379
ENV CACHE_REDIS_PREFIX_KEY=evolution
ENV CACHE_REDIS_TTL=604800
ENV CACHE_REDIS_SAVE_INSTANCES=false
ENV CACHE_LOCAL_ENABLED=false
ENV CACHE_LOCAL_TTL=604800
ENV AUTHENTICATION_TYPE=apikey
ENV AUTHENTICATION_API_KEY=B6D711FCDE4D4FD5936544120E713976
@@ -114,10 +175,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" ]

View File

@@ -39,12 +39,13 @@ This code was produced based on the baileys library and it is still under develo
</a>
</div>
#### Buy me coffe
#### Buy me coffe - PIX
<div align="center">
<a href="https://bmc.link/evolutionapi" target="_blank" rel="noopener noreferrer">
<img src="./public/images/bmc_qr.png" style="width: 50% !important;">
<img src="./public/images/qrcode-pix.png" style="width: 50% !important;">
</a>
<p><b>CHAVE PIX (Telefone):</b> (74)99987-9409</p>
</div>
</br>

View File

@@ -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

View File

@@ -1,6 +1,6 @@
{
"name": "evolution-api",
"version": "1.5.4",
"version": "1.8.1",
"description": "Rest api for communication with WhatsApp",
"main": "./dist/src/main.js",
"scripts": {
@@ -46,40 +46,48 @@
"@figuro/chatwoot-sdk": "^1.1.16",
"@hapi/boom": "^10.0.1",
"@sentry/node": "^7.59.2",
"@whiskeysockets/baileys": "^6.5.0",
"amqplib": "^0.10.3",
"axios": "^1.3.5",
"class-validator": "^0.13.2",
"@aws-sdk/client-sqs": "^3.569.0",
"axios": "^1.6.5",
"@whiskeysockets/baileys": "6.7.5",
"class-validator": "^0.14.1",
"compression": "^1.7.4",
"cors": "^2.8.5",
"cross-env": "^7.0.3",
"dayjs": "^1.11.7",
"eventemitter2": "^6.4.9",
"evolution-manager": "^0.4.13",
"exiftool-vendored": "^22.0.0",
"express": "^4.18.2",
"express-async-errors": "^3.1.1",
"fluent-ffmpeg": "^2.1.2",
"form-data": "^4.0.0",
"hbs": "^4.2.0",
"https-proxy-agent": "^7.0.2",
"i18next": "^23.7.19",
"jimp": "^0.16.13",
"join": "^3.0.0",
"js-yaml": "^4.1.0",
"jsonschema": "^1.4.1",
"jsonwebtoken": "^8.5.1",
"jsonwebtoken": "^9.0.2",
"libphonenumber-js": "^1.10.39",
"link-preview-js": "^3.0.4",
"mongoose": "^6.10.5",
"node-cache": "^5.1.2",
"node-mime-types": "^1.1.0",
"node-windows": "^1.0.0-beta.8",
"parse-bmfont-xml": "^1.1.4",
"pg": "^8.11.3",
"pino": "^8.11.0",
"proxy-agent": "^6.3.0",
"qrcode": "^1.5.1",
"qrcode-terminal": "^0.12.0",
"redis": "^4.6.5",
"sharp": "^0.30.7",
"sharp": "^0.32.2",
"socket.io": "^4.7.1",
"socks-proxy-agent": "^8.0.1",
"swagger-ui-express": "^5.0.0",
"uuid": "^9.0.0",
"xml2js": "^0.6.2",
"yamljs": "^0.3.0"
},
"devDependencies": {

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

View File

@@ -0,0 +1,19 @@
export interface ICache {
get(key: string): Promise<any>;
hGet(key: string, field: string): Promise<any>;
set(key: string, value: any, ttl?: number): void;
hSet(key: string, field: string, value: any): Promise<void>;
has(key: string): Promise<boolean>;
keys(appendCriteria?: string): Promise<string[]>;
delete(key: string | string[]): Promise<number>;
hDelete(key: string, field: string): Promise<any>;
deleteAll(appendCriteria?: string): Promise<number>;
}

View File

@@ -21,7 +21,6 @@ const logger = new Logger('Validate');
export abstract class RouterBroker {
constructor() {}
public routerPath(path: string, param = true) {
// const route = param ? '/:instanceName/' + path : '/' + path;
let route = '/' + path;
param ? (route += '/:instanceName') : null;
@@ -56,10 +55,6 @@ export abstract class RouterBroker {
message = stack.replace('instance.', '');
}
return message;
// return {
// property: property.replace('instance.', ''),
// message,
// };
});
logger.error(message);
throw new BadRequestException(message);

View File

@@ -1,14 +1,18 @@
import { Logger } from '../../config/logger.config';
import {
ArchiveChatDto,
BlockUserDto,
DeleteMessage,
getBase64FromMediaMessageDto,
MarkChatUnreadDto,
NumberDto,
PrivacySettingDto,
ProfileNameDto,
ProfilePictureDto,
ProfileStatusDto,
ReadMessageDto,
SendPresenceDto,
UpdateMessageDto,
WhatsAppNumberDto,
} from '../dto/chat.dto';
import { InstanceDto } from '../dto/instance.dto';
@@ -37,6 +41,11 @@ export class ChatController {
return await this.waMonitor.waInstances[instanceName].archiveChat(data);
}
public async markChatUnread({ instanceName }: InstanceDto, data: MarkChatUnreadDto) {
logger.verbose('requested markChatUnread from ' + instanceName + ' instance');
return await this.waMonitor.waInstances[instanceName].markChatUnread(data);
}
public async deleteMessage({ instanceName }: InstanceDto, data: DeleteMessage) {
logger.verbose('requested deleteMessage from ' + instanceName + ' instance');
return await this.waMonitor.waInstances[instanceName].deleteMessage(data);
@@ -77,6 +86,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();
@@ -111,4 +125,14 @@ export class ChatController {
logger.verbose('requested removeProfilePicture from ' + instanceName + ' instance');
return await this.waMonitor.waInstances[instanceName].removeProfilePicture();
}
public async updateMessage({ instanceName }: InstanceDto, data: UpdateMessageDto) {
logger.verbose('requested updateMessage from ' + instanceName + ' instance');
return await this.waMonitor.waInstances[instanceName].updateMessage(data);
}
public async blockUser({ instanceName }: InstanceDto, data: BlockUserDto) {
logger.verbose('requested blockUser from ' + instanceName + ' instance');
return await this.waMonitor.waInstances[instanceName].blockUser(data);
}
}

View File

@@ -1,5 +1,6 @@
import { Logger } from '../../config/logger.config';
import {
AcceptGroupInvite,
CreateGroupDto,
GetParticipant,
GroupDescriptionDto,
@@ -65,6 +66,11 @@ export class GroupController {
return await this.waMonitor.waInstances[instance.instanceName].sendInvite(data);
}
public async acceptInviteCode(instance: InstanceDto, inviteCode: AcceptGroupInvite) {
logger.verbose('requested acceptInviteCode from ' + instance.instanceName + ' instance');
return await this.waMonitor.waInstances[instance.instanceName].acceptInviteCode(inviteCode);
}
public async revokeInviteCode(instance: InstanceDto, groupJid: GroupJid) {
logger.verbose('requested revokeInviteCode from ' + instance.instanceName + ' instance');
return await this.waMonitor.waInstances[instance.instanceName].revokeInviteCode(groupJid);

View File

@@ -1,23 +1,29 @@
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 { Auth, ConfigService, HttpServer, WaBusiness } from '../../config/env.config';
import { Logger } from '../../config/logger.config';
import { BadRequestException, InternalServerErrorException } from '../../exceptions';
import { RedisCache } from '../../libs/redis.client';
import { InstanceDto } from '../dto/instance.dto';
import { BadRequestException, InternalServerErrorException, UnauthorizedException } from '../../exceptions';
import { InstanceDto, SetPresenceDto } from '../dto/instance.dto';
import { ChatwootService } from '../integrations/chatwoot/services/chatwoot.service';
import { RabbitmqService } from '../integrations/rabbitmq/services/rabbitmq.service';
import { SqsService } from '../integrations/sqs/services/sqs.service';
import { TypebotService } from '../integrations/typebot/services/typebot.service';
import { WebsocketService } from '../integrations/websocket/services/websocket.service';
import { ProviderFiles } from '../provider/sessions';
import { RepositoryBroker } from '../repository/repository.manager';
import { AuthService, OldToken } from '../services/auth.service';
import { ChatwootService } from '../services/chatwoot.service';
import { CacheService } from '../services/cache.service';
import { BaileysStartupService } from '../services/channels/whatsapp.baileys.service';
import { BusinessStartupService } from '../services/channels/whatsapp.business.service';
import { IntegrationService } from '../services/integration.service';
import { WAMonitoringService } from '../services/monitor.service';
import { RabbitmqService } from '../services/rabbitmq.service';
import { SettingsService } from '../services/settings.service';
import { 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, Integration, wa } from '../types/wa.types';
import { ProxyController } from './proxy.controller';
export class InstanceController {
constructor(
@@ -31,8 +37,14 @@ export class InstanceController {
private readonly settingsService: SettingsService,
private readonly websocketService: WebsocketService,
private readonly rabbitmqService: RabbitmqService,
private readonly sqsService: SqsService,
private readonly typebotService: TypebotService,
private readonly cache: RedisCache,
private readonly integrationService: IntegrationService,
private readonly proxyService: ProxyController,
private readonly cache: CacheService,
private readonly chatwootCache: CacheService,
private readonly baileysCache: CacheService,
private readonly providerFiles: ProviderFiles,
) {}
private readonly logger = new Logger(InstanceController.name);
@@ -45,6 +57,8 @@ export class InstanceController {
events,
qrcode,
number,
mobile,
integration,
token,
chatwoot_account_id,
chatwoot_token,
@@ -52,16 +66,24 @@ export class InstanceController {
chatwoot_sign_msg,
chatwoot_reopen_conversation,
chatwoot_conversation_pending,
chatwoot_import_contacts,
chatwoot_name_inbox,
chatwoot_merge_brazil_contacts,
chatwoot_import_messages,
chatwoot_days_limit_import_messages,
reject_call,
msg_call,
groups_ignore,
always_online,
read_messages,
read_status,
sync_full_history,
websocket_enabled,
websocket_events,
rabbitmq_enabled,
rabbitmq_events,
sqs_enabled,
sqs_events,
typebot_url,
typebot,
typebot_expire,
@@ -69,6 +91,7 @@ export class InstanceController {
typebot_delay_message,
typebot_unknown_message,
typebot_listening_from_me,
proxy,
}: InstanceDto) {
try {
this.logger.verbose('requested createInstance from ' + instanceName + ' instance');
@@ -76,10 +99,45 @@ export class InstanceController {
this.logger.verbose('checking duplicate token');
await this.authService.checkDuplicateToken(token);
if (!token && integration === Integration.WHATSAPP_BUSINESS) {
throw new BadRequestException('token is required');
}
this.logger.verbose('creating instance');
const instance = new WAStartupService(this.configService, this.eventEmitter, this.repository, this.cache);
let instance: BaileysStartupService | BusinessStartupService;
if (integration === Integration.WHATSAPP_BUSINESS) {
instance = new BusinessStartupService(
this.configService,
this.eventEmitter,
this.repository,
this.cache,
this.chatwootCache,
this.baileysCache,
this.providerFiles,
);
} else {
instance = new BaileysStartupService(
this.configService,
this.eventEmitter,
this.repository,
this.cache,
this.chatwootCache,
this.baileysCache,
this.providerFiles,
);
}
await this.waMonitor.saveInstance({ integration, instanceName, token, number, mobile });
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;
@@ -89,6 +147,7 @@ export class InstanceController {
const hash = await this.authService.generateHash(
{
instanceName: instance.instanceName,
instanceId: instanceId,
},
token,
);
@@ -126,6 +185,8 @@ export class InstanceController {
'GROUP_UPDATE',
'GROUP_PARTICIPANTS_UPDATE',
'CONNECTION_UPDATE',
'LABELS_EDIT',
'LABELS_ASSOCIATION',
'CALL',
'NEW_JWT_TOKEN',
'TYPEBOT_START',
@@ -176,6 +237,8 @@ export class InstanceController {
'GROUP_UPDATE',
'GROUP_PARTICIPANTS_UPDATE',
'CONNECTION_UPDATE',
'LABELS_EDIT',
'LABELS_ASSOCIATION',
'CALL',
'NEW_JWT_TOKEN',
'TYPEBOT_START',
@@ -223,6 +286,8 @@ export class InstanceController {
'GROUP_UPDATE',
'GROUP_PARTICIPANTS_UPDATE',
'CONNECTION_UPDATE',
'LABELS_EDIT',
'LABELS_ASSOCIATION',
'CALL',
'NEW_JWT_TOKEN',
'TYPEBOT_START',
@@ -243,6 +308,67 @@ export class InstanceController {
}
}
let sqsEvents: string[];
if (sqs_enabled) {
this.logger.verbose('creating sqs');
try {
let newEvents: string[] = [];
if (sqs_events.length === 0) {
newEvents = [
'APPLICATION_STARTUP',
'QRCODE_UPDATED',
'MESSAGES_SET',
'MESSAGES_UPSERT',
'MESSAGES_UPDATE',
'MESSAGES_DELETE',
'SEND_MESSAGE',
'CONTACTS_SET',
'CONTACTS_UPSERT',
'CONTACTS_UPDATE',
'PRESENCE_UPDATE',
'CHATS_SET',
'CHATS_UPSERT',
'CHATS_UPDATE',
'CHATS_DELETE',
'GROUPS_UPSERT',
'GROUP_UPDATE',
'GROUP_PARTICIPANTS_UPDATE',
'CONNECTION_UPDATE',
'LABELS_EDIT',
'LABELS_ASSOCIATION',
'CALL',
'NEW_JWT_TOKEN',
'TYPEBOT_START',
'TYPEBOT_CHANGE_STATUS',
'CHAMA_AI_ACTION',
];
} else {
newEvents = sqs_events;
}
this.sqsService.create(instance, {
enabled: true,
events: newEvents,
});
sqsEvents = (await this.sqsService.find(instance)).events;
} catch (error) {
this.logger.log(error);
}
}
if (proxy) {
const testProxy = await this.proxyService.testProxy(proxy);
if (!testProxy) {
throw new BadRequestException('Invalid proxy');
}
await this.proxyService.createProxy(instance, {
enabled: true,
proxy,
});
}
if (typebot_url) {
try {
if (!isURL(typebot_url, { require_tld: false })) {
@@ -270,22 +396,40 @@ 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,
sync_full_history: sync_full_history ?? false,
};
this.logger.verbose('settings: ' + JSON.stringify(settings));
this.settingsService.create(instance, settings);
let webhook_wa_business = null,
access_token_wa_business = '';
if (integration === Integration.WHATSAPP_BUSINESS) {
if (!number) {
throw new BadRequestException('number is required');
}
const urlServer = this.configService.get<HttpServer>('SERVER').URL;
webhook_wa_business = `${urlServer}/webhook/whatsapp/${encodeURIComponent(instance.instanceName)}`;
access_token_wa_business = this.configService.get<WaBusiness>('WA_BUSINESS').TOKEN_WEBHOOK;
}
this.integrationService.create(instance, {
integration,
number,
token,
});
if (!chatwoot_account_id || !chatwoot_token || !chatwoot_url) {
let getQrcode: wa.QrCode;
if (qrcode) {
this.logger.verbose('creating qrcode');
await instance.connectToWhatsapp(number);
await instance.connectToWhatsapp(number, mobile);
await delay(5000);
getQrcode = instance.qrCode;
}
@@ -293,6 +437,10 @@ export class InstanceController {
const result = {
instance: {
instanceName: instance.instanceName,
instanceId: instanceId,
integration: integration,
webhook_wa_business,
access_token_wa_business,
status: 'created',
},
hash,
@@ -310,6 +458,10 @@ export class InstanceController {
enabled: rabbitmq_enabled,
events: rabbitmqEvents,
},
sqs: {
enabled: sqs_enabled,
events: sqsEvents,
},
typebot: {
enabled: typebot_url ? true : false,
url: typebot_url,
@@ -367,19 +519,16 @@ export class InstanceController {
token: chatwoot_token,
url: chatwoot_url,
sign_msg: chatwoot_sign_msg || false,
name_inbox: instance.instanceName.split('-cwId-')[0],
name_inbox: chatwoot_name_inbox ?? instance.instanceName.split('-cwId-')[0],
number,
reopen_conversation: chatwoot_reopen_conversation || false,
conversation_pending: chatwoot_conversation_pending || false,
import_contacts: chatwoot_import_contacts ?? true,
merge_brazil_contacts: chatwoot_merge_brazil_contacts ?? false,
import_messages: chatwoot_import_messages ?? true,
days_limit_import_messages: chatwoot_days_limit_import_messages ?? 60,
auto_create: true,
});
this.chatwootService.initInstanceChatwoot(
instance,
instance.instanceName.split('-cwId-')[0],
`${urlServer}/chatwoot/webhook/${encodeURIComponent(instance.instanceName)}`,
qrcode,
number,
);
} catch (error) {
this.logger.log(error);
}
@@ -387,6 +536,10 @@ export class InstanceController {
return {
instance: {
instanceName: instance.instanceName,
instanceId: instanceId,
integration: integration,
webhook_wa_business,
access_token_wa_business,
status: 'created',
},
hash,
@@ -404,6 +557,10 @@ export class InstanceController {
enabled: rabbitmq_enabled,
events: rabbitmqEvents,
},
sqs: {
enabled: sqs_enabled,
events: sqsEvents,
},
typebot: {
enabled: typebot_url ? true : false,
url: typebot_url,
@@ -423,8 +580,12 @@ export class InstanceController {
sign_msg: chatwoot_sign_msg || false,
reopen_conversation: chatwoot_reopen_conversation || false,
conversation_pending: chatwoot_conversation_pending || false,
merge_brazil_contacts: chatwoot_merge_brazil_contacts ?? false,
import_contacts: chatwoot_import_contacts ?? true,
import_messages: chatwoot_import_messages ?? true,
days_limit_import_messages: chatwoot_days_limit_import_messages || 60,
number,
name_inbox: instance.instanceName,
name_inbox: chatwoot_name_inbox ?? instance.instanceName,
webhook_url: `${urlServer}/chatwoot/webhook/${encodeURIComponent(instance.instanceName)}`,
},
};
@@ -434,7 +595,7 @@ export class InstanceController {
}
}
public async connectToWhatsapp({ instanceName, number = null }: InstanceDto) {
public async connectToWhatsapp({ instanceName, number = null, mobile = null }: InstanceDto) {
try {
this.logger.verbose('requested connectToWhatsapp from ' + instanceName + ' instance');
@@ -457,7 +618,7 @@ export class InstanceController {
if (state == 'close') {
this.logger.verbose('connecting');
await instance.connectToWhatsapp(number);
await instance.connectToWhatsapp(number, mobile);
await delay(5000);
return instance.qrCode;
@@ -485,6 +646,7 @@ export class InstanceController {
switch (state) {
case 'open':
this.logger.verbose('logging out instance: ' + instanceName);
instance.clearCacheChatwoot();
await instance.reloadConnection();
await delay(2000);
@@ -497,6 +659,20 @@ export class InstanceController {
}
}
public async registerMobileCode({ instanceName }: InstanceDto, { mobileCode }: any) {
try {
this.logger.verbose('requested registerMobileCode from ' + instanceName + ' instance');
const instance = this.waMonitor.waInstances[instanceName];
console.log('mobileCode', mobileCode);
await instance.receiveMobileCode(mobileCode);
return { status: 'SUCCESS', error: false, response: { message: 'Mobile code registered' } };
} catch (error) {
this.logger.error(error);
}
}
public async connectionState({ instanceName }: InstanceDto) {
this.logger.verbose('requested connectionState from ' + instanceName + ' instance');
return {
@@ -507,17 +683,39 @@ export class InstanceController {
};
}
public async fetchInstances({ instanceName }: InstanceDto) {
if (instanceName) {
this.logger.verbose('requested fetchInstances from ' + instanceName + ' instance');
this.logger.verbose('instanceName: ' + instanceName);
return this.waMonitor.instanceInfo(instanceName);
public async fetchInstances({ instanceName, instanceId, number }: InstanceDto, key: string) {
const env = this.configService.get<Auth>('AUTHENTICATION').API_KEY;
let name = instanceName;
let arrayReturn = false;
if (env.KEY !== key) {
const instanceByKey = await this.repository.auth.findByKey(key);
if (instanceByKey) {
name = instanceByKey._id;
arrayReturn = true;
} else {
throw new UnauthorizedException();
}
}
if (name) {
this.logger.verbose('requested fetchInstances from ' + name + ' instance');
this.logger.verbose('instanceName: ' + name);
return this.waMonitor.instanceInfo(name, arrayReturn);
} else if (instanceId || number) {
return this.waMonitor.instanceInfoById(instanceId, number);
}
this.logger.verbose('requested fetchInstances (all instances)');
return this.waMonitor.instanceInfo();
}
public async setPresence({ instanceName }: InstanceDto, data: SetPresenceDto) {
this.logger.verbose('requested sendPresence from ' + instanceName + ' instance');
return await this.waMonitor.waInstances[instanceName].setPresence(data);
}
public async logout({ instanceName }: InstanceDto) {
this.logger.verbose('requested logout from ' + instanceName + ' instance');
const { instance } = await this.connectionState({ instanceName });
@@ -527,11 +725,7 @@ export class InstanceController {
}
try {
this.logger.verbose('logging out instance: ' + instanceName);
await this.waMonitor.waInstances[instanceName]?.client?.logout('Log out instance: ' + instanceName);
this.logger.verbose('close connection instance: ' + instanceName);
this.waMonitor.waInstances[instanceName]?.client?.ws?.close();
this.waMonitor.waInstances[instanceName]?.logoutInstance();
return { status: 'SUCCESS', error: false, response: { message: 'Instance logged out' } };
} catch (error) {
@@ -548,20 +742,28 @@ export class InstanceController {
}
try {
this.waMonitor.waInstances[instanceName]?.removeRabbitmqQueues();
this.waMonitor.waInstances[instanceName]?.clearCacheChatwoot();
if (instance.state === 'connecting') {
this.logger.verbose('logging out instance: ' + instanceName);
await this.logout({ instanceName });
delete this.waMonitor.waInstances[instanceName];
return { status: 'SUCCESS', error: false, response: { message: 'Instance deleted' } };
} else {
this.logger.verbose('deleting instance: ' + instanceName);
delete this.waMonitor.waInstances[instanceName];
this.eventEmitter.emit('remove.instance', instanceName, 'inner');
return { status: 'SUCCESS', error: false, response: { message: 'Instance deleted' } };
}
this.logger.verbose('deleting instance: ' + instanceName);
try {
this.waMonitor.waInstances[instanceName]?.sendDataWebhook(Events.INSTANCE_DELETE, {
instanceName,
instanceId: (await this.repository.auth.find(instanceName))?.instanceId,
});
} catch (error) {
this.logger.error(error);
}
delete this.waMonitor.waInstances[instanceName];
this.eventEmitter.emit('remove.instance', instanceName, 'inner');
return { status: 'SUCCESS', error: false, response: { message: 'Instance deleted' } };
} catch (error) {
throw new BadRequestException(error.toString());
}

View File

@@ -0,0 +1,20 @@
import { Logger } from '../../config/logger.config';
import { InstanceDto } from '../dto/instance.dto';
import { HandleLabelDto } from '../dto/label.dto';
import { WAMonitoringService } from '../services/monitor.service';
const logger = new Logger('LabelController');
export class LabelController {
constructor(private readonly waMonitor: WAMonitoringService) {}
public async fetchLabels({ instanceName }: InstanceDto) {
logger.verbose('requested fetchLabels from ' + instanceName + ' instance');
return await this.waMonitor.waInstances[instanceName].fetchLabels();
}
public async handleLabel({ instanceName }: InstanceDto, data: HandleLabelDto) {
logger.verbose('requested chat label change from ' + instanceName + ' instance');
return await this.waMonitor.waInstances[instanceName].handleLabel(data);
}
}

View File

@@ -0,0 +1,72 @@
import axios from 'axios';
import { Logger } from '../../config/logger.config';
import { BadRequestException, NotFoundException } from '../../exceptions';
import { makeProxyAgent } from '../../utils/makeProxyAgent';
import { InstanceDto } from '../dto/instance.dto';
import { ProxyDto } from '../dto/proxy.dto';
import { WAMonitoringService } from '../services/monitor.service';
import { ProxyService } from '../services/proxy.service';
const logger = new Logger('ProxyController');
export class ProxyController {
constructor(private readonly proxyService: ProxyService, private readonly waMonitor: WAMonitoringService) {}
public async createProxy(instance: InstanceDto, data: ProxyDto) {
logger.verbose('requested createProxy from ' + instance.instanceName + ' instance');
if (!this.waMonitor.waInstances[instance.instanceName]) {
throw new NotFoundException(`The "${instance.instanceName}" instance does not exist`);
}
if (!data.enabled) {
logger.verbose('proxy disabled');
data.proxy = null;
}
if (data.proxy) {
const testProxy = await this.testProxy(data.proxy);
if (!testProxy) {
throw new BadRequestException('Invalid proxy');
}
logger.verbose('proxy enabled');
}
return this.proxyService.create(instance, data);
}
public async findProxy(instance: InstanceDto) {
logger.verbose('requested findProxy from ' + instance.instanceName + ' instance');
if (!this.waMonitor.waInstances[instance.instanceName]) {
throw new NotFoundException(`The "${instance.instanceName}" instance does not exist`);
}
return this.proxyService.find(instance);
}
public async testProxy(proxy: ProxyDto['proxy']) {
logger.verbose('requested testProxy');
try {
const serverIp = await axios.get('https://icanhazip.com/');
const response = await axios.get('https://icanhazip.com/', {
httpsAgent: makeProxyAgent(proxy),
});
logger.verbose('[testProxy] from IP: ' + response?.data + ' To IP: ' + serverIp?.data);
return response?.data !== serverIp?.data;
} catch (error) {
if (axios.isAxiosError(error) && error.response?.data) {
logger.error('testProxy error: ' + error.response.data);
} else if (axios.isAxiosError(error)) {
logger.error('testProxy error: ');
logger.verbose(error.cause ?? error.message);
} else {
logger.error('testProxy error: ');
logger.verbose(error);
}
return false;
}
}
}

View File

@@ -14,6 +14,7 @@ import {
SendReactionDto,
SendStatusDto,
SendStickerDto,
SendTemplateDto,
SendTextDto,
} from '../dto/sendMessage.dto';
import { WAMonitoringService } from '../services/monitor.service';
@@ -28,6 +29,11 @@ export class SendMessageController {
return await this.waMonitor.waInstances[instanceName].textMessage(data);
}
public async sendTemplate({ instanceName }: InstanceDto, data: SendTemplateDto) {
logger.verbose('requested sendList from ' + instanceName + ' instance');
return await this.waMonitor.waInstances[instanceName].templateMessage(data);
}
public async sendMedia({ instanceName }: InstanceDto, data: SendMediaDto) {
logger.verbose('requested sendMedia from ' + instanceName + ' instance');

View File

@@ -1,7 +1,4 @@
// import { isURL } from 'class-validator';
import { Logger } from '../../config/logger.config';
// import { BadRequestException } from '../../exceptions';
import { InstanceDto } from '../dto/instance.dto';
import { SettingsDto } from '../dto/settings.dto';
import { SettingsService } from '../services/settings.service';
@@ -19,6 +16,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;
}
}

View File

@@ -4,12 +4,13 @@ import { Logger } from '../../config/logger.config';
import { BadRequestException } from '../../exceptions';
import { InstanceDto } from '../dto/instance.dto';
import { WebhookDto } from '../dto/webhook.dto';
import { WAMonitoringService } from '../services/monitor.service';
import { WebhookService } from '../services/webhook.service';
const logger = new Logger('WebhookController');
export class WebhookController {
constructor(private readonly webhookService: WebhookService) {}
constructor(private readonly webhookService: WebhookService, private readonly waMonitor: WAMonitoringService) {}
public async createWebhook(instance: InstanceDto, data: WebhookDto) {
logger.verbose('requested createWebhook from ' + instance.instanceName + ' instance');
@@ -46,6 +47,8 @@ export class WebhookController {
'GROUP_UPDATE',
'GROUP_PARTICIPANTS_UPDATE',
'CONNECTION_UPDATE',
'LABELS_EDIT',
'LABELS_ASSOCIATION',
'CALL',
'NEW_JWT_TOKEN',
'TYPEBOT_START',
@@ -61,4 +64,9 @@ export class WebhookController {
logger.verbose('requested findWebhook from ' + instance.instanceName + ' instance');
return this.webhookService.find(instance);
}
public async receiveWebhook(instance: InstanceDto, data: any) {
logger.verbose('requested receiveWebhook from ' + instance.instanceName + ' instance');
return await this.waMonitor.waInstances[instance.instanceName].connectToWhatsapp(data);
}
}

View File

@@ -1,7 +1,12 @@
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) {}
constructor(
public readonly jid: string,
public readonly exists: boolean,
public readonly number: string,
public readonly name?: string,
) {}
}
export class getBase64FromMediaMessageDto {
@@ -26,8 +31,12 @@ export class NumberBusiness {
message?: string;
description?: string;
email?: string;
websites?: string[];
website?: string[];
address?: string;
about?: string;
vertical?: string;
profilehandle?: string;
}
export class ProfileNameDto {
@@ -64,6 +73,11 @@ export class ArchiveChatDto {
archive: boolean;
}
export class MarkChatUnreadDto {
lastMessage?: LastMessage;
chat?: string;
}
class PrivacySetting {
readreceipts: WAReadReceiptsValue;
profile: WAPrivacyValue;
@@ -83,3 +97,31 @@ export class DeleteMessage {
remoteJid: string;
participant?: string;
}
export class Options {
delay?: number;
presence?: WAPresence;
}
class OptionsMessage {
options: Options;
}
export class Metadata extends OptionsMessage {
number: string;
}
export class SendPresenceDto extends Metadata {
options: {
presence: WAPresence;
delay: number;
};
}
export class UpdateMessageDto extends Metadata {
number: string;
key: proto.IMessageKey;
text: string;
}
export class BlockUserDto {
number: string;
status: 'block' | 'unblock';
}

View File

@@ -32,6 +32,10 @@ export class GroupInvite {
inviteCode: string;
}
export class AcceptGroupInvite {
inviteCode: string;
}
export class GroupSendInvite {
groupJid: string;
description: string;

View File

@@ -1,7 +1,14 @@
import { WAPresence } from '@whiskeysockets/baileys';
import { ProxyDto } from './proxy.dto';
export class InstanceDto {
instanceName: string;
instanceId?: string;
qrcode?: boolean;
number?: string;
mobile?: boolean;
integration?: string;
token?: string;
webhook?: string;
webhook_by_events?: boolean;
@@ -13,16 +20,24 @@ export class InstanceDto {
always_online?: boolean;
read_messages?: boolean;
read_status?: boolean;
sync_full_history?: boolean;
chatwoot_account_id?: string;
chatwoot_token?: string;
chatwoot_url?: string;
chatwoot_sign_msg?: boolean;
chatwoot_reopen_conversation?: boolean;
chatwoot_conversation_pending?: boolean;
chatwoot_merge_brazil_contacts?: boolean;
chatwoot_import_contacts?: boolean;
chatwoot_import_messages?: boolean;
chatwoot_days_limit_import_messages?: number;
chatwoot_name_inbox?: string;
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;
@@ -30,6 +45,9 @@ export class InstanceDto {
typebot_delay_message?: number;
typebot_unknown_message?: string;
typebot_listening_from_me?: boolean;
proxy_enabled?: boolean;
proxy_proxy?: string;
proxy?: ProxyDto['proxy'];
}
export class SetPresenceDto {
presence: WAPresence;
}

View File

@@ -0,0 +1,5 @@
export class IntegrationDto {
integration: string;
number: string;
token: string;
}

12
src/api/dto/label.dto.ts Normal file
View File

@@ -0,0 +1,12 @@
export class LabelDto {
id?: string;
name: string;
color: number;
predefinedId?: string;
}
export class HandleLabelDto {
number: string;
labelId: string;
action: 'add' | 'remove';
}

12
src/api/dto/proxy.dto.ts Normal file
View File

@@ -0,0 +1,12 @@
class Proxy {
host: string;
port: string;
protocol: string;
username?: string;
password?: string;
}
export class ProxyDto {
enabled: boolean;
proxy: Proxy;
}

View File

@@ -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;
@@ -137,6 +142,16 @@ export class ContactMessage {
email?: string;
url?: string;
}
export class TemplateMessage {
name: string;
language: string;
components: any;
}
export class SendTemplateDto extends Metadata {
templateMessage: TemplateMessage;
}
export class SendContactDto extends Metadata {
contactMessage: ContactMessage[];
}

View File

@@ -5,4 +5,5 @@ export class SettingsDto {
always_online?: boolean;
read_messages?: boolean;
read_status?: boolean;
sync_full_history?: boolean;
}

View File

@@ -3,12 +3,12 @@ import { NextFunction, Request, Response } from 'express';
import jwt from 'jsonwebtoken';
import { name } from '../../../package.json';
import { Auth, configService } from '../../config/env.config';
import { Auth, configService, Database } from '../../config/env.config';
import { Logger } from '../../config/logger.config';
import { ForbiddenException, UnauthorizedException } from '../../exceptions';
import { InstanceDto } from '../dto/instance.dto';
import { repository } from '../server.module';
import { JwtPayload } from '../services/auth.service';
import { repository } from '../whatsapp.module';
const logger = new Logger('GUARD');
@@ -58,6 +58,11 @@ async function jwtGuard(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');
const db = configService.get<Database>('DATABASE');
if (!key) {
throw new UnauthorizedException();
}
if (env.KEY === key) {
return next();
@@ -66,12 +71,21 @@ async function apikey(req: Request, _: Response, next: NextFunction) {
if ((req.originalUrl.includes('/instance/create') || req.originalUrl.includes('/instance/fetchInstances')) && !key) {
throw new ForbiddenException('Missing global api key', 'The global api key must be set');
}
const param = req.params as unknown as InstanceDto;
try {
const param = req.params as unknown as InstanceDto;
const instanceKey = await repository.auth.find(param.instanceName);
if (instanceKey.apikey === key) {
return next();
if (param?.instanceName) {
const instanceKey = await repository.auth.find(param.instanceName);
if (instanceKey?.apikey === key) {
return next();
}
} else {
if (req.originalUrl.includes('/instance/fetchInstances') && db.ENABLED) {
const instanceByKey = await repository.auth.findByKey(key);
if (instanceByKey) {
return next();
}
}
}
} catch (error) {
logger.error(error);

View File

@@ -2,7 +2,7 @@ import { NextFunction, Request, Response } from 'express';
import { existsSync } from 'fs';
import { join } from 'path';
import { configService, Database, Redis } from '../../config/env.config';
import { CacheConf, configService, Database } from '../../config/env.config';
import { INSTANCE_DIR } from '../../config/path.config';
import {
BadRequestException,
@@ -12,17 +12,18 @@ import {
} from '../../exceptions';
import { dbserver } from '../../libs/db.connect';
import { InstanceDto } from '../dto/instance.dto';
import { cache, waMonitor } from '../whatsapp.module';
import { cache, waMonitor } from '../server.module';
async function getInstance(instanceName: string) {
try {
const db = configService.get<Database>('DATABASE');
const redisConf = configService.get<Redis>('REDIS');
const cacheConf = configService.get<CacheConf>('CACHE');
const exists = !!waMonitor.waInstances[instanceName];
if (redisConf.ENABLED) {
const keyExists = await cache.keyExists();
if (cacheConf.REDIS.ENABLED && cacheConf.REDIS.SAVE_INSTANCES) {
const keyExists = await cache.has(instanceName);
return exists || keyExists;
}

View File

@@ -1,6 +1,6 @@
import { Logger } from '../../config/logger.config';
import { Logger } from '../../../../config/logger.config';
import { InstanceDto } from '../../../dto/instance.dto';
import { ChamaaiDto } from '../dto/chamaai.dto';
import { InstanceDto } from '../dto/instance.dto';
import { ChamaaiService } from '../services/chamaai.service';
const logger = new Logger('ChamaaiController');

View File

@@ -1,6 +1,6 @@
import { Schema } from 'mongoose';
import { dbserver } from '../../libs/db.connect';
import { dbserver } from '../../../../libs/db.connect';
export class ChamaaiRaw {
_id?: string;

View File

@@ -1,10 +1,10 @@
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';
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) {

View File

@@ -1,12 +1,12 @@
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 { Logger } from '../../../../config/logger.config';
import { chamaaiSchema, instanceNameSchema } from '../../../../validate/validate.schema';
import { RouterBroker } from '../../../abstract/abstract.router';
import { InstanceDto } from '../../../dto/instance.dto';
import { HttpStatus } from '../../../routes/index.router';
import { chamaaiController } from '../../../server.module';
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');

View File

@@ -2,13 +2,13 @@ import axios from 'axios';
import { writeFileSync } from 'fs';
import path from 'path';
import { ConfigService, HttpServer } from '../../config/env.config';
import { Logger } from '../../config/logger.config';
import { ConfigService, HttpServer } from '../../../../config/env.config';
import { Logger } from '../../../../config/logger.config';
import { InstanceDto } from '../../../dto/instance.dto';
import { ChamaaiRaw } from '../../../models';
import { WAMonitoringService } from '../../../services/monitor.service';
import { Events } from '../../../types/wa.types';
import { ChamaaiDto } from '../dto/chamaai.dto';
import { InstanceDto } from '../dto/instance.dto';
import { ChamaaiRaw } from '../models';
import { Events } from '../types/wa.types';
import { WAMonitoringService } from './monitor.service';
export class ChamaaiService {
constructor(private readonly waMonitor: WAMonitoringService, private readonly configService: ConfigService) {}

View File

@@ -0,0 +1,35 @@
import { JSONSchema7 } from 'json-schema';
import { v4 } from 'uuid';
const isNotEmpty = (...propertyNames: string[]): JSONSchema7 => {
const properties = {};
propertyNames.forEach(
(property) =>
(properties[property] = {
minLength: 1,
description: `The "${property}" cannot be empty`,
}),
);
return {
if: {
propertyNames: {
enum: [...propertyNames],
},
},
then: { properties },
};
};
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'),
};

View File

@@ -1,17 +1,24 @@
import { isURL } from 'class-validator';
import { ConfigService, HttpServer } from '../../config/env.config';
import { Logger } from '../../config/logger.config';
import { BadRequestException } from '../../exceptions';
import { CacheEngine } from '../../../../cache/cacheengine';
import { ConfigService, HttpServer } from '../../../../config/env.config';
import { Logger } from '../../../../config/logger.config';
import { BadRequestException } from '../../../../exceptions';
import { InstanceDto } from '../../../dto/instance.dto';
import { RepositoryBroker } from '../../../repository/repository.manager';
import { waMonitor } from '../../../server.module';
import { CacheService } from '../../../services/cache.service';
import { ChatwootDto } from '../dto/chatwoot.dto';
import { InstanceDto } from '../dto/instance.dto';
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 +39,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,13 +48,22 @@ export class ChatwootController {
data.token = '';
data.url = '';
data.sign_msg = false;
data.sign_delimiter = null;
data.reopen_conversation = false;
data.conversation_pending = false;
data.import_contacts = false;
data.import_messages = false;
data.merge_brazil_contacts = false;
data.days_limit_import_messages = 0;
data.auto_create = false;
data.name_inbox = '';
}
data.name_inbox = instance.instanceName;
if (!data.name_inbox || data.name_inbox === '') {
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;
@@ -64,7 +81,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: '',
@@ -86,7 +103,9 @@ 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 chatwootCache = new CacheService(new CacheEngine(this.configService, ChatwootService.name).getEngine());
const chatwootService = new ChatwootService(waMonitor, this.configService, this.repository, chatwootCache);
return chatwootService.receiveWebhook(instance, data);
}

View File

@@ -5,7 +5,13 @@ export class ChatwootDto {
url?: string;
name_inbox?: string;
sign_msg?: boolean;
sign_delimiter?: string;
number?: string;
reopen_conversation?: boolean;
conversation_pending?: boolean;
merge_brazil_contacts?: boolean;
import_contacts?: boolean;
import_messages?: boolean;
days_limit_import_messages?: number;
auto_create?: boolean;
}

View File

@@ -0,0 +1,49 @@
import postgresql from 'pg';
import { Chatwoot, configService } from '../../../../config/env.config';
import { Logger } from '../../../../config/logger.config';
const { Pool } = postgresql;
class Postgres {
private logger = new Logger(Postgres.name);
private pool;
private connected = false;
getConnection(connectionString: string) {
if (this.connected) {
return this.pool;
} else {
this.pool = new Pool({
connectionString,
ssl: {
rejectUnauthorized: false,
},
});
this.pool.on('error', () => {
this.logger.error('postgres disconnected');
this.connected = false;
});
try {
this.logger.verbose('connecting new postgres');
this.connected = true;
} catch (e) {
this.connected = false;
this.logger.error('postgres connect exception caught: ' + e);
return null;
}
return this.pool;
}
}
getChatwootConnection() {
const uri = configService.get<Chatwoot>('CHATWOOT').IMPORT.DATABASE.CONNECTION.URI;
return this.getConnection(uri);
}
}
export const postgresClient = new Postgres();

View File

@@ -1,6 +1,6 @@
import { Schema } from 'mongoose';
import { dbserver } from '../../libs/db.connect';
import { dbserver } from '../../../../libs/db.connect';
export class ChatwootRaw {
_id?: string;
@@ -10,9 +10,14 @@ export class ChatwootRaw {
url?: string;
name_inbox?: string;
sign_msg?: boolean;
sign_delimiter?: string;
number?: string;
reopen_conversation?: boolean;
conversation_pending?: boolean;
merge_brazil_contacts?: boolean;
import_contacts?: boolean;
import_messages?: boolean;
days_limit_import_messages?: number;
}
const chatwootSchema = new Schema<ChatwootRaw>({
@@ -23,9 +28,14 @@ const chatwootSchema = new Schema<ChatwootRaw>({
url: { type: String, required: true },
name_inbox: { type: String, required: true },
sign_msg: { type: Boolean, required: true },
sign_delimiter: { type: String, required: false },
number: { type: String, required: true },
reopen_conversation: { type: Boolean, required: true },
conversation_pending: { type: Boolean, required: true },
merge_brazil_contacts: { type: Boolean, required: true },
import_contacts: { type: Boolean, required: true },
import_messages: { type: Boolean, required: true },
days_limit_import_messages: { type: Number, required: true },
});
export const ChatwootModel = dbserver?.model(ChatwootRaw.name, chatwootSchema, 'chatwoot');

View File

@@ -1,10 +1,10 @@
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 { ChatwootRaw, IChatwootModel } from '../models';
import { ConfigService } from '../../../../config/env.config';
import { Logger } from '../../../../config/logger.config';
import { IInsert, Repository } from '../../../abstract/abstract.repository';
import { ChatwootRaw, IChatwootModel } from '../../../models';
export class ChatwootRepository extends Repository {
constructor(private readonly chatwootModel: IChatwootModel, private readonly configService: ConfigService) {

View File

@@ -1,13 +1,12 @@
import { RequestHandler, Router } from 'express';
import { Logger } from '../../config/logger.config';
import { chatwootSchema, instanceNameSchema } from '../../validate/validate.schema';
import { RouterBroker } from '../abstract/abstract.router';
import { Logger } from '../../../../config/logger.config';
import { chatwootSchema, instanceNameSchema } from '../../../../validate/validate.schema';
import { RouterBroker } from '../../../abstract/abstract.router';
import { InstanceDto } from '../../../dto/instance.dto';
import { HttpStatus } from '../../../routes/index.router';
import { chatwootController } from '../../../server.module';
import { ChatwootDto } from '../dto/chatwoot.dto';
import { InstanceDto } from '../dto/instance.dto';
// import { ChatwootService } from '../services/chatwoot.service';
import { chatwootController } from '../whatsapp.module';
import { HttpStatus } from './index.router';
const logger = new Logger('ChatwootRouter');

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,472 @@
import { inbox } from '@figuro/chatwoot-sdk';
import { proto } from '@whiskeysockets/baileys';
import { InstanceDto } from '../../../../api/dto/instance.dto';
import { ChatwootRaw, ContactRaw, MessageRaw } from '../../../../api/models';
import { Chatwoot, configService } from '../../../../config/env.config';
import { Logger } from '../../../../config/logger.config';
import { postgresClient } from '../libs/postgres.client';
import { ChatwootService } from '../services/chatwoot.service';
type ChatwootUser = {
user_type: string;
user_id: number;
};
type FksChatwoot = {
phone_number: string;
contact_id: string;
conversation_id: string;
};
type firstLastTimestamp = {
first: number;
last: number;
};
type IWebMessageInfo = Omit<proto.IWebMessageInfo, 'key'> & Partial<Pick<proto.IWebMessageInfo, 'key'>>;
class ChatwootImport {
private logger = new Logger(ChatwootImport.name);
private repositoryMessagesCache = new Map<string, Set<string>>();
private historyMessages = new Map<string, MessageRaw[]>();
private historyContacts = new Map<string, ContactRaw[]>();
public getRepositoryMessagesCache(instance: InstanceDto) {
return this.repositoryMessagesCache.has(instance.instanceName)
? this.repositoryMessagesCache.get(instance.instanceName)
: null;
}
public setRepositoryMessagesCache(instance: InstanceDto, repositoryMessagesCache: Set<string>) {
this.repositoryMessagesCache.set(instance.instanceName, repositoryMessagesCache);
}
public deleteRepositoryMessagesCache(instance: InstanceDto) {
this.repositoryMessagesCache.delete(instance.instanceName);
}
public addHistoryMessages(instance: InstanceDto, messagesRaw: MessageRaw[]) {
const actualValue = this.historyMessages.has(instance.instanceName)
? this.historyMessages.get(instance.instanceName)
: [];
this.historyMessages.set(instance.instanceName, actualValue.concat(messagesRaw));
}
public addHistoryContacts(instance: InstanceDto, contactsRaw: ContactRaw[]) {
const actualValue = this.historyContacts.has(instance.instanceName)
? this.historyContacts.get(instance.instanceName)
: [];
this.historyContacts.set(instance.instanceName, actualValue.concat(contactsRaw));
}
public deleteHistoryMessages(instance: InstanceDto) {
this.historyMessages.delete(instance.instanceName);
}
public deleteHistoryContacts(instance: InstanceDto) {
this.historyContacts.delete(instance.instanceName);
}
public clearAll(instance: InstanceDto) {
this.deleteRepositoryMessagesCache(instance);
this.deleteHistoryMessages(instance);
this.deleteHistoryContacts(instance);
}
public getHistoryMessagesLenght(instance: InstanceDto) {
return this.historyMessages.get(instance.instanceName)?.length ?? 0;
}
public async importHistoryContacts(instance: InstanceDto, provider: ChatwootRaw) {
try {
if (this.getHistoryMessagesLenght(instance) > 0) {
return;
}
const pgClient = postgresClient.getChatwootConnection();
let totalContactsImported = 0;
const contacts = this.historyContacts.get(instance.instanceName) || [];
if (contacts.length === 0) {
return 0;
}
let contactsChunk: ContactRaw[] = this.sliceIntoChunks(contacts, 3000);
while (contactsChunk.length > 0) {
// inserting contacts in chatwoot db
let sqlInsert = `INSERT INTO contacts
(name, phone_number, account_id, identifier, created_at, updated_at) VALUES `;
const bindInsert = [provider.account_id];
for (const contact of contactsChunk) {
bindInsert.push(contact.pushName);
const bindName = `$${bindInsert.length}`;
bindInsert.push(`+${contact.id.split('@')[0]}`);
const bindPhoneNumber = `$${bindInsert.length}`;
bindInsert.push(contact.id);
const bindIdentifier = `$${bindInsert.length}`;
sqlInsert += `(${bindName}, ${bindPhoneNumber}, $1, ${bindIdentifier}, NOW(), NOW()),`;
}
if (sqlInsert.slice(-1) === ',') {
sqlInsert = sqlInsert.slice(0, -1);
}
sqlInsert += ` ON CONFLICT (identifier, account_id)
DO UPDATE SET
name = EXCLUDED.name,
phone_number = EXCLUDED.phone_number,
identifier = EXCLUDED.identifier`;
totalContactsImported += (await pgClient.query(sqlInsert, bindInsert))?.rowCount ?? 0;
contactsChunk = this.sliceIntoChunks(contacts, 3000);
}
this.deleteHistoryContacts(instance);
return totalContactsImported;
} catch (error) {
this.logger.error(`Error on import history contacts: ${error.toString()}`);
}
}
public async importHistoryMessages(
instance: InstanceDto,
chatwootService: ChatwootService,
inbox: inbox,
provider: ChatwootRaw,
) {
try {
const pgClient = postgresClient.getChatwootConnection();
const chatwootUser = await this.getChatwootUser(provider);
if (!chatwootUser) {
throw new Error('User not found to import messages.');
}
let totalMessagesImported = 0;
const messagesOrdered = this.historyMessages.get(instance.instanceName) || [];
if (messagesOrdered.length === 0) {
return 0;
}
// ordering messages by number and timestamp asc
messagesOrdered.sort((a, b) => {
return (
parseInt(a.key.remoteJid) - parseInt(b.key.remoteJid) ||
(a.messageTimestamp as number) - (b.messageTimestamp as number)
);
});
const allMessagesMappedByPhoneNumber = this.createMessagesMapByPhoneNumber(messagesOrdered);
// Map structure: +552199999999 => { first message timestamp from number, last message timestamp from number}
const phoneNumbersWithTimestamp = new Map<string, firstLastTimestamp>();
allMessagesMappedByPhoneNumber.forEach((messages: MessageRaw[], phoneNumber: string) => {
phoneNumbersWithTimestamp.set(phoneNumber, {
first: messages[0]?.messageTimestamp as number,
last: messages[messages.length - 1]?.messageTimestamp as number,
});
});
// processing messages in batch
const batchSize = 4000;
let messagesChunk: MessageRaw[] = this.sliceIntoChunks(messagesOrdered, batchSize);
while (messagesChunk.length > 0) {
// Map structure: +552199999999 => MessageRaw[]
const messagesByPhoneNumber = this.createMessagesMapByPhoneNumber(messagesChunk);
if (messagesByPhoneNumber.size > 0) {
const fksByNumber = await this.selectOrCreateFksFromChatwoot(
provider,
inbox,
phoneNumbersWithTimestamp,
messagesByPhoneNumber,
);
// inserting messages in chatwoot db
let sqlInsertMsg = `INSERT INTO messages
(content, account_id, inbox_id, conversation_id, message_type, private, content_type,
sender_type, sender_id, created_at, updated_at) VALUES `;
const bindInsertMsg = [provider.account_id, inbox.id];
messagesByPhoneNumber.forEach((messages: MessageRaw[], phoneNumber: string) => {
const fksChatwoot = fksByNumber.get(phoneNumber);
messages.forEach((message) => {
if (!message.message) {
return;
}
if (!fksChatwoot?.conversation_id || !fksChatwoot?.contact_id) {
return;
}
const contentMessage = this.getContentMessage(chatwootService, message);
if (!contentMessage) {
return;
}
bindInsertMsg.push(contentMessage);
const bindContent = `$${bindInsertMsg.length}`;
bindInsertMsg.push(fksChatwoot.conversation_id);
const bindConversationId = `$${bindInsertMsg.length}`;
bindInsertMsg.push(message.key.fromMe ? '1' : '0');
const bindMessageType = `$${bindInsertMsg.length}`;
bindInsertMsg.push(message.key.fromMe ? chatwootUser.user_type : 'Contact');
const bindSenderType = `$${bindInsertMsg.length}`;
bindInsertMsg.push(message.key.fromMe ? chatwootUser.user_id : fksChatwoot.contact_id);
const bindSenderId = `$${bindInsertMsg.length}`;
bindInsertMsg.push(message.messageTimestamp as number);
const bindmessageTimestamp = `$${bindInsertMsg.length}`;
sqlInsertMsg += `(${bindContent}, $1, $2, ${bindConversationId}, ${bindMessageType}, FALSE, 0,
${bindSenderType},${bindSenderId}, to_timestamp(${bindmessageTimestamp}), to_timestamp(${bindmessageTimestamp})),`;
});
});
if (bindInsertMsg.length > 2) {
if (sqlInsertMsg.slice(-1) === ',') {
sqlInsertMsg = sqlInsertMsg.slice(0, -1);
}
totalMessagesImported += (await pgClient.query(sqlInsertMsg, bindInsertMsg))?.rowCount ?? 0;
}
}
messagesChunk = this.sliceIntoChunks(messagesOrdered, batchSize);
}
this.deleteHistoryMessages(instance);
this.deleteRepositoryMessagesCache(instance);
this.importHistoryContacts(instance, provider);
return totalMessagesImported;
} catch (error) {
this.logger.error(`Error on import history messages: ${error.toString()}`);
this.deleteHistoryMessages(instance);
this.deleteRepositoryMessagesCache(instance);
}
}
public async selectOrCreateFksFromChatwoot(
provider: ChatwootRaw,
inbox: inbox,
phoneNumbersWithTimestamp: Map<string, firstLastTimestamp>,
messagesByPhoneNumber: Map<string, MessageRaw[]>,
): Promise<Map<string, FksChatwoot>> {
const pgClient = postgresClient.getChatwootConnection();
const bindValues = [provider.account_id, inbox.id];
const phoneNumberBind = Array.from(messagesByPhoneNumber.keys())
.map((phoneNumber) => {
const phoneNumberTimestamp = phoneNumbersWithTimestamp.get(phoneNumber);
if (phoneNumberTimestamp) {
bindValues.push(phoneNumber);
let bindStr = `($${bindValues.length},`;
bindValues.push(phoneNumberTimestamp.first);
bindStr += `$${bindValues.length},`;
bindValues.push(phoneNumberTimestamp.last);
return `${bindStr}$${bindValues.length})`;
}
})
.join(',');
// select (or insert when necessary) data from tables contacts, contact_inboxes, conversations from chatwoot db
const sqlFromChatwoot = `WITH
phone_number AS (
SELECT phone_number, created_at::INTEGER, last_activity_at::INTEGER FROM (
VALUES
${phoneNumberBind}
) as t (phone_number, created_at, last_activity_at)
),
only_new_phone_number AS (
SELECT * FROM phone_number
WHERE phone_number NOT IN (
SELECT phone_number
FROM contacts
JOIN contact_inboxes ci ON ci.contact_id = contacts.id AND ci.inbox_id = $2
JOIN conversations con ON con.contact_inbox_id = ci.id
AND con.account_id = $1
AND con.inbox_id = $2
AND con.contact_id = contacts.id
WHERE contacts.account_id = $1
)
),
new_contact AS (
INSERT INTO contacts (name, phone_number, account_id, identifier, created_at, updated_at)
SELECT REPLACE(p.phone_number, '+', ''), p.phone_number, $1, CONCAT(REPLACE(p.phone_number, '+', ''),
'@s.whatsapp.net'), to_timestamp(p.created_at), to_timestamp(p.last_activity_at)
FROM only_new_phone_number AS p
ON CONFLICT(identifier, account_id) DO UPDATE SET updated_at = EXCLUDED.updated_at
RETURNING id, phone_number, created_at, updated_at
),
new_contact_inbox AS (
INSERT INTO contact_inboxes (contact_id, inbox_id, source_id, created_at, updated_at)
SELECT new_contact.id, $2, gen_random_uuid(), new_contact.created_at, new_contact.updated_at
FROM new_contact
RETURNING id, contact_id, created_at, updated_at
),
new_conversation AS (
INSERT INTO conversations (account_id, inbox_id, status, contact_id,
contact_inbox_id, uuid, last_activity_at, created_at, updated_at)
SELECT $1, $2, 0, new_contact_inbox.contact_id, new_contact_inbox.id, gen_random_uuid(),
new_contact_inbox.updated_at, new_contact_inbox.created_at, new_contact_inbox.updated_at
FROM new_contact_inbox
RETURNING id, contact_id
)
SELECT new_contact.phone_number, new_conversation.contact_id, new_conversation.id AS conversation_id
FROM new_conversation
JOIN new_contact ON new_conversation.contact_id = new_contact.id
UNION
SELECT p.phone_number, c.id contact_id, con.id conversation_id
FROM phone_number p
JOIN contacts c ON c.phone_number = p.phone_number
JOIN contact_inboxes ci ON ci.contact_id = c.id AND ci.inbox_id = $2
JOIN conversations con ON con.contact_inbox_id = ci.id AND con.account_id = $1
AND con.inbox_id = $2 AND con.contact_id = c.id`;
const fksFromChatwoot = await pgClient.query(sqlFromChatwoot, bindValues);
return new Map(fksFromChatwoot.rows.map((item: FksChatwoot) => [item.phone_number, item]));
}
public async getChatwootUser(provider: ChatwootRaw): Promise<ChatwootUser> {
try {
const pgClient = postgresClient.getChatwootConnection();
const sqlUser = `SELECT owner_type AS user_type, owner_id AS user_id
FROM access_tokens
WHERE token = $1`;
return (await pgClient.query(sqlUser, [provider.token]))?.rows[0] || false;
} catch (error) {
this.logger.error(`Error on getChatwootUser: ${error.toString()}`);
}
}
public createMessagesMapByPhoneNumber(messages: MessageRaw[]): Map<string, MessageRaw[]> {
return messages.reduce((acc: Map<string, MessageRaw[]>, message: MessageRaw) => {
if (!this.isIgnorePhoneNumber(message?.key?.remoteJid)) {
const phoneNumber = message?.key?.remoteJid?.split('@')[0];
if (phoneNumber) {
const phoneNumberPlus = `+${phoneNumber}`;
const messages = acc.has(phoneNumberPlus) ? acc.get(phoneNumberPlus) : [];
messages.push(message);
acc.set(phoneNumberPlus, messages);
}
}
return acc;
}, new Map());
}
public async getContactsOrderByRecentConversations(
inbox: inbox,
provider: ChatwootRaw,
limit = 50,
): Promise<{ id: number; phone_number: string; identifier: string }[]> {
try {
const pgClient = postgresClient.getChatwootConnection();
const sql = `SELECT contacts.id, contacts.identifier, contacts.phone_number
FROM conversations
JOIN contacts ON contacts.id = conversations.contact_id
WHERE conversations.account_id = $1
AND inbox_id = $2
ORDER BY conversations.last_activity_at DESC
LIMIT $3`;
return (await pgClient.query(sql, [provider.account_id, inbox.id, limit]))?.rows;
} catch (error) {
this.logger.error(`Error on get recent conversations: ${error.toString()}`);
}
}
public getContentMessage(chatwootService: ChatwootService, msg: IWebMessageInfo) {
const contentMessage = chatwootService.getConversationMessage(msg.message);
if (contentMessage) {
return contentMessage;
}
if (!configService.get<Chatwoot>('CHATWOOT').IMPORT.PLACEHOLDER_MEDIA_MESSAGE) {
return '';
}
const types = {
documentMessage: msg.message.documentMessage,
documentWithCaptionMessage: msg.message.documentWithCaptionMessage?.message?.documentMessage,
imageMessage: msg.message.imageMessage,
videoMessage: msg.message.videoMessage,
audioMessage: msg.message.audioMessage,
stickerMessage: msg.message.stickerMessage,
templateMessage: msg.message.templateMessage?.hydratedTemplate?.hydratedContentText,
};
const typeKey = Object.keys(types).find((key) => types[key] !== undefined);
switch (typeKey) {
case 'documentMessage':
return `_<File: ${msg.message.documentMessage.fileName}${
msg.message.documentMessage.caption ? ` ${msg.message.documentMessage.caption}` : ''
}>_`;
case 'documentWithCaptionMessage':
return `_<File: ${msg.message.documentWithCaptionMessage.message.documentMessage.fileName}${
msg.message.documentWithCaptionMessage.message.documentMessage.caption
? ` ${msg.message.documentWithCaptionMessage.message.documentMessage.caption}`
: ''
}>_`;
case 'templateMessage':
return msg.message.templateMessage.hydratedTemplate.hydratedTitleText
? `*${msg.message.templateMessage.hydratedTemplate.hydratedTitleText}*\\n`
: '' + msg.message.templateMessage.hydratedTemplate.hydratedContentText;
case 'imageMessage':
return '_<Image Message>_';
case 'videoMessage':
return '_<Video Message>_';
case 'audioMessage':
return '_<Audio Message>_';
case 'stickerMessage':
return '_<Sticker Message>_';
default:
return '';
}
}
public sliceIntoChunks(arr: any[], chunkSize: number) {
return arr.splice(0, chunkSize);
}
public isGroup(remoteJid: string) {
return remoteJid.includes('@g.us');
}
public isIgnorePhoneNumber(remoteJid: string) {
return this.isGroup(remoteJid) || remoteJid === 'status@broadcast' || remoteJid === '0@s.whatsapp.net';
}
}
export const chatwootImport = new ChatwootImport();

View File

@@ -0,0 +1,44 @@
import { JSONSchema7 } from 'json-schema';
import { v4 } from 'uuid';
const isNotEmpty = (...propertyNames: string[]): JSONSchema7 => {
const properties = {};
propertyNames.forEach(
(property) =>
(properties[property] = {
minLength: 1,
description: `The "${property}" cannot be empty`,
}),
);
return {
if: {
propertyNames: {
enum: [...propertyNames],
},
},
then: { properties },
};
};
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'] },
name_inbox: { type: ['string', 'null'] },
reopen_conversation: { type: 'boolean', enum: [true, false] },
conversation_pending: { type: 'boolean', enum: [true, false] },
auto_create: { type: 'boolean', enum: [true, false] },
import_contacts: { type: 'boolean', enum: [true, false] },
merge_brazil_contacts: { type: 'boolean', enum: [true, false] },
import_messages: { type: 'boolean', enum: [true, false] },
days_limit_import_messages: { type: 'number' },
},
required: ['enabled', 'account_id', 'token', 'url', 'sign_msg', 'reopen_conversation', 'conversation_pending'],
...isNotEmpty('account_id', 'token', 'url', 'sign_msg', 'reopen_conversation', 'conversation_pending'),
};

View File

@@ -1,5 +1,5 @@
import { Logger } from '../../config/logger.config';
import { InstanceDto } from '../dto/instance.dto';
import { Logger } from '../../../../config/logger.config';
import { InstanceDto } from '../../../dto/instance.dto';
import { RabbitmqDto } from '../dto/rabbitmq.dto';
import { RabbitmqService } from '../services/rabbitmq.service';
@@ -38,6 +38,8 @@ export class RabbitmqController {
'GROUP_UPDATE',
'GROUP_PARTICIPANTS_UPDATE',
'CONNECTION_UPDATE',
'LABELS_EDIT',
'LABELS_ASSOCIATION',
'CALL',
'NEW_JWT_TOKEN',
'TYPEBOT_START',

View File

@@ -1,7 +1,7 @@
import * as amqp from 'amqplib/callback_api';
import { configService, Rabbitmq } from '../config/env.config';
import { Logger } from '../config/logger.config';
import { configService, Rabbitmq } from '../../../../config/env.config';
import { Logger } from '../../../../config/logger.config';
const logger = new Logger('AMQP');
@@ -42,6 +42,41 @@ export const getAMQP = (): amqp.Channel | null => {
return amqpChannel;
};
export const initGlobalQueues = () => {
logger.info('Initializing global queues');
const events = configService.get<Rabbitmq>('RABBITMQ').EVENTS;
if (!events) {
logger.warn('No events to initialize on AMQP');
return;
}
const eventKeys = Object.keys(events);
eventKeys.forEach((event) => {
if (events[event] === false) return;
const queueName = `${event.replace(/_/g, '.').toLowerCase()}`;
const amqp = getAMQP();
const exchangeName = 'evolution_exchange';
amqp.assertExchange(exchangeName, 'topic', {
durable: true,
autoDelete: false,
});
amqp.assertQueue(queueName, {
durable: true,
autoDelete: false,
arguments: {
'x-queue-type': 'quorum',
},
});
amqp.bindQueue(queueName, exchangeName, event);
});
};
export const initQueues = (instanceName: string, events: string[]) => {
if (!events || !events.length) return;

View File

@@ -1,6 +1,6 @@
import { Schema } from 'mongoose';
import { dbserver } from '../../libs/db.connect';
import { dbserver } from '../../../../libs/db.connect';
export class RabbitmqRaw {
_id?: string;

View File

@@ -1,10 +1,10 @@
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';
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) {

View File

@@ -1,12 +1,12 @@
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 { 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 { HttpStatus } from '../../../routes/index.router';
import { rabbitmqController } from '../../../server.module';
import { RabbitmqDto } from '../dto/rabbitmq.dto';
import { rabbitmqController } from '../whatsapp.module';
import { HttpStatus } from './index.router';
const logger = new Logger('RabbitmqRouter');

View File

@@ -1,9 +1,9 @@
import { Logger } from '../../config/logger.config';
import { initQueues } from '../../libs/amqp.server';
import { InstanceDto } from '../dto/instance.dto';
import { Logger } from '../../../../config/logger.config';
import { InstanceDto } from '../../../dto/instance.dto';
import { RabbitmqRaw } from '../../../models';
import { WAMonitoringService } from '../../../services/monitor.service';
import { RabbitmqDto } from '../dto/rabbitmq.dto';
import { RabbitmqRaw } from '../models';
import { WAMonitoringService } from './monitor.service';
import { initQueues } from '../libs/amqp.server';
export class RabbitmqService {
constructor(private readonly waMonitor: WAMonitoringService) {}

View File

@@ -0,0 +1,66 @@
import { JSONSchema7 } from 'json-schema';
import { v4 } from 'uuid';
const isNotEmpty = (...propertyNames: string[]): JSONSchema7 => {
const properties = {};
propertyNames.forEach(
(property) =>
(properties[property] = {
minLength: 1,
description: `The "${property}" cannot be empty`,
}),
);
return {
if: {
propertyNames: {
enum: [...propertyNames],
},
},
then: { properties },
};
};
export const rabbitmqSchema: JSONSchema7 = {
$id: v4(),
type: 'object',
properties: {
enabled: { type: 'boolean', enum: [true, false] },
events: {
type: 'array',
minItems: 0,
items: {
type: 'string',
enum: [
'APPLICATION_STARTUP',
'QRCODE_UPDATED',
'MESSAGES_SET',
'MESSAGES_UPSERT',
'MESSAGES_UPDATE',
'MESSAGES_DELETE',
'SEND_MESSAGE',
'CONTACTS_SET',
'CONTACTS_UPSERT',
'CONTACTS_UPDATE',
'PRESENCE_UPDATE',
'CHATS_SET',
'CHATS_UPSERT',
'CHATS_UPDATE',
'CHATS_DELETE',
'GROUPS_UPSERT',
'GROUP_UPDATE',
'GROUP_PARTICIPANTS_UPDATE',
'CONNECTION_UPDATE',
'LABELS_EDIT',
'LABELS_ASSOCIATION',
'CALL',
'NEW_JWT_TOKEN',
'TYPEBOT_START',
'TYPEBOT_CHANGE_STATUS',
'CHAMA_AI_ACTION',
],
},
},
},
required: ['enabled'],
...isNotEmpty('enabled'),
};

View File

@@ -0,0 +1,58 @@
import { Logger } from '../../../../config/logger.config';
import { InstanceDto } from '../../../dto/instance.dto';
import { SqsDto } from '../dto/sqs.dto';
import { SqsService } from '../services/sqs.service';
const logger = new Logger('SqsController');
export class SqsController {
constructor(private readonly sqsService: SqsService) {}
public async createSqs(instance: InstanceDto, data: SqsDto) {
logger.verbose('requested createSqs from ' + instance.instanceName + ' instance');
if (!data.enabled) {
logger.verbose('sqs disabled');
data.events = [];
}
if (data.events.length === 0) {
logger.verbose('sqs events empty');
data.events = [
'APPLICATION_STARTUP',
'QRCODE_UPDATED',
'MESSAGES_SET',
'MESSAGES_UPSERT',
'MESSAGES_UPDATE',
'MESSAGES_DELETE',
'SEND_MESSAGE',
'CONTACTS_SET',
'CONTACTS_UPSERT',
'CONTACTS_UPDATE',
'PRESENCE_UPDATE',
'CHATS_SET',
'CHATS_UPSERT',
'CHATS_UPDATE',
'CHATS_DELETE',
'GROUPS_UPSERT',
'GROUP_UPDATE',
'GROUP_PARTICIPANTS_UPDATE',
'CONNECTION_UPDATE',
'LABELS_EDIT',
'LABELS_ASSOCIATION',
'CALL',
'NEW_JWT_TOKEN',
'TYPEBOT_START',
'TYPEBOT_CHANGE_STATUS',
'CHAMA_AI_ACTION',
];
}
return this.sqsService.create(instance, data);
}
public async findSqs(instance: InstanceDto) {
logger.verbose('requested findSqs from ' + instance.instanceName + ' instance');
return this.sqsService.find(instance);
}
}

View File

@@ -0,0 +1,4 @@
export class SqsDto {
enabled: boolean;
events?: string[];
}

View File

@@ -0,0 +1,100 @@
import { SQS } from '@aws-sdk/client-sqs';
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({
credentials: {
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`);
}
},
);
}
},
);
});
};

View 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;

View 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 {};
}
}
}

View File

@@ -0,0 +1,52 @@
import { RequestHandler, Router } from 'express';
import { Logger } from '../../../../config/logger.config';
import { instanceNameSchema, sqsSchema } from '../../../../validate/validate.schema';
import { RouterBroker } from '../../../abstract/abstract.router';
import { InstanceDto } from '../../../dto/instance.dto';
import { HttpStatus } from '../../../routes/index.router';
import { sqsController } from '../../../server.module';
import { SqsDto } from '../dto/sqs.dto';
const logger = new Logger('SqsRouter');
export class SqsRouter extends RouterBroker {
constructor(...guards: RequestHandler[]) {
super();
this.router
.post(this.routerPath('set'), ...guards, async (req, res) => {
logger.verbose('request received in setSqs');
logger.verbose('request body: ');
logger.verbose(req.body);
logger.verbose('request query: ');
logger.verbose(req.query);
const response = await this.dataValidate<SqsDto>({
request: req,
schema: sqsSchema,
ClassRef: SqsDto,
execute: (instance, data) => sqsController.createSqs(instance, data),
});
res.status(HttpStatus.CREATED).json(response);
})
.get(this.routerPath('find'), ...guards, async (req, res) => {
logger.verbose('request received in findSqs');
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) => sqsController.findSqs(instance),
});
res.status(HttpStatus.OK).json(response);
});
}
public readonly router = Router();
}

View File

@@ -0,0 +1,35 @@
import { Logger } from '../../../../config/logger.config';
import { InstanceDto } from '../../../dto/instance.dto';
import { SqsRaw } from '../../../models';
import { WAMonitoringService } from '../../../services/monitor.service';
import { SqsDto } from '../dto/sqs.dto';
import { initQueues } from '../libs/sqs.server';
export class SqsService {
constructor(private readonly waMonitor: WAMonitoringService) {}
private readonly logger = new Logger(SqsService.name);
public create(instance: InstanceDto, data: SqsDto) {
this.logger.verbose('create sqs: ' + instance.instanceName);
this.waMonitor.waInstances[instance.instanceName].setSqs(data);
initQueues(instance.instanceName, data.events);
return { sqs: { ...instance, sqs: data } };
}
public async find(instance: InstanceDto): Promise<SqsRaw> {
try {
this.logger.verbose('find sqs: ' + instance.instanceName);
const result = await this.waMonitor.waInstances[instance.instanceName].findSqs();
if (Object.keys(result).length === 0) {
throw new Error('Sqs not found');
}
return result;
} catch (error) {
return { enabled: false, events: [] };
}
}
}

View File

@@ -0,0 +1,66 @@
import { JSONSchema7 } from 'json-schema';
import { v4 } from 'uuid';
const isNotEmpty = (...propertyNames: string[]): JSONSchema7 => {
const properties = {};
propertyNames.forEach(
(property) =>
(properties[property] = {
minLength: 1,
description: `The "${property}" cannot be empty`,
}),
);
return {
if: {
propertyNames: {
enum: [...propertyNames],
},
},
then: { properties },
};
};
export const sqsSchema: JSONSchema7 = {
$id: v4(),
type: 'object',
properties: {
enabled: { type: 'boolean', enum: [true, false] },
events: {
type: 'array',
minItems: 0,
items: {
type: 'string',
enum: [
'APPLICATION_STARTUP',
'QRCODE_UPDATED',
'MESSAGES_SET',
'MESSAGES_UPSERT',
'MESSAGES_UPDATE',
'MESSAGES_DELETE',
'SEND_MESSAGE',
'CONTACTS_SET',
'CONTACTS_UPSERT',
'CONTACTS_UPDATE',
'PRESENCE_UPDATE',
'CHATS_SET',
'CHATS_UPSERT',
'CHATS_UPDATE',
'CHATS_DELETE',
'GROUPS_UPSERT',
'GROUP_UPDATE',
'GROUP_PARTICIPANTS_UPDATE',
'CONNECTION_UPDATE',
'LABELS_EDIT',
'LABELS_ASSOCIATION',
'CALL',
'NEW_JWT_TOKEN',
'TYPEBOT_START',
'TYPEBOT_CHANGE_STATUS',
'CHAMA_AI_ACTION',
],
},
},
},
required: ['enabled'],
...isNotEmpty('enabled'),
};

View File

@@ -1,5 +1,5 @@
import { Logger } from '../../config/logger.config';
import { InstanceDto } from '../dto/instance.dto';
import { Logger } from '../../../../config/logger.config';
import { InstanceDto } from '../../../dto/instance.dto';
import { TypebotDto } from '../dto/typebot.dto';
import { TypebotService } from '../services/typebot.service';

View File

@@ -10,6 +10,7 @@ export class Session {
export class PrefilledVariables {
remoteJid?: string;
pushName?: string;
messageType?: string;
additionalData?: { [key: string]: any };
}

View File

@@ -1,6 +1,6 @@
import { Schema } from 'mongoose';
import { dbserver } from '../../libs/db.connect';
import { dbserver } from '../../../../libs/db.connect';
class Session {
remoteJid?: string;

View File

@@ -1,10 +1,10 @@
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';
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) {

View File

@@ -1,17 +1,17 @@
import { RequestHandler, Router } from 'express';
import { Logger } from '../../config/logger.config';
import { Logger } from '../../../../config/logger.config';
import {
instanceNameSchema,
typebotSchema,
typebotStartSchema,
typebotStatusSchema,
} from '../../validate/validate.schema';
import { RouterBroker } from '../abstract/abstract.router';
import { InstanceDto } from '../dto/instance.dto';
} from '../../../../validate/validate.schema';
import { RouterBroker } from '../../../abstract/abstract.router';
import { InstanceDto } from '../../../dto/instance.dto';
import { HttpStatus } from '../../../routes/index.router';
import { typebotController } from '../../../server.module';
import { TypebotDto } from '../dto/typebot.dto';
import { typebotController } from '../whatsapp.module';
import { HttpStatus } from './index.router';
const logger = new Logger('TypebotRouter');

View File

@@ -0,0 +1,986 @@
import axios from 'axios';
import EventEmitter2 from 'eventemitter2';
import { ConfigService, Typebot } from '../../../../config/env.config';
import { Logger } from '../../../../config/logger.config';
import { InstanceDto } from '../../../dto/instance.dto';
import { MessageRaw } from '../../../models';
import { WAMonitoringService } from '../../../services/monitor.service';
import { Events } from '../../../types/wa.types';
import { Session, TypebotDto } from '../dto/typebot.dto';
export class TypebotService {
constructor(
private readonly waMonitor: WAMonitoringService,
private readonly configService: ConfigService,
private readonly eventEmitter: EventEmitter2,
) {
this.eventEmitter.on('typebot:end', async (data) => {
const keep_open = this.configService.get<Typebot>('TYPEBOT').KEEP_OPEN;
if (keep_open) return;
await this.clearSessions(data.instance, data.remoteJid);
});
}
private readonly logger = new Logger(TypebotService.name);
public create(instance: InstanceDto, data: TypebotDto) {
this.logger.verbose('create typebot: ' + instance.instanceName);
this.waMonitor.waInstances[instance.instanceName].setTypebot(data);
return { typebot: { ...instance, typebot: data } };
}
public async find(instance: InstanceDto): Promise<TypebotDto> {
try {
this.logger.verbose('find typebot: ' + instance.instanceName);
const result = await this.waMonitor.waInstances[instance.instanceName].findTypebot();
if (Object.keys(result).length === 0) {
throw new Error('Typebot not found');
}
return result;
} catch (error) {
return { enabled: false, url: '', typebot: '', expire: 0, sessions: [] };
}
}
public async changeStatus(instance: InstanceDto, data: any) {
const remoteJid = data.remoteJid;
const status = data.status;
const findData = await this.find(instance);
const session = findData.sessions.find((session) => session.remoteJid === remoteJid);
if (session) {
if (status === 'closed') {
findData.sessions.splice(findData.sessions.indexOf(session), 1);
const typebotData = {
enabled: findData.enabled,
url: findData.url,
typebot: findData.typebot,
expire: findData.expire,
keyword_finish: findData.keyword_finish,
delay_message: findData.delay_message,
unknown_message: findData.unknown_message,
listening_from_me: findData.listening_from_me,
sessions: findData.sessions,
};
this.create(instance, typebotData);
return { typebot: { ...instance, typebot: typebotData } };
}
findData.sessions.map((session) => {
if (session.remoteJid === remoteJid) {
session.status = status;
}
});
} else if (status === 'paused') {
const session: Session = {
remoteJid: remoteJid,
sessionId: Math.floor(Math.random() * 10000000000).toString(),
status: status,
createdAt: Date.now(),
updateAt: Date.now(),
prefilledVariables: {
remoteJid: remoteJid,
pushName: '',
additionalData: {},
},
};
findData.sessions.push(session);
}
const typebotData = {
enabled: findData.enabled,
url: findData.url,
typebot: findData.typebot,
expire: findData.expire,
keyword_finish: findData.keyword_finish,
delay_message: findData.delay_message,
unknown_message: findData.unknown_message,
listening_from_me: findData.listening_from_me,
sessions: findData.sessions,
};
this.create(instance, typebotData);
this.waMonitor.waInstances[instance.instanceName].sendDataWebhook(Events.TYPEBOT_CHANGE_STATUS, {
remoteJid: remoteJid,
status: status,
url: findData.url,
typebot: findData.typebot,
session,
});
return { typebot: { ...instance, typebot: typebotData } };
}
public async clearSessions(instance: InstanceDto, remoteJid: string) {
const findTypebot = await this.find(instance);
const sessions = (findTypebot.sessions as Session[]) ?? [];
const sessionWithRemoteJid = sessions.filter((session) => session.remoteJid === remoteJid);
if (sessionWithRemoteJid.length > 0) {
sessionWithRemoteJid.forEach((session) => {
sessions.splice(sessions.indexOf(session), 1);
});
const typebotData = {
enabled: findTypebot.enabled,
url: findTypebot.url,
typebot: findTypebot.typebot,
expire: findTypebot.expire,
keyword_finish: findTypebot.keyword_finish,
delay_message: findTypebot.delay_message,
unknown_message: findTypebot.unknown_message,
listening_from_me: findTypebot.listening_from_me,
sessions,
};
this.create(instance, typebotData);
return sessions;
}
return sessions;
}
public async startTypebot(instance: InstanceDto, data: any) {
if (data.remoteJid === 'status@broadcast') return;
const remoteJid = data.remoteJid;
const url = data.url;
const typebot = data.typebot;
const startSession = data.startSession;
const variables = data.variables;
const findTypebot = await this.find(instance);
const expire = findTypebot.expire;
const keyword_finish = findTypebot.keyword_finish;
const delay_message = findTypebot.delay_message;
const unknown_message = findTypebot.unknown_message;
const listening_from_me = findTypebot.listening_from_me;
const prefilledVariables = {
remoteJid: remoteJid,
instanceName: instance.instanceName,
};
if (variables?.length) {
variables.forEach((variable: { name: string | number; value: string }) => {
prefilledVariables[variable.name] = variable.value;
});
}
if (startSession) {
const newSessions = await this.clearSessions(instance, remoteJid);
const response = await this.createNewSession(instance, {
enabled: findTypebot.enabled,
url: url,
typebot: typebot,
remoteJid: remoteJid,
expire: expire,
keyword_finish: keyword_finish,
delay_message: delay_message,
unknown_message: unknown_message,
listening_from_me: listening_from_me,
sessions: newSessions,
prefilledVariables: prefilledVariables,
});
if (response.sessionId) {
await this.sendWAMessage(instance, remoteJid, response.messages, response.input, response.clientSideActions);
this.waMonitor.waInstances[instance.instanceName].sendDataWebhook(Events.TYPEBOT_START, {
remoteJid: remoteJid,
url: url,
typebot: typebot,
prefilledVariables: prefilledVariables,
sessionId: `${response.sessionId}`,
});
} else {
throw new Error('Session ID not found in response');
}
} else {
const id = Math.floor(Math.random() * 10000000000).toString();
try {
const version = this.configService.get<Typebot>('TYPEBOT').API_VERSION;
let url: string;
let reqData: {};
if (version === 'latest') {
url = `${data.url}/api/v1/typebots/${data.typebot}/startChat`;
reqData = {
prefilledVariables: prefilledVariables,
};
} else {
url = `${data.url}/api/v1/sendMessage`;
reqData = {
startParams: {
publicId: data.typebot,
prefilledVariables: prefilledVariables,
},
};
}
const request = await axios.post(url, reqData);
await this.sendWAMessage(
instance,
remoteJid,
request.data.messages,
request.data.input,
request.data.clientSideActions,
);
this.waMonitor.waInstances[instance.instanceName].sendDataWebhook(Events.TYPEBOT_START, {
remoteJid: remoteJid,
url: url,
typebot: typebot,
variables: variables,
sessionId: id,
});
} catch (error) {
this.logger.error(error);
return;
}
}
return {
typebot: {
...instance,
typebot: {
url: url,
remoteJid: remoteJid,
typebot: typebot,
prefilledVariables: prefilledVariables,
},
},
};
}
private getTypeMessage(msg: any) {
this.logger.verbose('get type message');
const types = {
conversation: msg.conversation,
extendedTextMessage: msg.extendedTextMessage?.text,
audioMessage: msg.audioMessage?.url,
imageMessage: msg.imageMessage?.url,
videoMessage: msg.videoMessage?.url,
documentMessage: msg.documentMessage?.fileName,
contactMessage: msg.contactMessage?.displayName,
locationMessage: msg.locationMessage?.degreesLatitude,
viewOnceMessageV2:
msg.viewOnceMessageV2?.message?.imageMessage?.url ||
msg.viewOnceMessageV2?.message?.videoMessage?.url ||
msg.viewOnceMessageV2?.message?.audioMessage?.url,
listResponseMessage: msg.listResponseMessage?.singleSelectReply?.selectedRowId,
responseRowId: msg.listResponseMessage?.singleSelectReply?.selectedRowId,
};
const messageType = Object.keys(types).find((key) => types[key] !== undefined) || 'unknown';
this.logger.verbose('Type message: ' + JSON.stringify(types));
return { ...types, messageType };
}
private getMessageContent(types: any) {
this.logger.verbose('get message content');
const typeKey = Object.keys(types).find((key) => types[key] !== undefined);
const result = typeKey ? types[typeKey] : undefined;
this.logger.verbose('message content: ' + result);
return result;
}
private getConversationMessage(msg: any) {
this.logger.verbose('get conversation message');
const types = this.getTypeMessage(msg);
const messageContent = this.getMessageContent(types);
this.logger.verbose('conversation message: ' + messageContent);
return messageContent;
}
private getAudioMessageContent(msg: any) {
this.logger.verbose('get audio message content');
const types = this.getTypeMessage(msg);
const audioContent = types.audioMessage;
this.logger.verbose('audio message URL: ' + audioContent);
return audioContent;
}
private getImageMessageContent(msg: any) {
this.logger.verbose('get image message content');
const types = this.getTypeMessage(msg);
const imageContent = types.imageMessage;
this.logger.verbose('image message URL: ' + imageContent);
return imageContent;
}
private getVideoMessageContent(msg: any) {
this.logger.verbose('get video message content');
const types = this.getTypeMessage(msg);
const videoContent = types.videoMessage;
this.logger.verbose('video message URL: ' + videoContent);
return videoContent;
}
private getDocumentMessageContent(msg: any) {
this.logger.verbose('get document message content');
const types = this.getTypeMessage(msg);
const documentContent = types.documentMessage;
this.logger.verbose('document message fileName: ' + documentContent);
return documentContent;
}
private getContactMessageContent(msg: any) {
this.logger.verbose('get contact message content');
const types = this.getTypeMessage(msg);
const contactContent = types.contactMessage;
this.logger.verbose('contact message displayName: ' + contactContent);
return contactContent;
}
private getLocationMessageContent(msg: any) {
this.logger.verbose('get location message content');
const types = this.getTypeMessage(msg);
const locationContent = types.locationMessage;
this.logger.verbose('location message degreesLatitude: ' + locationContent);
return locationContent;
}
private getViewOnceMessageV2Content(msg: any) {
this.logger.verbose('get viewOnceMessageV2 content');
const types = this.getTypeMessage(msg);
const viewOnceContent = types.viewOnceMessageV2;
this.logger.verbose('viewOnceMessageV2 URL: ' + viewOnceContent);
return viewOnceContent;
}
private getListResponseMessageContent(msg: any) {
this.logger.verbose('get listResponseMessage content');
const types = this.getTypeMessage(msg);
const listResponseContent = types.listResponseMessage || types.responseRowId;
this.logger.verbose('listResponseMessage selectedRowId: ' + listResponseContent);
return listResponseContent;
}
public async createNewSession(instance: InstanceDto, data: any) {
if (data.remoteJid === 'status@broadcast') return;
const id = Math.floor(Math.random() * 10000000000).toString();
try {
const version = this.configService.get<Typebot>('TYPEBOT').API_VERSION;
let url: string;
let reqData: {};
if (version === 'latest') {
url = `${data.url}/api/v1/typebots/${data.typebot}/startChat`;
reqData = {
prefilledVariables: {
...data.prefilledVariables,
remoteJid: data.remoteJid,
pushName: data.pushName || data.prefilledVariables?.pushName || '',
instanceName: instance.instanceName,
},
};
} else {
url = `${data.url}/api/v1/sendMessage`;
reqData = {
startParams: {
publicId: data.typebot,
prefilledVariables: {
...data.prefilledVariables,
remoteJid: data.remoteJid,
pushName: data.pushName || data.prefilledVariables?.pushName || '',
instanceName: instance.instanceName,
},
},
};
}
const request = await axios.post(url, reqData);
if (request?.data?.sessionId) {
data.sessions.push({
remoteJid: data.remoteJid,
sessionId: `${id}-${request.data.sessionId}`,
status: 'opened',
createdAt: Date.now(),
updateAt: Date.now(),
prefilledVariables: {
...data.prefilledVariables,
remoteJid: data.remoteJid,
pushName: data.pushName || '',
instanceName: instance.instanceName,
},
});
const typebotData = {
enabled: data.enabled,
url: data.url,
typebot: data.typebot,
expire: data.expire,
keyword_finish: data.keyword_finish,
delay_message: data.delay_message,
unknown_message: data.unknown_message,
listening_from_me: data.listening_from_me,
sessions: data.sessions,
};
this.create(instance, typebotData);
}
return request.data;
} catch (error) {
this.logger.error(error);
return;
}
}
public async sendWAMessage(
instance: InstanceDto,
remoteJid: string,
messages: any[],
input: any[],
clientSideActions: any[],
) {
processMessages(
this.waMonitor.waInstances[instance.instanceName],
messages,
input,
clientSideActions,
this.eventEmitter,
applyFormatting,
).catch((err) => {
console.error('Erro ao processar mensagens:', err);
});
function findItemAndGetSecondsToWait(array, targetId) {
if (!array) return null;
for (const item of array) {
if (item.lastBubbleBlockId === targetId) {
return item.wait?.secondsToWaitFor;
}
}
return null;
}
function applyFormatting(element) {
let text = '';
if (element.text) {
text += element.text;
}
if (element.children && element.type !== 'a') {
for (const child of element.children) {
text += applyFormatting(child);
}
}
if (element.type === 'p' && element.type !== 'inline-variable') {
text = text.trim() + '\n';
}
if (element.type === 'inline-variable') {
text = text.trim();
}
if (element.type === 'ol') {
text =
'\n' +
text
.split('\n')
.map((line, index) => (line ? `${index + 1}. ${line}` : ''))
.join('\n');
}
if (element.type === 'li') {
text = text
.split('\n')
.map((line) => (line ? ` ${line}` : ''))
.join('\n');
}
let formats = '';
if (element.bold) {
formats += '*';
}
if (element.italic) {
formats += '_';
}
if (element.underline) {
formats += '~';
}
let formattedText = `${formats}${text}${formats.split('').reverse().join('')}`;
if (element.url) {
formattedText = element.children[0]?.text ? `[${formattedText}]\n(${element.url})` : `${element.url}`;
}
return formattedText;
}
async function processMessages(instance, messages, input, clientSideActions, eventEmitter, applyFormatting) {
for (const message of messages) {
if (message.type === 'text') {
let formattedText = '';
for (const richText of message.content.richText) {
for (const element of richText.children) {
formattedText += applyFormatting(element);
}
formattedText += '\n';
}
formattedText = formattedText.replace(/\*\*/g, '').replace(/__/, '').replace(/~~/, '').replace(/\n$/, '');
formattedText = formattedText.replace(/\n$/, '');
await instance.textMessage({
number: remoteJid.split('@')[0],
options: {
delay: instance.localTypebot.delay_message || 1000,
presence: 'composing',
},
textMessage: {
text: formattedText,
},
});
}
if (message.type === 'image') {
await instance.mediaMessage({
number: remoteJid.split('@')[0],
options: {
delay: instance.localTypebot.delay_message || 1000,
presence: 'composing',
},
mediaMessage: {
mediatype: 'image',
media: message.content.url,
},
});
}
if (message.type === 'video') {
await instance.mediaMessage({
number: remoteJid.split('@')[0],
options: {
delay: instance.localTypebot.delay_message || 1000,
presence: 'composing',
},
mediaMessage: {
mediatype: 'video',
media: message.content.url,
},
});
}
if (message.type === 'audio') {
await instance.audioWhatsapp({
number: remoteJid.split('@')[0],
options: {
delay: instance.localTypebot.delay_message || 1000,
presence: 'recording',
encoding: true,
},
audioMessage: {
audio: message.content.url,
},
});
}
const wait = findItemAndGetSecondsToWait(clientSideActions, message.id);
if (wait) {
await new Promise((resolve) => setTimeout(resolve, wait * 1000));
}
}
if (input) {
if (input.type === 'choice input') {
let formattedText = '';
const items = input.items;
for (const item of items) {
formattedText += `▶️ ${item.content}\n`;
}
formattedText = formattedText.replace(/\n$/, '');
await instance.textMessage({
number: remoteJid.split('@')[0],
options: {
delay: instance.localTypebot.delay_message || 1000,
presence: 'composing',
},
textMessage: {
text: formattedText,
},
});
}
} else {
eventEmitter.emit('typebot:end', {
instance: instance,
remoteJid: remoteJid,
});
}
}
}
public async sendTypebot(instance: InstanceDto, remoteJid: string, msg: MessageRaw) {
const findTypebot = await this.find(instance);
const url = findTypebot.url;
const typebot = findTypebot.typebot;
const sessions = (findTypebot.sessions as Session[]) ?? [];
const expire = findTypebot.expire;
const keyword_finish = findTypebot.keyword_finish;
const delay_message = findTypebot.delay_message;
const unknown_message = findTypebot.unknown_message;
const listening_from_me = findTypebot.listening_from_me;
const messageType = this.getTypeMessage(msg.message).messageType;
const session = sessions.find((session) => session.remoteJid === remoteJid);
try {
if (session && expire && expire > 0) {
const now = Date.now();
const diff = now - session.updateAt;
const diffInMinutes = Math.floor(diff / 1000 / 60);
if (diffInMinutes > expire) {
const newSessions = await this.clearSessions(instance, remoteJid);
const data = await this.createNewSession(instance, {
enabled: findTypebot.enabled,
url: url,
typebot: typebot,
expire: expire,
keyword_finish: keyword_finish,
delay_message: delay_message,
unknown_message: unknown_message,
listening_from_me: listening_from_me,
sessions: newSessions,
remoteJid: remoteJid,
pushName: msg.pushName,
});
await this.sendWAMessage(instance, remoteJid, data.messages, data.input, data.clientSideActions);
if (data.messages.length === 0) {
const content = this.getConversationMessage(msg.message);
if (!content) {
if (unknown_message) {
this.waMonitor.waInstances[instance.instanceName].textMessage({
number: remoteJid.split('@')[0],
options: {
delay: delay_message || 1000,
presence: 'composing',
},
textMessage: {
text: unknown_message,
},
});
}
return;
}
if (keyword_finish && content.toLowerCase() === keyword_finish.toLowerCase()) {
const newSessions = await this.clearSessions(instance, remoteJid);
const typebotData = {
enabled: findTypebot.enabled,
url: url,
typebot: typebot,
expire: expire,
keyword_finish: keyword_finish,
delay_message: delay_message,
unknown_message: unknown_message,
listening_from_me: listening_from_me,
sessions: newSessions,
};
this.create(instance, typebotData);
return;
}
try {
const version = this.configService.get<Typebot>('TYPEBOT').API_VERSION;
let urlTypebot: string;
let reqData: {};
if (version === 'latest') {
urlTypebot = `${url}/api/v1/sessions/${data.sessionId}/continueChat`;
reqData = {
message: content,
};
} else {
urlTypebot = `${url}/api/v1/sendMessage`;
reqData = {
message: content,
sessionId: data.sessionId,
};
}
const request = await axios.post(urlTypebot, reqData);
await this.sendWAMessage(
instance,
remoteJid,
request.data.messages,
request.data.input,
request.data.clientSideActions,
);
} catch (error) {
this.logger.error(error);
return;
}
}
return;
}
}
if (session && session.status !== 'opened') {
return;
}
if (!session) {
const data = await this.createNewSession(instance, {
enabled: findTypebot.enabled,
url: url,
typebot: typebot,
expire: expire,
keyword_finish: keyword_finish,
delay_message: delay_message,
unknown_message: unknown_message,
listening_from_me: listening_from_me,
sessions: sessions,
remoteJid: remoteJid,
pushName: msg.pushName,
prefilledVariables: {
messageType: messageType,
},
});
await this.sendWAMessage(instance, remoteJid, data.messages, data.input, data.clientSideActions);
if (data.messages.length === 0) {
const content = this.getConversationMessage(msg.message);
if (!content) {
if (unknown_message) {
this.waMonitor.waInstances[instance.instanceName].textMessage({
number: remoteJid.split('@')[0],
options: {
delay: delay_message || 1000,
presence: 'composing',
},
textMessage: {
text: unknown_message,
},
});
}
return;
}
if (keyword_finish && content.toLowerCase() === keyword_finish.toLowerCase()) {
const newSessions = await this.clearSessions(instance, remoteJid);
const typebotData = {
enabled: findTypebot.enabled,
url: url,
typebot: typebot,
expire: expire,
keyword_finish: keyword_finish,
delay_message: delay_message,
unknown_message: unknown_message,
listening_from_me: listening_from_me,
sessions: newSessions,
};
this.create(instance, typebotData);
return;
}
let request: any;
try {
const version = this.configService.get<Typebot>('TYPEBOT').API_VERSION;
let urlTypebot: string;
let reqData: {};
if (version === 'latest') {
urlTypebot = `${url}/api/v1/sessions/${data.sessionId}/continueChat`;
reqData = {
message: content,
};
} else {
urlTypebot = `${url}/api/v1/sendMessage`;
reqData = {
message: content,
sessionId: data.sessionId,
};
}
request = await axios.post(urlTypebot, reqData);
await this.sendWAMessage(
instance,
remoteJid,
request.data.messages,
request.data.input,
request.data.clientSideActions,
);
} catch (error) {
this.logger.error(error);
return;
}
}
return;
}
sessions.map((session) => {
if (session.remoteJid === remoteJid) {
session.updateAt = Date.now();
}
});
const typebotData = {
enabled: findTypebot.enabled,
url: url,
typebot: typebot,
expire: expire,
keyword_finish: keyword_finish,
delay_message: delay_message,
unknown_message: unknown_message,
listening_from_me: listening_from_me,
sessions,
};
this.create(instance, typebotData);
const content = this.getConversationMessage(msg.message);
if (!content) {
if (unknown_message) {
this.waMonitor.waInstances[instance.instanceName].textMessage({
number: remoteJid.split('@')[0],
options: {
delay: delay_message || 1000,
presence: 'composing',
},
textMessage: {
text: unknown_message,
},
});
}
return;
}
if (keyword_finish && content.toLowerCase() === keyword_finish.toLowerCase()) {
const newSessions = await this.clearSessions(instance, remoteJid);
const typebotData = {
enabled: findTypebot.enabled,
url: url,
typebot: typebot,
expire: expire,
keyword_finish: keyword_finish,
delay_message: delay_message,
unknown_message: unknown_message,
listening_from_me: listening_from_me,
sessions: newSessions,
};
this.create(instance, typebotData);
return;
}
const version = this.configService.get<Typebot>('TYPEBOT').API_VERSION;
let urlTypebot: string;
let reqData: {};
if (version === 'latest') {
urlTypebot = `${url}/api/v1/sessions/${session.sessionId.split('-')[1]}/continueChat`;
reqData = {
message: content,
};
} else {
urlTypebot = `${url}/api/v1/sendMessage`;
reqData = {
message: content,
sessionId: session.sessionId.split('-')[1],
};
}
const request = await axios.post(urlTypebot, reqData);
await this.sendWAMessage(
instance,
remoteJid,
request.data.messages,
request.data.input,
request.data.clientSideActions,
);
return;
} catch (error) {
this.logger.error(error);
return;
}
}
}

View File

@@ -0,0 +1,60 @@
import { JSONSchema7 } from 'json-schema';
import { v4 } from 'uuid';
const isNotEmpty = (...propertyNames: string[]): JSONSchema7 => {
const properties = {};
propertyNames.forEach(
(property) =>
(properties[property] = {
minLength: 1,
description: `The "${property}" cannot be empty`,
}),
);
return {
if: {
propertyNames: {
enum: [...propertyNames],
},
},
then: { properties },
};
};
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'),
};

View File

@@ -1,5 +1,5 @@
import { Logger } from '../../config/logger.config';
import { InstanceDto } from '../dto/instance.dto';
import { Logger } from '../../../../config/logger.config';
import { InstanceDto } from '../../../dto/instance.dto';
import { WebsocketDto } from '../dto/websocket.dto';
import { WebsocketService } from '../services/websocket.service';
@@ -38,6 +38,8 @@ export class WebsocketController {
'GROUP_UPDATE',
'GROUP_PARTICIPANTS_UPDATE',
'CONNECTION_UPDATE',
'LABELS_EDIT',
'LABELS_ASSOCIATION',
'CALL',
'NEW_JWT_TOKEN',
'TYPEBOT_START',

View File

@@ -1,8 +1,8 @@
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';
import { configService, Cors, Websocket } from '../../../../config/env.config';
import { Logger } from '../../../../config/logger.config';
const logger = new Logger('Socket');

View File

@@ -1,6 +1,6 @@
import { Schema } from 'mongoose';
import { dbserver } from '../../libs/db.connect';
import { dbserver } from '../../../../libs/db.connect';
export class WebsocketRaw {
_id?: string;

View File

@@ -1,10 +1,10 @@
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';
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) {

View File

@@ -1,12 +1,12 @@
import { RequestHandler, Router } from 'express';
import { Logger } from '../../config/logger.config';
import { instanceNameSchema, websocketSchema } from '../../validate/validate.schema';
import { RouterBroker } from '../abstract/abstract.router';
import { InstanceDto } from '../dto/instance.dto';
import { Logger } from '../../../../config/logger.config';
import { instanceNameSchema, websocketSchema } from '../../../../validate/validate.schema';
import { RouterBroker } from '../../../abstract/abstract.router';
import { InstanceDto } from '../../../dto/instance.dto';
import { HttpStatus } from '../../../routes/index.router';
import { websocketController } from '../../../server.module';
import { WebsocketDto } from '../dto/websocket.dto';
import { websocketController } from '../whatsapp.module';
import { HttpStatus } from './index.router';
const logger = new Logger('WebsocketRouter');

View File

@@ -1,8 +1,8 @@
import { Logger } from '../../config/logger.config';
import { InstanceDto } from '../dto/instance.dto';
import { Logger } from '../../../../config/logger.config';
import { InstanceDto } from '../../../dto/instance.dto';
import { WebsocketRaw } from '../../../models';
import { WAMonitoringService } from '../../../services/monitor.service';
import { WebsocketDto } from '../dto/websocket.dto';
import { WebsocketRaw } from '../models';
import { WAMonitoringService } from './monitor.service';
export class WebsocketService {
constructor(private readonly waMonitor: WAMonitoringService) {}

View File

@@ -0,0 +1,66 @@
import { JSONSchema7 } from 'json-schema';
import { v4 } from 'uuid';
const isNotEmpty = (...propertyNames: string[]): JSONSchema7 => {
const properties = {};
propertyNames.forEach(
(property) =>
(properties[property] = {
minLength: 1,
description: `The "${property}" cannot be empty`,
}),
);
return {
if: {
propertyNames: {
enum: [...propertyNames],
},
},
then: { properties },
};
};
export const websocketSchema: JSONSchema7 = {
$id: v4(),
type: 'object',
properties: {
enabled: { type: 'boolean', enum: [true, false] },
events: {
type: 'array',
minItems: 0,
items: {
type: 'string',
enum: [
'APPLICATION_STARTUP',
'QRCODE_UPDATED',
'MESSAGES_SET',
'MESSAGES_UPSERT',
'MESSAGES_UPDATE',
'MESSAGES_DELETE',
'SEND_MESSAGE',
'CONTACTS_SET',
'CONTACTS_UPSERT',
'CONTACTS_UPDATE',
'PRESENCE_UPDATE',
'CHATS_SET',
'CHATS_UPSERT',
'CHATS_UPDATE',
'CHATS_DELETE',
'GROUPS_UPSERT',
'GROUP_UPDATE',
'GROUP_PARTICIPANTS_UPDATE',
'CONNECTION_UPDATE',
'LABELS_EDIT',
'LABELS_ASSOCIATION',
'CALL',
'NEW_JWT_TOKEN',
'TYPEBOT_START',
'TYPEBOT_CHANGE_STATUS',
'CHAMA_AI_ACTION',
],
},
},
},
required: ['enabled'],
...isNotEmpty('enabled'),
};

View File

@@ -6,12 +6,14 @@ 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');

View File

@@ -7,12 +7,19 @@ export class ChatRaw {
id?: string;
owner: string;
lastMsgTimestamp?: number;
labels?: string[];
}
type ChatRawBoolean<T> = {
[P in keyof T]?: 0 | 1;
};
export type ChatRawSelect = ChatRawBoolean<ChatRaw>;
const chatSchema = new Schema<ChatRaw>({
_id: { type: String, _id: true },
id: { type: String, required: true, minlength: 1 },
owner: { type: String, required: true, minlength: 1 },
labels: { type: [String], default: [] },
});
export const ChatModel = dbserver?.model(ChatRaw.name, chatSchema, 'chats');

View File

@@ -10,6 +10,11 @@ export class ContactRaw {
owner: string;
}
type ContactRawBoolean<T> = {
[P in keyof T]?: 0 | 1;
};
export type ContactRawSelect = ContactRawBoolean<ContactRaw>;
const contactSchema = new Schema<ContactRaw>({
_id: { type: String, _id: true },
pushName: { type: String, minlength: 1 },

15
src/api/models/index.ts Normal file
View File

@@ -0,0 +1,15 @@
export * from '../integrations/chamaai/models/chamaai.model';
export * from '../integrations/chatwoot/models/chatwoot.model';
export * from '../integrations/rabbitmq/models/rabbitmq.model';
export * from '../integrations/sqs/models/sqs.model';
export * from '../integrations/typebot/models/typebot.model';
export * from '../integrations/websocket/models/websocket.model';
export * from './auth.model';
export * from './chat.model';
export * from './contact.model';
export * from './integration.model';
export * from './label.model';
export * from './message.model';
export * from './proxy.model';
export * from './settings.model';
export * from './webhook.model';

View File

@@ -0,0 +1,20 @@
import { Schema } from 'mongoose';
import { dbserver } from '../../libs/db.connect';
export class IntegrationRaw {
_id?: string;
integration?: string;
number?: string;
token?: string;
}
const integrationSchema = new Schema<IntegrationRaw>({
_id: { type: String, _id: true },
integration: { type: String, required: true },
number: { type: String, required: true },
token: { type: String, required: true },
});
export const IntegrationModel = dbserver?.model(IntegrationRaw.name, integrationSchema, 'integration');
export type IntegrationModel = typeof IntegrationModel;

View File

@@ -0,0 +1,29 @@
import { Schema } from 'mongoose';
import { dbserver } from '../../libs/db.connect';
export class LabelRaw {
_id?: string;
id?: string;
owner: string;
name: string;
color: number;
predefinedId?: string;
}
type LabelRawBoolean<T> = {
[P in keyof T]?: 0 | 1;
};
export type LabelRawSelect = LabelRawBoolean<LabelRaw>;
const labelSchema = new Schema<LabelRaw>({
_id: { type: String, _id: true },
id: { type: String, required: true, minlength: 1 },
owner: { type: String, required: true, minlength: 1 },
name: { type: String, required: true, minlength: 1 },
color: { type: Number, required: true, min: 0, max: 19 },
predefinedId: { type: String },
});
export const LabelModel = dbserver?.model(LabelRaw.name, labelSchema, 'labels');
export type ILabelModel = typeof LabelModel;

View File

@@ -10,6 +10,14 @@ class Key {
participant?: string;
}
class ChatwootMessage {
messageId?: number;
inboxId?: number;
conversationId?: number;
contactInbox?: { sourceId: string };
isRead?: boolean;
}
export class MessageRaw {
_id?: string;
key?: Key;
@@ -19,11 +27,22 @@ export class MessageRaw {
messageType?: string;
messageTimestamp?: number | Long.Long;
owner: string;
source?: 'android' | 'web' | 'ios';
source?: 'android' | 'web' | 'ios' | 'unknown' | 'desktop';
source_id?: string;
source_reply_id?: string;
chatwoot?: ChatwootMessage;
contextInfo?: any;
status?: wa.StatusMessage | any;
}
type MessageRawBoolean<T> = {
[P in keyof T]?: 0 | 1;
};
export type MessageRawSelect = Omit<Omit<MessageRawBoolean<MessageRaw>, 'key'>, 'chatwoot'> & {
key?: MessageRawBoolean<Key>;
chatwoot?: MessageRawBoolean<ChatwootMessage>;
};
const messageSchema = new Schema<MessageRaw>({
_id: { type: String, _id: true },
key: {
@@ -36,11 +55,23 @@ const messageSchema = new Schema<MessageRaw>({
participant: { type: String },
messageType: { type: String },
message: { type: Object },
source: { type: String, minlength: 3, enum: ['android', 'web', 'ios'] },
source: { type: String, minlength: 3, enum: ['android', 'web', 'ios', 'unknown', 'desktop'] },
messageTimestamp: { type: Number, required: true },
owner: { type: String, required: true, minlength: 1 },
chatwoot: {
messageId: { type: Number },
inboxId: { type: Number },
conversationId: { type: Number },
contactInbox: { type: Object },
isRead: { type: Boolean },
},
});
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;

View File

@@ -2,16 +2,30 @@ import { Schema } from 'mongoose';
import { dbserver } from '../../libs/db.connect';
class Proxy {
host?: string;
port?: string;
protocol?: string;
username?: string;
password?: string;
}
export class ProxyRaw {
_id?: string;
enabled?: boolean;
proxy?: string;
proxy?: Proxy;
}
const proxySchema = new Schema<ProxyRaw>({
_id: { type: String, _id: true },
enabled: { type: Boolean, required: true },
proxy: { type: String, required: true },
proxy: {
host: { type: String, required: true },
port: { type: String, required: true },
protocol: { type: String, required: true },
username: { type: String, required: false },
password: { type: String, required: false },
},
});
export const ProxyModel = dbserver?.model(ProxyRaw.name, proxySchema, 'proxy');

View File

@@ -10,6 +10,7 @@ export class SettingsRaw {
always_online?: boolean;
read_messages?: boolean;
read_status?: boolean;
sync_full_history?: boolean;
}
const settingsSchema = new Schema<SettingsRaw>({
@@ -20,6 +21,7 @@ const settingsSchema = new Schema<SettingsRaw>({
always_online: { type: Boolean, required: true },
read_messages: { type: Boolean, required: true },
read_status: { type: Boolean, required: true },
sync_full_history: { type: Boolean, required: true },
});
export const SettingsModel = dbserver?.model(SettingsRaw.name, settingsSchema, 'settings');

View File

@@ -0,0 +1,150 @@
import axios from 'axios';
import { execSync } from 'child_process';
import { Auth, ConfigService, ProviderSession } from '../../config/env.config';
import { Logger } from '../../config/logger.config';
type ResponseSuccess = { status: number; data?: any };
type ResponseProvider = Promise<[ResponseSuccess?, Error?]>;
export class ProviderFiles {
constructor(private readonly configService: ConfigService) {
this.baseUrl = `http://${this.config.HOST}:${this.config.PORT}/session/${this.config.PREFIX}`;
this.globalApiToken = this.configService.get<Auth>('AUTHENTICATION').API_KEY.KEY;
}
private readonly logger = new Logger(ProviderFiles.name);
private baseUrl: string;
private globalApiToken: string;
private readonly config = Object.freeze(this.configService.get<ProviderSession>('PROVIDER'));
get isEnabled() {
return !!this.config?.ENABLED;
}
public async onModuleInit() {
if (this.config.ENABLED) {
const url = `http://${this.config.HOST}:${this.config.PORT}`;
try {
const response = await axios.options(url + '/ping');
if (response?.data != 'pong') {
throw new Error('Offline file provider.');
}
await axios.post(`${url}/session`, { group: this.config.PREFIX }, { headers: { apikey: this.globalApiToken } });
} catch (error) {
this.logger.error(['Failed to connect to the file server', error?.message, error?.stack]);
const pid = process.pid;
execSync(`kill -9 ${pid}`);
}
}
}
public async onModuleDestroy() {
//
}
public async create(instance: string): ResponseProvider {
try {
const response = await axios.post(
`${this.baseUrl}`,
{
instance,
},
{ headers: { apikey: this.globalApiToken } },
);
return [{ status: response.status, data: response?.data }];
} catch (error) {
return [
{
status: error?.response?.status,
data: error?.response?.data,
},
error,
];
}
}
public async write(instance: string, key: string, data: any): ResponseProvider {
try {
const response = await axios.post(`${this.baseUrl}/${instance}/${key}`, data, {
headers: { apikey: this.globalApiToken },
});
return [{ status: response.status, data: response?.data }];
} catch (error) {
return [
{
status: error?.response?.status,
data: error?.response?.data,
},
error,
];
}
}
public async read(instance: string, key: string): ResponseProvider {
try {
const response = await axios.get(`${this.baseUrl}/${instance}/${key}`, {
headers: { apikey: this.globalApiToken },
});
return [{ status: response.status, data: response?.data }];
} catch (error) {
return [
{
status: error?.response?.status,
data: error?.response?.data,
},
error,
];
}
}
public async delete(instance: string, key: string): ResponseProvider {
try {
const response = await axios.delete(`${this.baseUrl}/${instance}/${key}`, {
headers: { apikey: this.globalApiToken },
});
return [{ status: response.status, data: response?.data }];
} catch (error) {
return [
{
status: error?.response?.status,
data: error?.response?.data,
},
error,
];
}
}
public async allInstances(): ResponseProvider {
try {
const response = await axios.get(`${this.baseUrl}/list-instances`, { headers: { apikey: this.globalApiToken } });
return [{ status: response.status, data: response?.data as string[] }];
} catch (error) {
return [
{
status: error?.response?.status,
data: error?.response?.data,
},
error,
];
}
}
public async removeSession(instance: string): ResponseProvider {
try {
const response = await axios.delete(`${this.baseUrl}/${instance}`, { headers: { apikey: this.globalApiToken } });
return [{ status: response.status, data: response?.data }];
} catch (error) {
return [
{
status: error?.response?.status,
data: error?.response?.data,
},
error,
];
}
}
}

Some files were not shown because too many files have changed in this diff Show More