@igoruehara/canvas-flow 0.1.13 → 0.1.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/public/index.html CHANGED
@@ -4,8 +4,8 @@
4
4
  <meta charset="UTF-8" />
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
6
  <title>Canvas Flow</title>
7
- <script type="module" crossorigin src="/assets/index-CAYKh0bn.js"></script>
8
- <link rel="stylesheet" crossorigin href="/assets/index-PCQkqMUe.css">
7
+ <script type="module" crossorigin src="/assets/index-BUVXJRGV.js"></script>
8
+ <link rel="stylesheet" crossorigin href="/assets/index-D-m2588h.css">
9
9
  </head>
10
10
  <body>
11
11
  <div id="root"></div>
@@ -15,6 +15,41 @@ export declare class ProviderConfigController {
15
15
  settings: any;
16
16
  secretStatus: Record<string, boolean>;
17
17
  }>;
18
+ completeWhatsappEmbeddedSignup(body: any, agentId?: string, authorization?: string, headerToken?: string, xApiKey?: string): Promise<{
19
+ onboarding: {
20
+ ok: boolean;
21
+ coexistenceEnabled: boolean;
22
+ businessAccountId: string;
23
+ phoneNumberId: string;
24
+ phoneNumber: string;
25
+ tokenExchange: any;
26
+ subscribe: {
27
+ skipped: boolean;
28
+ reason: string;
29
+ ok?: undefined;
30
+ status?: undefined;
31
+ error?: undefined;
32
+ body?: undefined;
33
+ } | {
34
+ ok: boolean;
35
+ status: number;
36
+ error: any;
37
+ body: any;
38
+ skipped?: undefined;
39
+ reason?: undefined;
40
+ } | {
41
+ ok: boolean;
42
+ status: number;
43
+ body: any;
44
+ skipped?: undefined;
45
+ reason?: undefined;
46
+ error?: undefined;
47
+ };
48
+ };
49
+ providerStatus?: Record<import("./provider-config-service").ProviderConfigSection, any>;
50
+ settings: any;
51
+ secretStatus: Record<string, boolean>;
52
+ }>;
18
53
  clearConfigSection(section: string, agentId?: string, authorization?: string, headerToken?: string, xApiKey?: string): Promise<{
19
54
  providerStatus?: Record<import("./provider-config-service").ProviderConfigSection, any>;
20
55
  settings: any;
@@ -33,6 +33,10 @@ let ProviderConfigController = class ProviderConfigController {
33
33
  const user = await this.assertAuth(authorization, headerToken, xApiKey);
34
34
  return await this.service.updateSettings(body?.settings || body || {}, user?.id, body?.agentId || agentId);
35
35
  }
36
+ async completeWhatsappEmbeddedSignup(body, agentId, authorization, headerToken, xApiKey) {
37
+ const user = await this.assertAuth(authorization, headerToken, xApiKey);
38
+ return await this.service.completeWhatsappEmbeddedSignup(body || {}, user?.id, body?.agentId || agentId);
39
+ }
36
40
  async clearConfigSection(section, agentId, authorization, headerToken, xApiKey) {
37
41
  const user = await this.assertAuth(authorization, headerToken, xApiKey);
38
42
  return await this.service.clearSection(section, user?.id, agentId);
@@ -60,6 +64,17 @@ __decorate([
60
64
  __metadata("design:paramtypes", [Object, String, String, String, String]),
61
65
  __metadata("design:returntype", Promise)
62
66
  ], ProviderConfigController.prototype, "updateConfig", null);
67
+ __decorate([
68
+ (0, common_1.Post)('whatsapp/embedded-signup'),
69
+ __param(0, (0, common_1.Body)()),
70
+ __param(1, (0, common_1.Query)('agentId')),
71
+ __param(2, (0, common_1.Headers)('authorization')),
72
+ __param(3, (0, common_1.Headers)('x-canvas-flow-token')),
73
+ __param(4, (0, common_1.Headers)('x-api-key')),
74
+ __metadata("design:type", Function),
75
+ __metadata("design:paramtypes", [Object, String, String, String, String]),
76
+ __metadata("design:returntype", Promise)
77
+ ], ProviderConfigController.prototype, "completeWhatsappEmbeddedSignup", null);
63
78
  __decorate([
64
79
  (0, common_1.Delete)(':section'),
65
80
  __param(0, (0, common_1.Param)('section')),
@@ -1 +1 @@
1
- {"version":3,"file":"provider-config-controller.js","sourceRoot":"","sources":["../../src/provider-config/provider-config-controller.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AAAA,2CAA2F;AAC3F,6CAA0C;AAC1C,uDAAmD;AACnD,uEAAkE;AAI3D,IAAM,wBAAwB,GAA9B,MAAM,wBAAwB;IACnC,YACmB,OAA8B,EAC9B,WAAwB;QADxB,YAAO,GAAP,OAAO,CAAuB;QAC9B,gBAAW,GAAX,WAAW,CAAa;IACxC,CAAC;IAEI,KAAK,CAAC,UAAU,CAAC,aAAsB,EAAE,WAAoB,EAAE,OAAgB;QACrF,OAAO,MAAM,IAAI,CAAC,WAAW,CAAC,YAAY,CAAC,aAAa,EAAE,WAAW,EAAE,OAAO,CAAC,CAAC;IAClF,CAAC;IAGK,AAAN,KAAK,CAAC,SAAS,CACK,OAAgB,EACR,aAAsB,EAChB,WAAoB,EAC9B,OAAgB;QAEtC,MAAM,IAAI,CAAC,UAAU,CAAC,aAAa,EAAE,WAAW,EAAE,OAAO,CAAC,CAAC;QAC3D,OAAO,MAAM,IAAI,CAAC,OAAO,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC;IACrD,CAAC;IAGK,AAAN,KAAK,CAAC,YAAY,CACR,IAAS,EACC,OAAgB,EACR,aAAsB,EAChB,WAAoB,EAC9B,OAAgB;QAEtC,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,aAAa,EAAE,WAAW,EAAE,OAAO,CAAC,CAAC;QACxE,OAAO,MAAM,IAAI,CAAC,OAAO,CAAC,cAAc,CAAC,IAAI,EAAE,QAAQ,IAAI,IAAI,IAAI,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,IAAI,OAAO,CAAC,CAAC;IAC7G,CAAC;IAGK,AAAN,KAAK,CAAC,kBAAkB,CACJ,OAAe,EACf,OAAgB,EACR,aAAsB,EAChB,WAAoB,EAC9B,OAAgB;QAEtC,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,aAAa,EAAE,WAAW,EAAE,OAAO,CAAC,CAAC;QACxE,OAAO,MAAM,IAAI,CAAC,OAAO,CAAC,YAAY,CAAC,OAAO,EAAE,IAAI,EAAE,EAAE,EAAE,OAAO,CAAC,CAAC;IACrE,CAAC;CACF,CAAA;AA5CY,4DAAwB;AAW7B;IADL,IAAA,YAAG,GAAE;IAEH,WAAA,IAAA,cAAK,EAAC,SAAS,CAAC,CAAA;IAChB,WAAA,IAAA,gBAAO,EAAC,eAAe,CAAC,CAAA;IACxB,WAAA,IAAA,gBAAO,EAAC,qBAAqB,CAAC,CAAA;IAC9B,WAAA,IAAA,gBAAO,EAAC,WAAW,CAAC,CAAA;;;;yDAItB;AAGK;IADL,IAAA,YAAG,GAAE;IAEH,WAAA,IAAA,aAAI,GAAE,CAAA;IACN,WAAA,IAAA,cAAK,EAAC,SAAS,CAAC,CAAA;IAChB,WAAA,IAAA,gBAAO,EAAC,eAAe,CAAC,CAAA;IACxB,WAAA,IAAA,gBAAO,EAAC,qBAAqB,CAAC,CAAA;IAC9B,WAAA,IAAA,gBAAO,EAAC,WAAW,CAAC,CAAA;;;;4DAItB;AAGK;IADL,IAAA,eAAM,EAAC,UAAU,CAAC;IAEhB,WAAA,IAAA,cAAK,EAAC,SAAS,CAAC,CAAA;IAChB,WAAA,IAAA,cAAK,EAAC,SAAS,CAAC,CAAA;IAChB,WAAA,IAAA,gBAAO,EAAC,eAAe,CAAC,CAAA;IACxB,WAAA,IAAA,gBAAO,EAAC,qBAAqB,CAAC,CAAA;IAC9B,WAAA,IAAA,gBAAO,EAAC,WAAW,CAAC,CAAA;;;;kEAItB;mCA3CU,wBAAwB;IAFpC,IAAA,iBAAO,EAAC,iBAAiB,CAAC;IAC1B,IAAA,mBAAU,EAAC,qBAAqB,CAAC;qCAGJ,+CAAqB;QACjB,0BAAW;GAHhC,wBAAwB,CA4CpC"}
1
+ {"version":3,"file":"provider-config-controller.js","sourceRoot":"","sources":["../../src/provider-config/provider-config-controller.ts"],"names":[],"mappings":"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"}
@@ -104,12 +104,23 @@ export interface ProviderSettings {
104
104
  whatsapp: {
105
105
  provider: 'meta' | 'blip' | 'sinch';
106
106
  deliveryMode: 'provider' | 'apiResponse';
107
+ onboardingMode: 'manual' | 'embeddedSignup' | 'coexistence';
107
108
  autoReply: boolean;
108
109
  verifyToken: string;
109
110
  businessAccountId: string;
110
111
  phoneNumberId: string;
111
112
  accessToken: string;
112
113
  graphApiVersion: string;
114
+ coexistenceEnabled: boolean;
115
+ syncMessageEchoes: boolean;
116
+ syncHistory: boolean;
117
+ embeddedSignupAppId: string;
118
+ embeddedSignupConfigId: string;
119
+ embeddedSignupAppSecret: string;
120
+ embeddedSignupSolutionId: string;
121
+ embeddedSignupFeatureType: string;
122
+ embeddedSignupSessionInfoVersion: string;
123
+ embeddedSignupVersion: string;
113
124
  blipContractId: string;
114
125
  blipAuthorizationKey: string;
115
126
  sinchProjectId: string;
@@ -152,6 +163,8 @@ export declare class ProviderConfigService {
152
163
  private hasClaudeConfig;
153
164
  private hasGrokConfig;
154
165
  private hasBedrockConfig;
166
+ private hasMeaningfulOverride;
167
+ private hasSectionOverride;
155
168
  private hasSectionConfig;
156
169
  private buildProviderStatus;
157
170
  private hasEnvWebWidgetConfig;
@@ -164,6 +177,47 @@ export declare class ProviderConfigService {
164
177
  settings: any;
165
178
  secretStatus: Record<string, boolean>;
166
179
  }>;
180
+ private normalizeWhatsappGraphApiVersion;
181
+ private parseGraphResponse;
182
+ private exchangeWhatsappEmbeddedSignupCode;
183
+ private getWhatsappPhoneNumber;
184
+ private findWhatsappPhoneNumber;
185
+ private subscribeWhatsappWaba;
186
+ completeWhatsappEmbeddedSignup(body: any, updatedBy?: string, agentId?: string): Promise<{
187
+ onboarding: {
188
+ ok: boolean;
189
+ coexistenceEnabled: boolean;
190
+ businessAccountId: string;
191
+ phoneNumberId: string;
192
+ phoneNumber: string;
193
+ tokenExchange: any;
194
+ subscribe: {
195
+ skipped: boolean;
196
+ reason: string;
197
+ ok?: undefined;
198
+ status?: undefined;
199
+ error?: undefined;
200
+ body?: undefined;
201
+ } | {
202
+ ok: boolean;
203
+ status: number;
204
+ error: any;
205
+ body: any;
206
+ skipped?: undefined;
207
+ reason?: undefined;
208
+ } | {
209
+ ok: boolean;
210
+ status: number;
211
+ body: any;
212
+ skipped?: undefined;
213
+ reason?: undefined;
214
+ error?: undefined;
215
+ };
216
+ };
217
+ providerStatus?: Record<ProviderConfigSection, any>;
218
+ settings: any;
219
+ secretStatus: Record<string, boolean>;
220
+ }>;
167
221
  updateSettings(patch: any, updatedBy?: string, agentId?: string): Promise<{
168
222
  providerStatus?: Record<ProviderConfigSection, any>;
169
223
  settings: any;
@@ -31,10 +31,17 @@ const SECRET_PATHS = new Set([
31
31
  'azureSearch.apiKey',
32
32
  'mongodb.connectionString',
33
33
  'whatsapp.accessToken',
34
+ 'whatsapp.embeddedSignupAppSecret',
34
35
  'whatsapp.blipAuthorizationKey',
35
36
  'whatsapp.sinchAccessToken',
36
37
  'whatsapp.sinchServiceToken',
37
38
  ]);
39
+ const SINERGY_WHATSAPP_COEXISTENCE_PRESET = {
40
+ embeddedSignupAppId: '617497366521622',
41
+ embeddedSignupConfigId: '1952866105586018',
42
+ embeddedSignupSessionInfoVersion: '3',
43
+ embeddedSignupVersion: 'v3',
44
+ };
38
45
  let ProviderConfigService = class ProviderConfigService {
39
46
  constructor(model, configService) {
40
47
  this.model = model;
@@ -174,6 +181,7 @@ let ProviderConfigService = class ProviderConfigService {
174
181
  whatsapp: {
175
182
  provider: this.configService.get('WHATSAPP_PROVIDER') || 'meta',
176
183
  deliveryMode: this.configService.get('WHATSAPP_DELIVERY_MODE') || 'provider',
184
+ onboardingMode: this.configService.get('WHATSAPP_ONBOARDING_MODE') || 'manual',
177
185
  autoReply: this.configService.get('WHATSAPP_AUTO_REPLY') === undefined
178
186
  ? true
179
187
  : this.envFlag(this.configService.get('WHATSAPP_AUTO_REPLY')),
@@ -182,6 +190,18 @@ let ProviderConfigService = class ProviderConfigService {
182
190
  phoneNumberId: this.configService.get('WHATSAPP_PHONE_NUMBER_ID') || '',
183
191
  accessToken: this.configService.get('WHATSAPP_ACCESS_TOKEN') || '',
184
192
  graphApiVersion: this.configService.get('WHATSAPP_GRAPH_API_VERSION') || 'v20.0',
193
+ coexistenceEnabled: this.envFlag(this.configService.get('WHATSAPP_COEXISTENCE_ENABLED')),
194
+ syncMessageEchoes: this.configService.get('WHATSAPP_SYNC_MESSAGE_ECHOES') === undefined
195
+ ? true
196
+ : this.envFlag(this.configService.get('WHATSAPP_SYNC_MESSAGE_ECHOES')),
197
+ syncHistory: this.envFlag(this.configService.get('WHATSAPP_SYNC_HISTORY')),
198
+ embeddedSignupAppId: this.configService.get('WHATSAPP_EMBEDDED_SIGNUP_APP_ID') || SINERGY_WHATSAPP_COEXISTENCE_PRESET.embeddedSignupAppId,
199
+ embeddedSignupConfigId: this.configService.get('WHATSAPP_EMBEDDED_SIGNUP_CONFIG_ID') || SINERGY_WHATSAPP_COEXISTENCE_PRESET.embeddedSignupConfigId,
200
+ embeddedSignupAppSecret: this.configService.get('WHATSAPP_EMBEDDED_SIGNUP_APP_SECRET') || '',
201
+ embeddedSignupSolutionId: this.configService.get('WHATSAPP_EMBEDDED_SIGNUP_SOLUTION_ID') || '',
202
+ embeddedSignupFeatureType: this.configService.get('WHATSAPP_EMBEDDED_SIGNUP_FEATURE_TYPE') || '',
203
+ embeddedSignupSessionInfoVersion: this.configService.get('WHATSAPP_EMBEDDED_SIGNUP_SESSION_INFO_VERSION') || SINERGY_WHATSAPP_COEXISTENCE_PRESET.embeddedSignupSessionInfoVersion,
204
+ embeddedSignupVersion: this.configService.get('WHATSAPP_EMBEDDED_SIGNUP_VERSION') || SINERGY_WHATSAPP_COEXISTENCE_PRESET.embeddedSignupVersion,
185
205
  blipContractId: this.configService.get('BLIP_CONTRACT_ID') || '',
186
206
  blipAuthorizationKey: this.configService.get('BLIP_AUTHORIZATION_KEY') || '',
187
207
  sinchProjectId: this.configService.get('SINCH_PROJECT_ID') || '',
@@ -274,6 +294,9 @@ let ProviderConfigService = class ProviderConfigService {
274
294
  if (clean.whatsapp?.deliveryMode && !['provider', 'apiResponse'].includes(clean.whatsapp.deliveryMode)) {
275
295
  throw new common_1.BadRequestException('whatsapp.deliveryMode invalido.');
276
296
  }
297
+ if (clean.whatsapp?.onboardingMode && !['manual', 'embeddedSignup', 'coexistence'].includes(clean.whatsapp.onboardingMode)) {
298
+ throw new common_1.BadRequestException('whatsapp.onboardingMode invalido.');
299
+ }
277
300
  if (clean.whatsapp?.sinchApiMode && !['conversation', 'relay'].includes(clean.whatsapp.sinchApiMode)) {
278
301
  throw new common_1.BadRequestException('whatsapp.sinchApiMode invalido.');
279
302
  }
@@ -327,6 +350,23 @@ let ProviderConfigService = class ProviderConfigService {
327
350
  hasBedrockConfig(settings) {
328
351
  return Boolean(String(settings.bedrock?.apiKey || '').trim() && String(settings.bedrock?.baseUrl || '').trim());
329
352
  }
353
+ hasMeaningfulOverride(value) {
354
+ if (value === undefined || value === null || value === '')
355
+ return false;
356
+ if (Array.isArray(value))
357
+ return value.some((item) => this.hasMeaningfulOverride(item));
358
+ if (typeof value === 'object') {
359
+ return Object.values(value).some((item) => this.hasMeaningfulOverride(item));
360
+ }
361
+ return true;
362
+ }
363
+ hasSectionOverride(section, settings) {
364
+ if (!settings || typeof settings !== 'object')
365
+ return false;
366
+ if (!Object.prototype.hasOwnProperty.call(settings, section))
367
+ return false;
368
+ return this.hasMeaningfulOverride(settings[section]);
369
+ }
330
370
  hasSectionConfig(section, settings) {
331
371
  if (!settings || typeof settings !== 'object')
332
372
  return false;
@@ -385,24 +425,34 @@ let ProviderConfigService = class ProviderConfigService {
385
425
  buildProviderStatus(params) {
386
426
  const sections = ['openai', 'azureOpenai', 'gemini', 'claude', 'grok', 'bedrock', 'milvus', 'azureBlob', 'azureSearch', 'mongodb', 'webWidget', 'whatsapp'];
387
427
  const envSettings = params.envSettings || this.getEnvSettings();
428
+ const globalStored = params.globalStored || {};
429
+ const scopedStored = params.scopedStored || {};
430
+ const globalEffective = this.deepMergeFallback(envSettings, globalStored);
431
+ const scopedEffective = this.deepMergeFallback(globalEffective, scopedStored);
388
432
  const status = {};
389
433
  const scoped = String(params.agentId || '').trim();
390
434
  sections.forEach((section) => {
391
- const agentConfigured = scoped ? this.hasSectionConfig(section, params.scopedStored) : false;
392
- const globalConfigured = this.hasSectionConfig(section, params.globalStored);
435
+ const agentOverride = scoped ? this.hasSectionOverride(section, scopedStored) : false;
436
+ const globalOverride = this.hasSectionOverride(section, globalStored);
393
437
  const envConfigured = section === 'webWidget' ? this.hasEnvWebWidgetConfig() : this.hasSectionConfig(section, envSettings);
438
+ const effectiveSettings = scoped ? scopedEffective : globalEffective;
439
+ const configured = section === 'webWidget'
440
+ ? agentOverride || globalOverride || envConfigured
441
+ : this.hasSectionConfig(section, effectiveSettings);
394
442
  let source = 'none';
395
- if (agentConfigured)
396
- source = 'agent';
397
- else if (globalConfigured)
398
- source = 'global';
399
- else if (envConfigured)
400
- source = 'env';
443
+ if (configured) {
444
+ if (agentOverride)
445
+ source = 'agent';
446
+ else if (globalOverride)
447
+ source = 'global';
448
+ else if (envConfigured)
449
+ source = 'env';
450
+ }
401
451
  status[section] = {
402
- configured: source !== 'none',
452
+ configured,
403
453
  source,
404
- scopeConfigured: scoped ? agentConfigured : globalConfigured,
405
- inherited: scoped ? !agentConfigured && source !== 'none' : source === 'env',
454
+ scopeConfigured: scoped ? agentOverride : globalOverride,
455
+ inherited: scoped ? !agentOverride && source !== 'none' : !globalOverride && source === 'env',
406
456
  };
407
457
  });
408
458
  return status;
@@ -456,6 +506,16 @@ let ProviderConfigService = class ProviderConfigService {
456
506
  else if (bedrockConfigured)
457
507
  next.llmProvider = 'bedrock';
458
508
  }
509
+ const whatsapp = next.whatsapp || {};
510
+ const onboardingMode = String(whatsapp.onboardingMode || 'manual');
511
+ whatsapp.onboardingMode = ['manual', 'embeddedSignup', 'coexistence'].includes(onboardingMode)
512
+ ? onboardingMode
513
+ : 'manual';
514
+ whatsapp.coexistenceEnabled = whatsapp.onboardingMode === 'coexistence' || whatsapp.coexistenceEnabled === true;
515
+ whatsapp.syncMessageEchoes = whatsapp.syncMessageEchoes !== false;
516
+ whatsapp.syncHistory = whatsapp.syncHistory === true;
517
+ whatsapp.embeddedSignupSessionInfoVersion = String(whatsapp.embeddedSignupSessionInfoVersion || '3');
518
+ next.whatsapp = whatsapp;
459
519
  return next;
460
520
  }
461
521
  blankSection(section) {
@@ -537,12 +597,23 @@ let ProviderConfigService = class ProviderConfigService {
537
597
  whatsapp: {
538
598
  provider: 'meta',
539
599
  deliveryMode: 'provider',
600
+ onboardingMode: 'manual',
540
601
  autoReply: true,
541
602
  verifyToken: '',
542
603
  businessAccountId: '',
543
604
  phoneNumberId: '',
544
605
  accessToken: '',
545
606
  graphApiVersion: defaults.whatsapp.graphApiVersion || 'v20.0',
607
+ coexistenceEnabled: false,
608
+ syncMessageEchoes: true,
609
+ syncHistory: false,
610
+ embeddedSignupAppId: defaults.whatsapp.embeddedSignupAppId || SINERGY_WHATSAPP_COEXISTENCE_PRESET.embeddedSignupAppId,
611
+ embeddedSignupConfigId: defaults.whatsapp.embeddedSignupConfigId || SINERGY_WHATSAPP_COEXISTENCE_PRESET.embeddedSignupConfigId,
612
+ embeddedSignupAppSecret: '',
613
+ embeddedSignupSolutionId: defaults.whatsapp.embeddedSignupSolutionId || '',
614
+ embeddedSignupFeatureType: defaults.whatsapp.embeddedSignupFeatureType || '',
615
+ embeddedSignupSessionInfoVersion: defaults.whatsapp.embeddedSignupSessionInfoVersion || SINERGY_WHATSAPP_COEXISTENCE_PRESET.embeddedSignupSessionInfoVersion,
616
+ embeddedSignupVersion: defaults.whatsapp.embeddedSignupVersion || SINERGY_WHATSAPP_COEXISTENCE_PRESET.embeddedSignupVersion,
546
617
  blipContractId: '',
547
618
  blipAuthorizationKey: '',
548
619
  sinchProjectId: '',
@@ -600,6 +671,175 @@ let ProviderConfigService = class ProviderConfigService {
600
671
  }
601
672
  return this.maskSafe(await this.getEffectiveSettings(agentId), this.buildProviderStatus({ agentId, globalStored, scopedStored, envSettings: this.getEnvSettings() }));
602
673
  }
674
+ normalizeWhatsappGraphApiVersion(value) {
675
+ const raw = String(value || 'v20.0').trim() || 'v20.0';
676
+ return raw.startsWith('v') ? raw : `v${raw}`;
677
+ }
678
+ async parseGraphResponse(response) {
679
+ const body = await response.json().catch(() => ({}));
680
+ if (response.ok)
681
+ return body;
682
+ const message = body?.error?.message || body?.message || `Meta Graph API HTTP ${response.status}`;
683
+ const error = new common_1.BadRequestException(message);
684
+ error.graphBody = body;
685
+ error.graphStatus = response.status;
686
+ throw error;
687
+ }
688
+ async exchangeWhatsappEmbeddedSignupCode(params) {
689
+ const buildUrl = (includeRedirectUri) => {
690
+ const url = new URL(`https://graph.facebook.com/${params.graphApiVersion}/oauth/access_token`);
691
+ url.searchParams.set('client_id', params.appId);
692
+ url.searchParams.set('client_secret', params.appSecret);
693
+ url.searchParams.set('code', params.code);
694
+ if (includeRedirectUri && params.redirectUri)
695
+ url.searchParams.set('redirect_uri', params.redirectUri);
696
+ return url;
697
+ };
698
+ try {
699
+ const response = await fetch(buildUrl(Boolean(params.redirectUri)).toString());
700
+ return await this.parseGraphResponse(response);
701
+ }
702
+ catch (error) {
703
+ if (!params.redirectUri)
704
+ throw error;
705
+ const response = await fetch(buildUrl(false).toString());
706
+ return await this.parseGraphResponse(response);
707
+ }
708
+ }
709
+ async getWhatsappPhoneNumber(phoneNumberId, accessToken, graphApiVersion) {
710
+ if (!phoneNumberId || !accessToken)
711
+ return null;
712
+ const url = new URL(`https://graph.facebook.com/${graphApiVersion}/${encodeURIComponent(phoneNumberId)}`);
713
+ url.searchParams.set('fields', 'id,display_phone_number,verified_name,whatsapp_business_account');
714
+ const response = await fetch(url.toString(), {
715
+ headers: { Authorization: `Bearer ${accessToken}` },
716
+ });
717
+ return await this.parseGraphResponse(response).catch(() => null);
718
+ }
719
+ async findWhatsappPhoneNumber(wabaId, accessToken, graphApiVersion) {
720
+ if (!wabaId || !accessToken)
721
+ return null;
722
+ const url = new URL(`https://graph.facebook.com/${graphApiVersion}/${encodeURIComponent(wabaId)}/phone_numbers`);
723
+ url.searchParams.set('fields', 'id,display_phone_number,verified_name');
724
+ const response = await fetch(url.toString(), {
725
+ headers: { Authorization: `Bearer ${accessToken}` },
726
+ });
727
+ const body = await this.parseGraphResponse(response).catch(() => null);
728
+ return Array.isArray(body?.data) ? body.data[0] || null : null;
729
+ }
730
+ async subscribeWhatsappWaba(wabaId, accessToken, graphApiVersion) {
731
+ if (!wabaId || !accessToken)
732
+ return { skipped: true, reason: 'missing_waba_or_token' };
733
+ const response = await fetch(`https://graph.facebook.com/${graphApiVersion}/${encodeURIComponent(wabaId)}/subscribed_apps`, {
734
+ method: 'POST',
735
+ headers: { Authorization: `Bearer ${accessToken}` },
736
+ });
737
+ const body = await response.json().catch(() => ({}));
738
+ if (!response.ok) {
739
+ return {
740
+ ok: false,
741
+ status: response.status,
742
+ error: body?.error?.message || body?.message || `Meta Graph API HTTP ${response.status}`,
743
+ body,
744
+ };
745
+ }
746
+ return { ok: true, status: response.status, body };
747
+ }
748
+ async completeWhatsappEmbeddedSignup(body, updatedBy, agentId) {
749
+ if (this.model.db.readyState !== 1) {
750
+ throw new common_1.BadRequestException('MongoDB ainda nao esta conectado para salvar configuracoes.');
751
+ }
752
+ const effective = await this.getEffectiveSettings(agentId);
753
+ const whatsapp = effective.whatsapp || this.getEnvSettings().whatsapp;
754
+ const graphApiVersion = this.normalizeWhatsappGraphApiVersion(body?.graphApiVersion || whatsapp.graphApiVersion);
755
+ const appId = String(body?.appId || whatsapp.embeddedSignupAppId || '').trim();
756
+ const appSecret = String(body?.appSecret || whatsapp.embeddedSignupAppSecret || '').trim();
757
+ const code = String(body?.code || '').trim();
758
+ let accessToken = String(whatsapp.accessToken || '').trim();
759
+ let tokenExchange = { skipped: true, reason: 'missing_code_or_app_secret' };
760
+ if (code && appId && appSecret) {
761
+ const tokenResult = await this.exchangeWhatsappEmbeddedSignupCode({
762
+ appId,
763
+ appSecret,
764
+ code,
765
+ graphApiVersion,
766
+ redirectUri: String(body?.redirectUri || '').trim() || undefined,
767
+ });
768
+ accessToken = String(tokenResult?.access_token || accessToken || '').trim();
769
+ tokenExchange = {
770
+ ok: Boolean(accessToken),
771
+ tokenType: tokenResult?.token_type,
772
+ expiresIn: tokenResult?.expires_in,
773
+ };
774
+ }
775
+ let businessAccountId = String(body?.wabaId ||
776
+ body?.waba_id ||
777
+ body?.businessAccountId ||
778
+ whatsapp.businessAccountId ||
779
+ '').trim();
780
+ let phoneNumberId = String(body?.phoneNumberId ||
781
+ body?.phone_number_id ||
782
+ whatsapp.phoneNumberId ||
783
+ '').trim();
784
+ let phoneNumber = String(body?.phoneNumber || body?.displayPhoneNumber || '').trim();
785
+ if (phoneNumberId && accessToken) {
786
+ const phoneNumberInfo = await this.getWhatsappPhoneNumber(phoneNumberId, accessToken, graphApiVersion);
787
+ if (!phoneNumber)
788
+ phoneNumber = String(phoneNumberInfo?.display_phone_number || '').trim();
789
+ if (!businessAccountId) {
790
+ businessAccountId = String(phoneNumberInfo?.whatsapp_business_account?.id ||
791
+ phoneNumberInfo?.whatsapp_business_account ||
792
+ '').trim();
793
+ }
794
+ }
795
+ if (!phoneNumberId && businessAccountId && accessToken) {
796
+ const phoneNumberInfo = await this.findWhatsappPhoneNumber(businessAccountId, accessToken, graphApiVersion);
797
+ phoneNumberId = String(phoneNumberInfo?.id || '').trim();
798
+ phoneNumber = phoneNumber || String(phoneNumberInfo?.display_phone_number || '').trim();
799
+ }
800
+ if (!businessAccountId && !phoneNumberId) {
801
+ throw new common_1.BadRequestException('Embedded Signup nao retornou WABA ID nem Phone Number ID.');
802
+ }
803
+ const coexistenceEnabled = body?.coexistenceEnabled === true || whatsapp.coexistenceEnabled === true || whatsapp.onboardingMode === 'coexistence';
804
+ const subscribe = body?.subscribeWebhooks === false
805
+ ? { skipped: true, reason: 'disabled' }
806
+ : await this.subscribeWhatsappWaba(businessAccountId, accessToken, graphApiVersion);
807
+ const settingsPatch = {
808
+ whatsapp: {
809
+ provider: 'meta',
810
+ deliveryMode: whatsapp.deliveryMode || 'provider',
811
+ onboardingMode: coexistenceEnabled ? 'coexistence' : 'embeddedSignup',
812
+ coexistenceEnabled,
813
+ syncMessageEchoes: coexistenceEnabled ? true : whatsapp.syncMessageEchoes !== false,
814
+ syncHistory: whatsapp.syncHistory === true,
815
+ businessAccountId,
816
+ phoneNumberId,
817
+ accessToken,
818
+ graphApiVersion,
819
+ embeddedSignupAppId: appId || whatsapp.embeddedSignupAppId || '',
820
+ embeddedSignupConfigId: String(body?.configId || body?.configurationId || whatsapp.embeddedSignupConfigId || '').trim(),
821
+ embeddedSignupSolutionId: String(body?.solutionId || whatsapp.embeddedSignupSolutionId || '').trim(),
822
+ embeddedSignupFeatureType: String(body?.featureType || whatsapp.embeddedSignupFeatureType || '').trim(),
823
+ embeddedSignupSessionInfoVersion: String(body?.sessionInfoVersion || whatsapp.embeddedSignupSessionInfoVersion || '3').trim(),
824
+ embeddedSignupVersion: String(body?.embeddedSignupVersion || whatsapp.embeddedSignupVersion || '').trim(),
825
+ },
826
+ };
827
+ if (appSecret)
828
+ settingsPatch.whatsapp.embeddedSignupAppSecret = appSecret;
829
+ const safe = await this.updateSettings(settingsPatch, updatedBy, agentId);
830
+ return {
831
+ ...safe,
832
+ onboarding: {
833
+ ok: true,
834
+ coexistenceEnabled,
835
+ businessAccountId,
836
+ phoneNumberId,
837
+ phoneNumber,
838
+ tokenExchange,
839
+ subscribe,
840
+ },
841
+ };
842
+ }
603
843
  async updateSettings(patch, updatedBy, agentId) {
604
844
  if (this.model.db.readyState !== 1) {
605
845
  throw new common_1.BadRequestException('MongoDB ainda nao esta conectado para salvar configuracoes.');