FEAT: Implementa chat funcional Whatsapp com atribuição de usuario
All checks were successful
Deploy Dev / deploy (push) Successful in 47s
All checks were successful
Deploy Dev / deploy (push) Successful in 47s
- Aumento do limite de payload no NestJS para 50MB (json/urlencoded) permitindo upload de mídias em base64 sem erros de 'PayloadTooLarge'. - Criação de mecanismo de 'Smart Name Resolution' e 'Auto-Reparação' reativa para evitar que nomes de contatos sejam sobrescritos por JIDs numéricos, consultando o Puppeteer em segundo plano. - Correção de erro na atribuição de chats (/whatsapp/assign) onde 'area_id' nulo violava restrição 'NOT NULL' do PostgreSQL, agora exigindo o ID inteiro correspondente.
This commit is contained in:
parent
312f330bdf
commit
8c28e9c479
6
.gitignore
vendored
6
.gitignore
vendored
@ -3,3 +3,9 @@
|
||||
/dist
|
||||
/logs
|
||||
*.tsbuildinfo
|
||||
whatsapp-sessions/*
|
||||
/whatsapp-session
|
||||
whatsapp-chats-persist.json
|
||||
all-chats-dump.json
|
||||
test-api-out.json
|
||||
*.log
|
||||
@ -0,0 +1,36 @@
|
||||
node:internal/modules/cjs/loader:1424
|
||||
throw err;
|
||||
^
|
||||
|
||||
Error: Cannot find module './whatsapp.gateway'
|
||||
Require stack:
|
||||
- C:\Users\rafael.lopes\Projetos\DevelopmentSothis\omnichannel\backend\dist\modules\whatsapp\whatsapp.service.js
|
||||
- C:\Users\rafael.lopes\Projetos\DevelopmentSothis\omnichannel\backend\dist\modules\whatsapp\whatsapp.module.js
|
||||
- C:\Users\rafael.lopes\Projetos\DevelopmentSothis\omnichannel\backend\dist\app.module.js
|
||||
- C:\Users\rafael.lopes\Projetos\DevelopmentSothis\omnichannel\backend\dist\main.js
|
||||
at Module._resolveFilename (node:internal/modules/cjs/loader:1421:15)
|
||||
at defaultResolveImpl (node:internal/modules/cjs/loader:1059:19)
|
||||
at resolveForCJSWithHooks (node:internal/modules/cjs/loader:1064:22)
|
||||
at Module._load (node:internal/modules/cjs/loader:1227:37)
|
||||
at TracingChannel.traceSync (node:diagnostics_channel:328:14)
|
||||
at wrapModuleLoad (node:internal/modules/cjs/loader:245:24)
|
||||
at Module.require (node:internal/modules/cjs/loader:1504:12)
|
||||
at require (node:internal/modules/helpers:152:16)
|
||||
at Object.<anonymous> (C:\Users\rafael.lopes\Projetos\DevelopmentSothis\omnichannel\backend\src\modules\whatsapp\whatsapp.service.ts:3:1)
|
||||
at Module._compile (node:internal/modules/cjs/loader:1761:14)
|
||||
at Object..js (node:internal/modules/cjs/loader:1893:10)
|
||||
at Module.load (node:internal/modules/cjs/loader:1481:32)
|
||||
at Module._load (node:internal/modules/cjs/loader:1300:12)
|
||||
at TracingChannel.traceSync (node:diagnostics_channel:328:14)
|
||||
at wrapModuleLoad (node:internal/modules/cjs/loader:245:24)
|
||||
at Module.require (node:internal/modules/cjs/loader:1504:12) {
|
||||
code: 'MODULE_NOT_FOUND',
|
||||
requireStack: [
|
||||
'C:\\Users\\rafael.lopes\\Projetos\\DevelopmentSothis\\omnichannel\\backend\\dist\\modules\\whatsapp\\whatsapp.service.js',
|
||||
'C:\\Users\\rafael.lopes\\Projetos\\DevelopmentSothis\\omnichannel\\backend\\dist\\modules\\whatsapp\\whatsapp.module.js',
|
||||
'C:\\Users\\rafael.lopes\\Projetos\\DevelopmentSothis\\omnichannel\\backend\\dist\\app.module.js',
|
||||
'C:\\Users\\rafael.lopes\\Projetos\\DevelopmentSothis\\omnichannel\\backend\\dist\\main.js'
|
||||
]
|
||||
}
|
||||
|
||||
Node.js v24.11.1
|
||||
221
backend-dev.log
221
backend-dev.log
@ -24,3 +24,224 @@
|
||||
[32m[Nest] 42064 - [39m14/05/2026, 17:25:44 [32m LOG[39m [38;5;3m[RouterExplorer] [39m[32mMapped {/admin/access/users/:id, PUT} route[39m[38;5;3m +4ms[39m
|
||||
[32m[Nest] 42064 - [39m14/05/2026, 17:25:44 [32m LOG[39m [38;5;3m[NestApplication] [39m[32mNest application successfully started[39m[38;5;3m +9ms[39m
|
||||
[32m[Nest] 42064 - [39m14/05/2026, 17:25:44 [32m LOG[39m [38;5;3m[Bootstrap] [39m[32mBackend ouvindo na porta 3001[39m
|
||||
[2J[3J[H[[90m18:11:44[0m] File change detected. Starting incremental compilation...
|
||||
|
||||
[[90m18:11:47[0m] Found 0 errors. Watching for file changes.
|
||||
|
||||
[2J[3J[H[[90m18:11:47[0m] File change detected. Starting incremental compilation...
|
||||
|
||||
[[90m18:11:50[0m] Found 0 errors. Watching for file changes.
|
||||
|
||||
[2J[3J[H[[90m18:11:50[0m] File change detected. Starting incremental compilation...
|
||||
|
||||
[[90m18:11:53[0m] Found 0 errors. Watching for file changes.
|
||||
|
||||
[2J[3J[H[[90m18:11:57[0m] File change detected. Starting incremental compilation...
|
||||
|
||||
[[90m18:11:59[0m] Found 0 errors. Watching for file changes.
|
||||
|
||||
[32m[Nest] 13228 - [39m14/05/2026, 18:12:05 [32m LOG[39m [38;5;3m[NestFactory] [39m[32mStarting Nest application...[39m
|
||||
[32m[Nest] 13228 - [39m14/05/2026, 18:12:05 [32m LOG[39m [38;5;3m[InstanceLoader] [39m[32mDatabaseModule dependencies initialized[39m[38;5;3m +55ms[39m
|
||||
[32m[Nest] 13228 - [39m14/05/2026, 18:12:05 [32m LOG[39m [38;5;3m[InstanceLoader] [39m[32mAppModule dependencies initialized[39m[38;5;3m +6ms[39m
|
||||
[32m[Nest] 13228 - [39m14/05/2026, 18:12:05 [32m LOG[39m [38;5;3m[InstanceLoader] [39m[32mAdminModule dependencies initialized[39m[38;5;3m +5ms[39m
|
||||
[32m[Nest] 13228 - [39m14/05/2026, 18:12:05 [32m LOG[39m [38;5;3m[InstanceLoader] [39m[32mAuthModule dependencies initialized[39m[38;5;3m +2ms[39m
|
||||
[32m[Nest] 13228 - [39m14/05/2026, 18:12:05 [32m LOG[39m [38;5;3m[RoutesResolver] [39m[32mAppController {/}:[39m[38;5;3m +15ms[39m
|
||||
[32m[Nest] 13228 - [39m14/05/2026, 18:12:05 [32m LOG[39m [38;5;3m[RouterExplorer] [39m[32mMapped {/health, GET} route[39m[38;5;3m +11ms[39m
|
||||
[32m[Nest] 13228 - [39m14/05/2026, 18:12:05 [32m LOG[39m [38;5;3m[RoutesResolver] [39m[32mAuthController {/auth}:[39m[38;5;3m +1ms[39m
|
||||
[32m[Nest] 13228 - [39m14/05/2026, 18:12:05 [32m LOG[39m [38;5;3m[RouterExplorer] [39m[32mMapped {/auth/config, GET} route[39m[38;5;3m +3ms[39m
|
||||
[32m[Nest] 13228 - [39m14/05/2026, 18:12:05 [32m LOG[39m [38;5;3m[RouterExplorer] [39m[32mMapped {/auth/login, POST} route[39m[38;5;3m +5ms[39m
|
||||
[32m[Nest] 13228 - [39m14/05/2026, 18:12:05 [32m LOG[39m [38;5;3m[RouterExplorer] [39m[32mMapped {/auth/oauth/microsoft/start, GET} route[39m[38;5;3m +1ms[39m
|
||||
[32m[Nest] 13228 - [39m14/05/2026, 18:12:05 [32m LOG[39m [38;5;3m[RouterExplorer] [39m[32mMapped {/auth/oauth/microsoft/callback, GET} route[39m[38;5;3m +2ms[39m
|
||||
[32m[Nest] 13228 - [39m14/05/2026, 18:12:05 [32m LOG[39m [38;5;3m[RoutesResolver] [39m[32mAdminAccessController {/admin/access}:[39m[38;5;3m +2ms[39m
|
||||
[32m[Nest] 13228 - [39m14/05/2026, 18:12:05 [32m LOG[39m [38;5;3m[RouterExplorer] [39m[32mMapped {/admin/access/options, GET} route[39m[38;5;3m +1ms[39m
|
||||
[32m[Nest] 13228 - [39m14/05/2026, 18:12:05 [32m LOG[39m [38;5;3m[RouterExplorer] [39m[32mMapped {/admin/access/users, GET} route[39m[38;5;3m +1ms[39m
|
||||
[32m[Nest] 13228 - [39m14/05/2026, 18:12:05 [32m LOG[39m [38;5;3m[RouterExplorer] [39m[32mMapped {/admin/access/users/:id, PUT} route[39m[38;5;3m +2ms[39m
|
||||
[32m[Nest] 13228 - [39m14/05/2026, 18:12:05 [32m LOG[39m [38;5;3m[NestApplication] [39m[32mNest application successfully started[39m[38;5;3m +8ms[39m
|
||||
[32m[Nest] 13228 - [39m14/05/2026, 18:12:05 [32m LOG[39m [38;5;3m[Bootstrap] [39m[32mBackend ouvindo na porta 3001[39m
|
||||
[2J[3J[H[[90m18:12:25[0m] File change detected. Starting incremental compilation...
|
||||
|
||||
[96msrc/modules/whatsapp/whatsapp.module.ts[0m:[93m2[0m:[93m33[0m - [91merror[0m[90m TS2307: [0mCannot find module './whatsapp.service' or its corresponding type declarations.
|
||||
|
||||
[7m2[0m import { WhatsappService } from './whatsapp.service';
|
||||
[7m [0m [91m ~~~~~~~~~~~~~~~~~~~~[0m
|
||||
|
||||
[96msrc/modules/whatsapp/whatsapp.module.ts[0m:[93m3[0m:[93m33[0m - [91merror[0m[90m TS2307: [0mCannot find module './whatsapp.gateway' or its corresponding type declarations.
|
||||
|
||||
[7m3[0m import { WhatsappGateway } from './whatsapp.gateway';
|
||||
[7m [0m [91m ~~~~~~~~~~~~~~~~~~~~[0m
|
||||
|
||||
[96msrc/modules/whatsapp/whatsapp.module.ts[0m:[93m4[0m:[93m36[0m - [91merror[0m[90m TS2307: [0mCannot find module './whatsapp.controller' or its corresponding type declarations.
|
||||
|
||||
[7m4[0m import { WhatsappController } from './whatsapp.controller';
|
||||
[7m [0m [91m ~~~~~~~~~~~~~~~~~~~~~~~[0m
|
||||
|
||||
[[90m18:12:25[0m] Found 3 errors. Watching for file changes.
|
||||
|
||||
[2J[3J[H[[90m18:12:27[0m] File change detected. Starting incremental compilation...
|
||||
|
||||
[96msrc/modules/whatsapp/whatsapp.controller.ts[0m:[93m2[0m:[93m33[0m - [91merror[0m[90m TS2307: [0mCannot find module './whatsapp.service' or its corresponding type declarations.
|
||||
|
||||
[7m2[0m import { WhatsappService } from './whatsapp.service';
|
||||
[7m [0m [91m ~~~~~~~~~~~~~~~~~~~~[0m
|
||||
|
||||
[96msrc/modules/whatsapp/whatsapp.module.ts[0m:[93m2[0m:[93m33[0m - [91merror[0m[90m TS2307: [0mCannot find module './whatsapp.service' or its corresponding type declarations.
|
||||
|
||||
[7m2[0m import { WhatsappService } from './whatsapp.service';
|
||||
[7m [0m [91m ~~~~~~~~~~~~~~~~~~~~[0m
|
||||
|
||||
[96msrc/modules/whatsapp/whatsapp.module.ts[0m:[93m3[0m:[93m33[0m - [91merror[0m[90m TS2307: [0mCannot find module './whatsapp.gateway' or its corresponding type declarations.
|
||||
|
||||
[7m3[0m import { WhatsappGateway } from './whatsapp.gateway';
|
||||
[7m [0m [91m ~~~~~~~~~~~~~~~~~~~~[0m
|
||||
|
||||
[[90m18:12:28[0m] Found 3 errors. Watching for file changes.
|
||||
|
||||
[2J[3J[H[[90m18:12:36[0m] File change detected. Starting incremental compilation...
|
||||
|
||||
[96msrc/modules/whatsapp/whatsapp.controller.ts[0m:[93m2[0m:[93m33[0m - [91merror[0m[90m TS2307: [0mCannot find module './whatsapp.service' or its corresponding type declarations.
|
||||
|
||||
[7m2[0m import { WhatsappService } from './whatsapp.service';
|
||||
[7m [0m [91m ~~~~~~~~~~~~~~~~~~~~[0m
|
||||
|
||||
[96msrc/modules/whatsapp/whatsapp.module.ts[0m:[93m2[0m:[93m33[0m - [91merror[0m[90m TS2307: [0mCannot find module './whatsapp.service' or its corresponding type declarations.
|
||||
|
||||
[7m2[0m import { WhatsappService } from './whatsapp.service';
|
||||
[7m [0m [91m ~~~~~~~~~~~~~~~~~~~~[0m
|
||||
|
||||
[96msrc/modules/whatsapp/whatsapp.module.ts[0m:[93m3[0m:[93m33[0m - [91merror[0m[90m TS2307: [0mCannot find module './whatsapp.gateway' or its corresponding type declarations.
|
||||
|
||||
[7m3[0m import { WhatsappGateway } from './whatsapp.gateway';
|
||||
[7m [0m [91m ~~~~~~~~~~~~~~~~~~~~[0m
|
||||
|
||||
[[90m18:12:40[0m] Found 3 errors. Watching for file changes.
|
||||
|
||||
[2J[3J[H[[90m18:12:41[0m] File change detected. Starting incremental compilation...
|
||||
|
||||
[96msrc/modules/whatsapp/whatsapp.controller.ts[0m:[93m2[0m:[93m33[0m - [91merror[0m[90m TS2307: [0mCannot find module './whatsapp.service' or its corresponding type declarations.
|
||||
|
||||
[7m2[0m import { WhatsappService } from './whatsapp.service';
|
||||
[7m [0m [91m ~~~~~~~~~~~~~~~~~~~~[0m
|
||||
|
||||
[96msrc/modules/whatsapp/whatsapp.module.ts[0m:[93m2[0m:[93m33[0m - [91merror[0m[90m TS2307: [0mCannot find module './whatsapp.service' or its corresponding type declarations.
|
||||
|
||||
[7m2[0m import { WhatsappService } from './whatsapp.service';
|
||||
[7m [0m [91m ~~~~~~~~~~~~~~~~~~~~[0m
|
||||
|
||||
[96msrc/modules/whatsapp/whatsapp.module.ts[0m:[93m3[0m:[93m33[0m - [91merror[0m[90m TS2307: [0mCannot find module './whatsapp.gateway' or its corresponding type declarations.
|
||||
|
||||
[7m3[0m import { WhatsappGateway } from './whatsapp.gateway';
|
||||
[7m [0m [91m ~~~~~~~~~~~~~~~~~~~~[0m
|
||||
|
||||
[[90m18:12:41[0m] Found 3 errors. Watching for file changes.
|
||||
|
||||
[2J[3J[H[[90m18:13:01[0m] File change detected. Starting incremental compilation...
|
||||
|
||||
[96msrc/modules/whatsapp/whatsapp.controller.ts[0m:[93m2[0m:[93m33[0m - [91merror[0m[90m TS2307: [0mCannot find module './whatsapp.service' or its corresponding type declarations.
|
||||
|
||||
[7m2[0m import { WhatsappService } from './whatsapp.service';
|
||||
[7m [0m [91m ~~~~~~~~~~~~~~~~~~~~[0m
|
||||
|
||||
[96msrc/modules/whatsapp/whatsapp.module.ts[0m:[93m2[0m:[93m33[0m - [91merror[0m[90m TS2307: [0mCannot find module './whatsapp.service' or its corresponding type declarations.
|
||||
|
||||
[7m2[0m import { WhatsappService } from './whatsapp.service';
|
||||
[7m [0m [91m ~~~~~~~~~~~~~~~~~~~~[0m
|
||||
|
||||
[96msrc/modules/whatsapp/whatsapp.module.ts[0m:[93m3[0m:[93m33[0m - [91merror[0m[90m TS2307: [0mCannot find module './whatsapp.gateway' or its corresponding type declarations.
|
||||
|
||||
[7m3[0m import { WhatsappGateway } from './whatsapp.gateway';
|
||||
[7m [0m [91m ~~~~~~~~~~~~~~~~~~~~[0m
|
||||
|
||||
[[90m18:13:01[0m] Found 3 errors. Watching for file changes.
|
||||
|
||||
[2J[3J[H[[90m18:13:12[0m] File change detected. Starting incremental compilation...
|
||||
|
||||
[96msrc/modules/whatsapp/whatsapp.module.ts[0m:[93m3[0m:[93m33[0m - [91merror[0m[90m TS2307: [0mCannot find module './whatsapp.gateway' or its corresponding type declarations.
|
||||
|
||||
[7m3[0m import { WhatsappGateway } from './whatsapp.gateway';
|
||||
[7m [0m [91m ~~~~~~~~~~~~~~~~~~~~[0m
|
||||
|
||||
[96msrc/modules/whatsapp/whatsapp.service.ts[0m:[93m3[0m:[93m33[0m - [91merror[0m[90m TS2307: [0mCannot find module './whatsapp.gateway' or its corresponding type declarations.
|
||||
|
||||
[7m3[0m import { WhatsappGateway } from './whatsapp.gateway';
|
||||
[7m [0m [91m ~~~~~~~~~~~~~~~~~~~~[0m
|
||||
|
||||
[96msrc/modules/whatsapp/whatsapp.service.ts[0m:[93m63[0m:[93m25[0m - [91merror[0m[90m TS2339: [0mProperty 'isGroupMsg' does not exist on type 'Message'.
|
||||
|
||||
[7m63[0m isGroupMsg: msg.isGroupMsg,
|
||||
[7m [0m [91m ~~~~~~~~~~[0m
|
||||
|
||||
[[90m18:13:13[0m] Found 3 errors. Watching for file changes.
|
||||
|
||||
[2J[3J[H[[90m18:13:14[0m] File change detected. Starting incremental compilation...
|
||||
|
||||
[96msrc/modules/whatsapp/whatsapp.service.ts[0m:[93m63[0m:[93m25[0m - [91merror[0m[90m TS2339: [0mProperty 'isGroupMsg' does not exist on type 'Message'.
|
||||
|
||||
[7m63[0m isGroupMsg: msg.isGroupMsg,
|
||||
[7m [0m [91m ~~~~~~~~~~[0m
|
||||
|
||||
[[90m18:13:16[0m] Found 1 error. Watching for file changes.
|
||||
|
||||
[2J[3J[H[[90m18:14:57[0m] File change detected. Starting incremental compilation...
|
||||
|
||||
[96msrc/modules/whatsapp/whatsapp.service.ts[0m:[93m63[0m:[93m25[0m - [91merror[0m[90m TS2339: [0mProperty 'isGroupMsg' does not exist on type 'Message'.
|
||||
|
||||
[7m63[0m isGroupMsg: msg.isGroupMsg,
|
||||
[7m [0m [91m ~~~~~~~~~~[0m
|
||||
|
||||
[[90m18:14:58[0m] Found 1 error. Watching for file changes.
|
||||
|
||||
[2J[3J[H[[90m18:19:00[0m] File change detected. Starting incremental compilation...
|
||||
|
||||
[96msrc/modules/whatsapp/whatsapp.service.ts[0m:[93m63[0m:[93m25[0m - [91merror[0m[90m TS2339: [0mProperty 'isGroupMsg' does not exist on type 'Message'.
|
||||
|
||||
[7m63[0m isGroupMsg: msg.isGroupMsg,
|
||||
[7m [0m [91m ~~~~~~~~~~[0m
|
||||
|
||||
[[90m18:19:00[0m] Found 1 error. Watching for file changes.
|
||||
|
||||
[2J[3J[H[[90m18:19:05[0m] File change detected. Starting incremental compilation...
|
||||
|
||||
[96msrc/modules/whatsapp/whatsapp.service.ts[0m:[93m63[0m:[93m25[0m - [91merror[0m[90m TS2339: [0mProperty 'isGroupMsg' does not exist on type 'Message'.
|
||||
|
||||
[7m63[0m isGroupMsg: msg.isGroupMsg,
|
||||
[7m [0m [91m ~~~~~~~~~~[0m
|
||||
|
||||
[[90m18:19:05[0m] Found 1 error. Watching for file changes.
|
||||
|
||||
[2J[3J[H[[90m18:19:12[0m] File change detected. Starting incremental compilation...
|
||||
|
||||
[96msrc/modules/whatsapp/whatsapp.service.ts[0m:[93m63[0m:[93m25[0m - [91merror[0m[90m TS2339: [0mProperty 'isGroupMsg' does not exist on type 'Message'.
|
||||
|
||||
[7m63[0m isGroupMsg: msg.isGroupMsg,
|
||||
[7m [0m [91m ~~~~~~~~~~[0m
|
||||
|
||||
[[90m18:19:13[0m] Found 1 error. Watching for file changes.
|
||||
|
||||
[2J[3J[H[[90m18:19:20[0m] File change detected. Starting incremental compilation...
|
||||
|
||||
[96msrc/modules/whatsapp/whatsapp.service.ts[0m:[93m67[0m:[93m25[0m - [91merror[0m[90m TS2339: [0mProperty 'isGroupMsg' does not exist on type 'Message'.
|
||||
|
||||
[7m67[0m isGroupMsg: msg.isGroupMsg,
|
||||
[7m [0m [91m ~~~~~~~~~~[0m
|
||||
|
||||
[[90m18:19:21[0m] Found 1 error. Watching for file changes.
|
||||
|
||||
[2J[3J[H[[90m18:30:52[0m] File change detected. Starting incremental compilation...
|
||||
|
||||
[96msrc/modules/whatsapp/whatsapp.service.ts[0m:[93m67[0m:[93m25[0m - [91merror[0m[90m TS2339: [0mProperty 'isGroupMsg' does not exist on type 'Message'.
|
||||
|
||||
[7m67[0m isGroupMsg: msg.isGroupMsg,
|
||||
[7m [0m [91m ~~~~~~~~~~[0m
|
||||
|
||||
[[90m18:30:53[0m] Found 1 error. Watching for file changes.
|
||||
|
||||
[2J[3J[H[[90m18:32:18[0m] File change detected. Starting incremental compilation...
|
||||
|
||||
[2J[3J[H[[90m18:33:45[0m] File change detected. Starting incremental compilation...
|
||||
|
||||
[[90m18:33:48[0m] Found 0 errors. Watching for file changes.
|
||||
|
||||
[2J[3J[H[[90m18:33:49[0m] File change detected. Starting incremental compilation...
|
||||
|
||||
[[90m18:33:50[0m] Found 0 errors. Watching for file changes.
|
||||
|
||||
|
||||
2257
package-lock.json
generated
2257
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -12,12 +12,17 @@
|
||||
"@nestjs/common": "^11.1.19",
|
||||
"@nestjs/core": "^11.1.19",
|
||||
"@nestjs/platform-express": "^11.1.19",
|
||||
"@nestjs/platform-socket.io": "^11.1.21",
|
||||
"@nestjs/websockets": "^11.1.21",
|
||||
"dotenv": "^16.6.1",
|
||||
"jsonwebtoken": "^9.0.3",
|
||||
"ldapts": "^8.1.7",
|
||||
"pg": "^8.20.0",
|
||||
"qrcode": "^1.5.4",
|
||||
"reflect-metadata": "^0.2.2",
|
||||
"rxjs": "^7.8.2",
|
||||
"socket.io": "^4.8.3",
|
||||
"whatsapp-web.js": "^1.34.7",
|
||||
"winston": "^3.19.0",
|
||||
"winston-daily-rotate-file": "^5.0.0"
|
||||
},
|
||||
|
||||
@ -3,9 +3,10 @@ import { AppController } from './app.controller';
|
||||
import { DatabaseModule } from './infra/database/database.module';
|
||||
import { AdminModule } from './modules/admin/admin.module';
|
||||
import { AuthModule } from './modules/auth/auth.module';
|
||||
import { WhatsappModule } from './modules/whatsapp/whatsapp.module';
|
||||
|
||||
@Module({
|
||||
imports: [DatabaseModule, AuthModule, AdminModule],
|
||||
imports: [DatabaseModule, AuthModule, AdminModule, WhatsappModule],
|
||||
controllers: [AppController],
|
||||
})
|
||||
export class AppModule {}
|
||||
|
||||
50
src/modules/whatsapp/whatsapp-assignment.service.ts
Normal file
50
src/modules/whatsapp/whatsapp-assignment.service.ts
Normal file
@ -0,0 +1,50 @@
|
||||
import { Injectable, Logger } from '@nestjs/common';
|
||||
import { DatabaseService } from '../../infra/database/database.service';
|
||||
|
||||
@Injectable()
|
||||
export class WhatsappAssignmentService {
|
||||
private readonly logger = new Logger(WhatsappAssignmentService.name);
|
||||
|
||||
constructor(private readonly db: DatabaseService) {}
|
||||
|
||||
async assignChat(chatId: string, userId: string, areaId?: string) {
|
||||
this.logger.log(`Atribuindo chat ${chatId} ao usuário ${userId}`);
|
||||
|
||||
const query = `
|
||||
INSERT INTO whatsapp_chat_atribuicoes (chat_id, user_id, area_id)
|
||||
VALUES ($1, $2, $3)
|
||||
ON CONFLICT (chat_id) DO UPDATE SET
|
||||
user_id = EXCLUDED.user_id,
|
||||
area_id = EXCLUDED.area_id,
|
||||
assigned_at = CURRENT_TIMESTAMP
|
||||
RETURNING *;
|
||||
`;
|
||||
|
||||
const result = await this.db.query(query, [chatId, userId, areaId]);
|
||||
return result.rows[0];
|
||||
}
|
||||
|
||||
async getAssignment(chatId: string) {
|
||||
const query = `SELECT * FROM whatsapp_chat_atribuicoes WHERE chat_id = $1`;
|
||||
const result = await this.db.query(query, [chatId]);
|
||||
return result.rows[0];
|
||||
}
|
||||
|
||||
async releaseChat(chatId: string) {
|
||||
this.logger.log(`Liberando chat ${chatId}`);
|
||||
const query = `DELETE FROM whatsapp_chat_atribuicoes WHERE chat_id = $1`;
|
||||
await this.db.query(query, [chatId]);
|
||||
}
|
||||
|
||||
async getChatsByArea(areaId: string) {
|
||||
const query = `SELECT * FROM whatsapp_chat_atribuicoes WHERE area_id = $1`;
|
||||
const result = await this.db.query(query, [areaId]);
|
||||
return result.rows;
|
||||
}
|
||||
|
||||
async getChatsByUser(userId: string) {
|
||||
const query = `SELECT * FROM whatsapp_chat_atribuicoes WHERE user_id = $1`;
|
||||
const result = await this.db.query(query, [userId]);
|
||||
return result.rows;
|
||||
}
|
||||
}
|
||||
51
src/modules/whatsapp/whatsapp.controller.ts
Normal file
51
src/modules/whatsapp/whatsapp.controller.ts
Normal file
@ -0,0 +1,51 @@
|
||||
import { Controller, Get, Post, Body, Delete, Param } from '@nestjs/common';
|
||||
import { WhatsappService } from './whatsapp.service';
|
||||
import { WhatsappAssignmentService } from './whatsapp-assignment.service';
|
||||
|
||||
@Controller('whatsapp')
|
||||
export class WhatsappController {
|
||||
constructor(
|
||||
private readonly whatsappService: WhatsappService,
|
||||
private readonly assignmentService: WhatsappAssignmentService
|
||||
) {}
|
||||
|
||||
@Get('status')
|
||||
getStatus() {
|
||||
return { status: this.whatsappService.getStatus() };
|
||||
}
|
||||
|
||||
@Get('chats')
|
||||
async getChats() {
|
||||
return this.whatsappService.getChats();
|
||||
}
|
||||
|
||||
@Get('messages/:chatId')
|
||||
async getChatMessages(@Param('chatId') chatId: string) {
|
||||
return this.whatsappService.getChatMessages(chatId);
|
||||
}
|
||||
|
||||
@Get('media/:chatId/:messageId')
|
||||
async getMessageMedia(@Param('chatId') chatId: string, @Param('messageId') messageId: string) {
|
||||
return this.whatsappService.getMessageMedia(chatId, messageId);
|
||||
}
|
||||
|
||||
@Post('send')
|
||||
async sendMessage(@Body() body: { to: string; message: string; media?: { data: string; mimetype: string; filename?: string } }) {
|
||||
return this.whatsappService.sendMessage(body.to, body.message, body.media);
|
||||
}
|
||||
|
||||
@Post('assign')
|
||||
async assignChat(@Body() body: { chatId: string; userId: string; areaId?: string }) {
|
||||
return this.assignmentService.assignChat(body.chatId, body.userId, body.areaId);
|
||||
}
|
||||
|
||||
@Delete('release/:chatId')
|
||||
async releaseChat(@Param('chatId') chatId: string) {
|
||||
return this.assignmentService.releaseChat(chatId);
|
||||
}
|
||||
|
||||
@Get('assignment/:chatId')
|
||||
async getAssignment(@Param('chatId') chatId: string) {
|
||||
return this.assignmentService.getAssignment(chatId);
|
||||
}
|
||||
}
|
||||
52
src/modules/whatsapp/whatsapp.gateway.ts
Normal file
52
src/modules/whatsapp/whatsapp.gateway.ts
Normal file
@ -0,0 +1,52 @@
|
||||
import {
|
||||
WebSocketGateway,
|
||||
WebSocketServer,
|
||||
OnGatewayConnection,
|
||||
OnGatewayDisconnect,
|
||||
SubscribeMessage,
|
||||
MessageBody,
|
||||
ConnectedSocket
|
||||
} from '@nestjs/websockets';
|
||||
import { Server, Socket } from 'socket.io';
|
||||
import { Logger } from '@nestjs/common';
|
||||
import * as QRCode from 'qrcode';
|
||||
|
||||
@WebSocketGateway({
|
||||
cors: {
|
||||
origin: '*',
|
||||
},
|
||||
namespace: '/whatsapp'
|
||||
})
|
||||
export class WhatsappGateway implements OnGatewayConnection, OnGatewayDisconnect {
|
||||
@WebSocketServer()
|
||||
server: Server;
|
||||
|
||||
private logger: Logger = new Logger('WhatsappGateway');
|
||||
// Hack to access service if needed, but normally injected via forwardRef or circular dep
|
||||
// To avoid circular dependency, we pass data directly from service to gateway methods.
|
||||
|
||||
handleConnection(client: Socket) {
|
||||
this.logger.log(`Client connected: ${client.id}`);
|
||||
}
|
||||
|
||||
handleDisconnect(client: Socket) {
|
||||
this.logger.log(`Client disconnected: ${client.id}`);
|
||||
}
|
||||
|
||||
async emitQrCode(qrString: string) {
|
||||
try {
|
||||
const qrImage = await QRCode.toDataURL(qrString);
|
||||
this.server.emit('qr', qrImage);
|
||||
} catch (err) {
|
||||
this.logger.error('Failed to generate QR code data URL', err);
|
||||
}
|
||||
}
|
||||
|
||||
emitStatus(status: string) {
|
||||
this.server.emit('status', status);
|
||||
}
|
||||
|
||||
emitNewMessage(message: any) {
|
||||
this.server.emit('message', message);
|
||||
}
|
||||
}
|
||||
12
src/modules/whatsapp/whatsapp.module.ts
Normal file
12
src/modules/whatsapp/whatsapp.module.ts
Normal file
@ -0,0 +1,12 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { WhatsappService } from './whatsapp.service';
|
||||
import { WhatsappGateway } from './whatsapp.gateway';
|
||||
import { WhatsappController } from './whatsapp.controller';
|
||||
import { WhatsappAssignmentService } from './whatsapp-assignment.service';
|
||||
|
||||
@Module({
|
||||
providers: [WhatsappService, WhatsappGateway, WhatsappAssignmentService],
|
||||
controllers: [WhatsappController],
|
||||
exports: [WhatsappService, WhatsappAssignmentService],
|
||||
})
|
||||
export class WhatsappModule {}
|
||||
310
src/modules/whatsapp/whatsapp.service.ts
Normal file
310
src/modules/whatsapp/whatsapp.service.ts
Normal file
@ -0,0 +1,310 @@
|
||||
import { Injectable, Logger, OnModuleInit } from '@nestjs/common';
|
||||
import { Client, LocalAuth, MessageMedia } from 'whatsapp-web.js';
|
||||
import { WhatsappGateway } from './whatsapp.gateway';
|
||||
import { WhatsappAssignmentService } from './whatsapp-assignment.service';
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
|
||||
@Injectable()
|
||||
export class WhatsappService implements OnModuleInit {
|
||||
private client: Client;
|
||||
private readonly logger = new Logger(WhatsappService.name);
|
||||
private status: 'DISCONNECTED' | 'AWAITING_QR' | 'CONNECTED' = 'DISCONNECTED';
|
||||
private currentQr: string | null = null;
|
||||
|
||||
constructor(
|
||||
private readonly gateway: WhatsappGateway,
|
||||
private readonly assignmentService: WhatsappAssignmentService
|
||||
) {}
|
||||
|
||||
onModuleInit() {
|
||||
this.logger.log('Inicializando WhatsApp Client...');
|
||||
|
||||
this.client = new Client({
|
||||
authStrategy: new LocalAuth({ dataPath: './whatsapp-session' }),
|
||||
puppeteer: {
|
||||
headless: true,
|
||||
executablePath: 'C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe',
|
||||
args: ['--no-sandbox', '--disable-setuid-sandbox', '--disable-dev-shm-usage', '--disable-accelerated-2d-canvas', '--no-first-run', '--disable-gpu']
|
||||
},
|
||||
webVersionCache: {
|
||||
type: 'none'
|
||||
}
|
||||
});
|
||||
|
||||
this.client.on('qr', (qr) => {
|
||||
this.logger.log('QR Code recebido. Envie para o frontend.');
|
||||
this.status = 'AWAITING_QR';
|
||||
this.currentQr = qr;
|
||||
this.gateway.emitQrCode(qr);
|
||||
this.gateway.emitStatus(this.status);
|
||||
});
|
||||
|
||||
this.client.on('ready', () => {
|
||||
this.logger.log('WhatsApp Web Conectado!');
|
||||
this.status = 'CONNECTED';
|
||||
this.currentQr = null;
|
||||
this.gateway.emitStatus(this.status);
|
||||
});
|
||||
|
||||
this.client.on('authenticated', () => {
|
||||
this.logger.log('WhatsApp Autenticado');
|
||||
});
|
||||
|
||||
this.client.on('auth_failure', (msg) => {
|
||||
this.logger.error('Falha na Autenticação do WhatsApp', msg);
|
||||
this.status = 'DISCONNECTED';
|
||||
this.gateway.emitStatus(this.status);
|
||||
});
|
||||
|
||||
this.client.on('disconnected', (reason) => {
|
||||
this.logger.warn('WhatsApp Desconectado', reason);
|
||||
this.status = 'DISCONNECTED';
|
||||
this.gateway.emitStatus(this.status);
|
||||
});
|
||||
|
||||
// Mudar para message_create captura tanto mensagens recebidas quanto enviadas no WhatsApp
|
||||
this.client.on('message_create', async (msg) => {
|
||||
if (msg.from === 'status@broadcast') return;
|
||||
|
||||
const remoteJid = msg.id.remote || (msg.fromMe ? msg.to : msg.from);
|
||||
this.logger.log(`Mensagem registrada (fromMe: ${msg.fromMe}) remote: ${remoteJid} - ${msg.body}`);
|
||||
|
||||
let mediaData: any = null;
|
||||
if (msg.hasMedia) {
|
||||
try {
|
||||
this.logger.log(`Baixando mídia em tempo real de ${msg.id._serialized}...`);
|
||||
const media = await msg.downloadMedia();
|
||||
if (media) {
|
||||
mediaData = {
|
||||
mimetype: media.mimetype,
|
||||
data: media.data,
|
||||
filename: media.filename || 'arquivo'
|
||||
};
|
||||
}
|
||||
} catch (err) {
|
||||
this.logger.error(`Erro ao baixar mídia em tempo real de ${msg.id._serialized}:`, err);
|
||||
}
|
||||
}
|
||||
|
||||
// Transmite a mensagem em tempo real para o frontend
|
||||
this.gateway.emitNewMessage({
|
||||
from: remoteJid,
|
||||
body: msg.body,
|
||||
timestamp: msg.timestamp,
|
||||
isGroupMsg: remoteJid.endsWith('@g.us'),
|
||||
id: msg.id._serialized,
|
||||
fromMe: msg.fromMe,
|
||||
notifyName: msg['_data']?.notifyName || '',
|
||||
hasMedia: msg.hasMedia,
|
||||
media: mediaData
|
||||
});
|
||||
|
||||
// Salva ou atualiza a conversa na persistência híbrida
|
||||
await this.addOrUpdatePersistentChat(remoteJid, {
|
||||
name: msg['_data']?.notifyName || remoteJid.split('@')[0],
|
||||
preview: msg.hasMedia ? `[Mídia: ${mediaData?.filename || 'Arquivo'}]` : (msg.body || '[Mídia]'),
|
||||
timestamp: msg.timestamp,
|
||||
unreadCount: msg.fromMe ? 0 : 1
|
||||
});
|
||||
});
|
||||
|
||||
this.client.initialize();
|
||||
}
|
||||
|
||||
private getPersistFilePath() {
|
||||
return path.join(process.cwd(), 'whatsapp-chats-persist.json');
|
||||
}
|
||||
|
||||
private async loadPersistentChats(): Promise<any> {
|
||||
try {
|
||||
const filepath = this.getPersistFilePath();
|
||||
if (!fs.existsSync(filepath)) {
|
||||
return {};
|
||||
}
|
||||
const data = fs.readFileSync(filepath, 'utf-8');
|
||||
return JSON.parse(data);
|
||||
} catch (err) {
|
||||
this.logger.error('Erro ao ler chats persistentes:', err);
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
private async savePersistentChats(chats: any): Promise<void> {
|
||||
try {
|
||||
const filepath = this.getPersistFilePath();
|
||||
fs.writeFileSync(filepath, JSON.stringify(chats, null, 2), 'utf-8');
|
||||
} catch (err) {
|
||||
this.logger.error('Erro ao salvar chats persistentes:', err);
|
||||
}
|
||||
}
|
||||
|
||||
private async addOrUpdatePersistentChat(chatId: string, data: { name?: string, preview?: string, timestamp?: number, unreadCount?: number }) {
|
||||
if (chatId === 'status@broadcast' || chatId.endsWith('@g.us')) return;
|
||||
|
||||
const chats = await this.loadPersistentChats();
|
||||
const existing = chats[chatId] || {};
|
||||
const isNumber = (val: string) => /^\d+$/.test(val);
|
||||
|
||||
let finalName = existing.name || data.name || chatId.split('@')[0];
|
||||
if (data.name && !isNumber(data.name)) {
|
||||
finalName = data.name;
|
||||
}
|
||||
|
||||
if (isNumber(finalName) && this.status === 'CONNECTED') {
|
||||
try {
|
||||
const contact = await this.client.getContactById(chatId);
|
||||
if (contact) {
|
||||
if (contact.name && !isNumber(contact.name)) {
|
||||
finalName = contact.name;
|
||||
} else if (contact.pushname && !isNumber(contact.pushname)) {
|
||||
finalName = contact.pushname;
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
// Ignorar erros na consulta do Puppeteer
|
||||
}
|
||||
}
|
||||
|
||||
chats[chatId] = {
|
||||
id: {
|
||||
server: chatId.split('@')[1] || 'c.us',
|
||||
user: chatId.split('@')[0],
|
||||
_serialized: chatId
|
||||
},
|
||||
name: finalName,
|
||||
isGroup: false,
|
||||
isReadOnly: false,
|
||||
unreadCount: data.unreadCount !== undefined ? data.unreadCount : (existing.unreadCount || 0),
|
||||
timestamp: data.timestamp || existing.timestamp || Math.floor(Date.now() / 1000),
|
||||
archived: false,
|
||||
pinned: false,
|
||||
isLocked: false,
|
||||
isMuted: false,
|
||||
preview: data.preview || existing.preview || ''
|
||||
};
|
||||
|
||||
await this.savePersistentChats(chats);
|
||||
}
|
||||
|
||||
getStatus() {
|
||||
return this.status;
|
||||
}
|
||||
|
||||
getCurrentQr() {
|
||||
return this.currentQr;
|
||||
}
|
||||
|
||||
async getChats() {
|
||||
if (this.status !== 'CONNECTED') return [];
|
||||
|
||||
let liveChats: any[] = [];
|
||||
try {
|
||||
liveChats = await this.client.getChats();
|
||||
} catch (err) {
|
||||
this.logger.error('Erro ao chamar client.getChats():', err);
|
||||
}
|
||||
|
||||
const persistentChatsObj = await this.loadPersistentChats();
|
||||
const mergedChatsMap = new Map<string, any>();
|
||||
|
||||
// Adiciona os chats persistentes locais primeiro
|
||||
Object.values(persistentChatsObj).forEach((c: any) => {
|
||||
mergedChatsMap.set(c.id._serialized, c);
|
||||
});
|
||||
|
||||
// Adiciona/Mescla com os chats em tempo real do Puppeteer
|
||||
liveChats.forEach((c: any) => {
|
||||
if (!c.isGroup && c.id.server === 'c.us') {
|
||||
const serializedId = c.id._serialized;
|
||||
const existingPersistent = persistentChatsObj[serializedId] || {};
|
||||
|
||||
const isNumber = (val: string) => /^\d+$/.test(val);
|
||||
let finalName = c.name || existingPersistent.name || c.id.user;
|
||||
if (isNumber(c.name) && existingPersistent.name && !isNumber(existingPersistent.name)) {
|
||||
finalName = existingPersistent.name;
|
||||
}
|
||||
|
||||
mergedChatsMap.set(serializedId, {
|
||||
id: c.id,
|
||||
name: finalName,
|
||||
isGroup: c.isGroup,
|
||||
isReadOnly: c.isReadOnly || false,
|
||||
unreadCount: c.unreadCount !== undefined ? c.unreadCount : (existingPersistent.unreadCount || 0),
|
||||
timestamp: c.timestamp || existingPersistent.timestamp || Math.floor(Date.now() / 1000),
|
||||
archived: c.archived || false,
|
||||
pinned: c.pinned || false,
|
||||
isLocked: c.isLocked || false,
|
||||
isMuted: c.isMuted || false,
|
||||
preview: c.lastMessage ? (c.lastMessage.body || '[Mídia]') : (existingPersistent.preview || '')
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
const conversas = Array.from(mergedChatsMap.values());
|
||||
|
||||
// Ordenar chats pelo timestamp mais recente
|
||||
conversas.sort((a, b) => b.timestamp - a.timestamp);
|
||||
|
||||
// Buscar todas as atribuições para enriquecer os chats
|
||||
return Promise.all(conversas.map(async chat => {
|
||||
const assignment = await this.assignmentService.getAssignment(chat.id._serialized);
|
||||
return {
|
||||
...chat,
|
||||
assignment: assignment || null
|
||||
};
|
||||
}));
|
||||
}
|
||||
|
||||
async getChatMessages(chatId: string) {
|
||||
if (this.status !== 'CONNECTED') return [];
|
||||
const chat = await this.client.getChatById(chatId);
|
||||
return chat.fetchMessages({ limit: 50 });
|
||||
}
|
||||
|
||||
async getMessageMedia(chatId: string, messageId: string) {
|
||||
if (this.status !== 'CONNECTED') throw new Error('WhatsApp não está conectado');
|
||||
|
||||
this.logger.log(`Buscando mídia do chat ${chatId}, mensagem ${messageId}`);
|
||||
const chat = await this.client.getChatById(chatId);
|
||||
const messages = await chat.fetchMessages({ limit: 50 });
|
||||
const msg = messages.find(m => m.id._serialized === messageId);
|
||||
|
||||
if (msg && msg.hasMedia) {
|
||||
this.logger.log(`Baixando mídia para mensagem ${messageId}...`);
|
||||
const media = await msg.downloadMedia();
|
||||
if (media) {
|
||||
return {
|
||||
mimetype: media.mimetype,
|
||||
data: media.data,
|
||||
filename: media.filename || 'arquivo'
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
throw new Error('Mídia não encontrada para esta mensagem');
|
||||
}
|
||||
|
||||
async sendMessage(to: string, message: string, media?: { data: string; mimetype: string; filename?: string }) {
|
||||
if (this.status !== 'CONNECTED') throw new Error('WhatsApp não está conectado');
|
||||
|
||||
let sentMsg;
|
||||
if (media) {
|
||||
this.logger.log(`Enviando mídia para ${to}: ${media.filename} (${media.mimetype})`);
|
||||
const messageMedia = new MessageMedia(media.mimetype, media.data, media.filename);
|
||||
sentMsg = await this.client.sendMessage(to, messageMedia, { caption: message });
|
||||
} else {
|
||||
sentMsg = await this.client.sendMessage(to, message);
|
||||
}
|
||||
|
||||
// Sincronizar na persistência também!
|
||||
await this.addOrUpdatePersistentChat(to, {
|
||||
name: to.split('@')[0],
|
||||
preview: media ? `[Mídia: ${media.filename || 'Arquivo'}]` : message,
|
||||
timestamp: Math.floor(Date.now() / 1000),
|
||||
unreadCount: 0
|
||||
});
|
||||
|
||||
return sentMsg;
|
||||
}
|
||||
}
|
||||
20
test-api.js
Normal file
20
test-api.js
Normal file
@ -0,0 +1,20 @@
|
||||
const fs = require('fs');
|
||||
const fetch = require('node-fetch') || global.fetch;
|
||||
|
||||
async function run() {
|
||||
try {
|
||||
const chatsRes = await fetch('http://localhost:3001/whatsapp/chats');
|
||||
const chats = await chatsRes.json();
|
||||
if (chats.length > 0) {
|
||||
const firstChatId = chats[0].id._serialized;
|
||||
const msgsRes = await fetch(`http://localhost:3001/whatsapp/messages/${firstChatId}`);
|
||||
if (!msgsRes.ok) return;
|
||||
const msgs = await msgsRes.json();
|
||||
fs.writeFileSync('test-api-out.json', JSON.stringify(msgs, null, 2));
|
||||
console.log('Saved to test-api-out.json');
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Error:', err);
|
||||
}
|
||||
}
|
||||
run();
|
||||
15
test-chats.js
Normal file
15
test-chats.js
Normal file
@ -0,0 +1,15 @@
|
||||
const fs = require('fs');
|
||||
const fetch = require('node-fetch') || global.fetch;
|
||||
|
||||
async function run() {
|
||||
try {
|
||||
const chatsRes = await fetch('http://localhost:3001/whatsapp/chats');
|
||||
const chats = await chatsRes.json();
|
||||
console.log('Total chats fetched:', chats.length);
|
||||
fs.writeFileSync('all-chats-dump.json', JSON.stringify(chats, null, 2));
|
||||
console.log('Saved all-chats-dump.json');
|
||||
} catch (err) {
|
||||
console.error('Error:', err);
|
||||
}
|
||||
}
|
||||
run();
|
||||
Loading…
Reference in New Issue
Block a user