rez_core 2.3.1 → 2.3.4
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/dist/app.module.js +2 -0
- package/dist/app.module.js.map +1 -1
- package/dist/module/enterprise/repository/school.repository.js +0 -1
- package/dist/module/enterprise/repository/school.repository.js.map +1 -1
- package/dist/module/filter/repository/saved-filter.repository.js +0 -1
- package/dist/module/filter/repository/saved-filter.repository.js.map +1 -1
- package/dist/module/integration/controller/integration.controller.d.ts +8 -1
- package/dist/module/integration/controller/integration.controller.js +22 -0
- package/dist/module/integration/controller/integration.controller.js.map +1 -1
- package/dist/module/integration/dto/create-config.dto.d.ts +7 -0
- package/dist/module/integration/dto/create-config.dto.js +29 -1
- package/dist/module/integration/dto/create-config.dto.js.map +1 -1
- package/dist/module/integration/factories/sms.factory.d.ts +9 -1
- package/dist/module/integration/factories/sms.factory.js +36 -4
- package/dist/module/integration/factories/sms.factory.js.map +1 -1
- package/dist/module/integration/factories/whatsapp.factory.d.ts +5 -1
- package/dist/module/integration/factories/whatsapp.factory.js +14 -2
- package/dist/module/integration/factories/whatsapp.factory.js.map +1 -1
- package/dist/module/integration/integration.module.js +10 -0
- package/dist/module/integration/integration.module.js.map +1 -1
- package/dist/module/integration/service/integration.service.d.ts +7 -0
- package/dist/module/integration/service/integration.service.js +51 -0
- package/dist/module/integration/service/integration.service.js.map +1 -1
- package/dist/module/integration/strategies/sms/gupshup-sms.strategy.d.ts +9 -0
- package/dist/module/integration/strategies/sms/gupshup-sms.strategy.js +104 -0
- package/dist/module/integration/strategies/sms/gupshup-sms.strategy.js.map +1 -0
- package/dist/module/integration/strategies/sms/msg91-sms.strategy.d.ts +9 -0
- package/dist/module/integration/strategies/sms/msg91-sms.strategy.js +113 -0
- package/dist/module/integration/strategies/sms/msg91-sms.strategy.js.map +1 -0
- package/dist/module/integration/strategies/sms/tubelight-sms.strategy.d.ts +9 -0
- package/dist/module/integration/strategies/sms/tubelight-sms.strategy.js +109 -0
- package/dist/module/integration/strategies/sms/tubelight-sms.strategy.js.map +1 -0
- package/dist/module/integration/strategies/telephone/ozonetel-voice.strategy.d.ts +7 -2
- package/dist/module/integration/strategies/telephone/ozonetel-voice.strategy.js +52 -5
- package/dist/module/integration/strategies/telephone/ozonetel-voice.strategy.js.map +1 -1
- package/dist/module/integration/strategies/whatsapp/gupshup-whatsapp.strategy.d.ts +19 -0
- package/dist/module/integration/strategies/whatsapp/gupshup-whatsapp.strategy.js +257 -0
- package/dist/module/integration/strategies/whatsapp/gupshup-whatsapp.strategy.js.map +1 -0
- package/dist/module/integration/strategies/whatsapp/tubelight-whatsapp.strategy.d.ts +22 -0
- package/dist/module/integration/strategies/whatsapp/tubelight-whatsapp.strategy.js +295 -0
- package/dist/module/integration/strategies/whatsapp/tubelight-whatsapp.strategy.js.map +1 -0
- package/dist/module/layout_preference/service/layout_preference.service.js +0 -2
- package/dist/module/layout_preference/service/layout_preference.service.js.map +1 -1
- package/dist/module/listmaster/service/list-master.service.js +0 -1
- package/dist/module/listmaster/service/list-master.service.js.map +1 -1
- package/dist/module/mapper/controller/field-mapper.controller.d.ts +6 -0
- package/dist/module/mapper/controller/field-mapper.controller.js +45 -0
- package/dist/module/mapper/controller/field-mapper.controller.js.map +1 -0
- package/dist/module/mapper/dto/field-mapper.dto.d.ts +11 -0
- package/dist/module/mapper/dto/field-mapper.dto.js +8 -0
- package/dist/module/mapper/dto/field-mapper.dto.js.map +1 -0
- package/dist/module/mapper/entity/field-lovs.entity.d.ts +7 -0
- package/dist/module/mapper/entity/field-lovs.entity.js +38 -0
- package/dist/module/mapper/entity/field-lovs.entity.js.map +1 -0
- package/dist/module/mapper/entity/field-mapper.entity.d.ts +12 -0
- package/dist/module/mapper/entity/field-mapper.entity.js +58 -0
- package/dist/module/mapper/entity/field-mapper.entity.js.map +1 -0
- package/dist/module/mapper/mapper.module.d.ts +2 -0
- package/dist/module/mapper/mapper.module.js +38 -0
- package/dist/module/mapper/mapper.module.js.map +1 -0
- package/dist/module/mapper/repository/field-lovs.repository.d.ts +8 -0
- package/dist/module/mapper/repository/field-lovs.repository.js +41 -0
- package/dist/module/mapper/repository/field-lovs.repository.js.map +1 -0
- package/dist/module/mapper/repository/field-mapper.repository.d.ts +9 -0
- package/dist/module/mapper/repository/field-mapper.repository.js +42 -0
- package/dist/module/mapper/repository/field-mapper.repository.js.map +1 -0
- package/dist/module/mapper/service/field-mapper.service.d.ts +16 -0
- package/dist/module/mapper/service/field-mapper.service.js +92 -0
- package/dist/module/mapper/service/field-mapper.service.js.map +1 -0
- package/dist/module/master/service/master.service.js +0 -2
- package/dist/module/master/service/master.service.js.map +1 -1
- package/dist/module/meta/repository/entity-table.repository.js +0 -1
- package/dist/module/meta/repository/entity-table.repository.js.map +1 -1
- package/dist/module/meta/service/entity-dynamic.service.js +0 -1
- package/dist/module/meta/service/entity-dynamic.service.js.map +1 -1
- package/dist/module/meta/service/entity-service-impl.service.js +0 -2
- package/dist/module/meta/service/entity-service-impl.service.js.map +1 -1
- package/dist/module/meta/service/entity-table.service.js +0 -2
- package/dist/module/meta/service/entity-table.service.js.map +1 -1
- package/dist/module/meta/service/field-group.service.js +0 -1
- package/dist/module/meta/service/field-group.service.js.map +1 -1
- package/dist/module/module/service/module-access.service.js +0 -6
- package/dist/module/module/service/module-access.service.js.map +1 -1
- package/dist/module/notification/service/email.service.js +0 -1
- package/dist/module/notification/service/email.service.js.map +1 -1
- package/dist/module/user/service/login.service.js +0 -1
- package/dist/module/user/service/login.service.js.map +1 -1
- package/dist/module/workflow/entity/action.entity.d.ts +2 -0
- package/dist/module/workflow/entity/action.entity.js +8 -0
- package/dist/module/workflow/entity/action.entity.js.map +1 -1
- package/dist/module/workflow/repository/stage.repository.js +0 -1
- package/dist/module/workflow/repository/stage.repository.js.map +1 -1
- package/dist/module/workflow/service/action.service.js +0 -1
- package/dist/module/workflow/service/action.service.js.map +1 -1
- package/dist/module/workflow/service/comm-template.service.js +0 -1
- package/dist/module/workflow/service/comm-template.service.js.map +1 -1
- package/dist/module/workflow/service/populate-workflow.service.js +0 -5
- package/dist/module/workflow/service/populate-workflow.service.js.map +1 -1
- package/dist/module/workflow/service/task.service.js +0 -1
- package/dist/module/workflow/service/task.service.js.map +1 -1
- package/dist/module/workflow/service/workflow-meta.service.js +0 -2
- package/dist/module/workflow/service/workflow-meta.service.js.map +1 -1
- package/dist/module/workflow-automation/service/workflow-automation-engine.service.js +0 -1
- package/dist/module/workflow-automation/service/workflow-automation-engine.service.js.map +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/docs/modules/integration.md +55 -1
- package/package.json +1 -1
- package/src/app.module.ts +2 -0
- package/src/module/enterprise/repository/school.repository.ts +0 -1
- package/src/module/filter/repository/saved-filter.repository.ts +0 -1
- package/src/module/integration/controller/integration.controller.ts +24 -0
- package/src/module/integration/dto/create-config.dto.ts +22 -0
- package/src/module/integration/factories/sms.factory.ts +39 -6
- package/src/module/integration/factories/whatsapp.factory.ts +10 -0
- package/src/module/integration/integration.module.ts +14 -0
- package/src/module/integration/service/integration.service.ts +107 -0
- package/src/module/integration/strategies/sms/gupshup-sms.strategy.ts +146 -0
- package/src/module/integration/strategies/sms/msg91-sms.strategy.ts +164 -0
- package/src/module/integration/strategies/sms/tubelight-sms.strategy.ts +163 -0
- package/src/module/integration/strategies/telephone/ozonetel-voice.strategy.ts +80 -7
- package/src/module/integration/strategies/whatsapp/gupshup-whatsapp.strategy.ts +360 -0
- package/src/module/integration/strategies/whatsapp/tubelight-whatsapp.strategy.ts +419 -0
- package/src/module/layout_preference/service/layout_preference.service.ts +0 -2
- package/src/module/listmaster/service/list-master.service.ts +1 -10
- package/src/module/mapper/controller/field-mapper.controller.ts +19 -0
- package/src/module/mapper/dto/field-mapper.dto.ts +14 -0
- package/src/module/mapper/entity/field-lovs.entity.ts +21 -0
- package/src/module/mapper/entity/field-mapper.entity.ts +36 -0
- package/src/module/mapper/mapper.module.ts +26 -0
- package/src/module/mapper/repository/field-lovs.repository.ts +22 -0
- package/src/module/mapper/repository/field-mapper.repository.ts +27 -0
- package/src/module/mapper/service/field-mapper.service.ts +91 -0
- package/src/module/master/service/master.service.ts +0 -2
- package/src/module/meta/repository/entity-table.repository.ts +0 -1
- package/src/module/meta/service/entity-dynamic.service.ts +1 -5
- package/src/module/meta/service/entity-service-impl.service.ts +0 -4
- package/src/module/meta/service/entity-table.service.ts +0 -2
- package/src/module/meta/service/field-group.service.ts +0 -1
- package/src/module/module/service/module-access.service.ts +0 -13
- package/src/module/notification/service/email.service.ts +0 -1
- package/src/module/user/service/login.service.ts +0 -1
- package/src/module/workflow/entity/action.entity.ts +6 -0
- package/src/module/workflow/repository/stage.repository.ts +1 -2
- package/src/module/workflow/service/action.service.ts +0 -1
- package/src/module/workflow/service/comm-template.service.ts +0 -1
- package/src/module/workflow/service/populate-workflow.service.ts +2 -12
- package/src/module/workflow/service/task.service.ts +0 -1
- package/src/module/workflow/service/workflow-meta.service.ts +1 -4
- package/src/module/workflow-automation/service/workflow-automation-engine.service.ts +0 -1
- package/src/resources/dev.properties.yaml +1 -1
- package/.vscode/extensions.json +0 -5
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import { Injectable, Logger } from '@nestjs/common';
|
|
2
|
+
import axios, { AxiosResponse } from 'axios';
|
|
3
|
+
import {
|
|
4
|
+
IntegrationStrategy,
|
|
5
|
+
IntegrationResult,
|
|
6
|
+
} from '../integration.strategy';
|
|
7
|
+
|
|
8
|
+
interface Msg91SmsConfig {
|
|
9
|
+
authKey: string;
|
|
10
|
+
senderId: string;
|
|
11
|
+
route?: string; // Route like '4' for transactional, '1' for promotional
|
|
12
|
+
baseUrl?: string;
|
|
13
|
+
dltTemplateId?: string;
|
|
14
|
+
entityId?: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
interface Msg91SmsApiResponse {
|
|
18
|
+
type: string;
|
|
19
|
+
message?: string;
|
|
20
|
+
request_id?: string;
|
|
21
|
+
error?: {
|
|
22
|
+
code: string;
|
|
23
|
+
message: string;
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
@Injectable()
|
|
28
|
+
export class Msg91SmsStrategy implements IntegrationStrategy {
|
|
29
|
+
private readonly logger = new Logger(Msg91SmsStrategy.name);
|
|
30
|
+
private readonly defaultBaseUrl = 'https://api.msg91.com/api/v5';
|
|
31
|
+
|
|
32
|
+
async sendMessage(
|
|
33
|
+
to: string,
|
|
34
|
+
message: string,
|
|
35
|
+
config: any,
|
|
36
|
+
): Promise<IntegrationResult> {
|
|
37
|
+
try {
|
|
38
|
+
if (!this.validateConfig(config)) {
|
|
39
|
+
throw new Error('Invalid MSG91 SMS configuration');
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const msg91Config = config as Msg91SmsConfig;
|
|
43
|
+
const baseUrl = msg91Config.baseUrl || this.defaultBaseUrl;
|
|
44
|
+
const url = `${baseUrl}/flow`;
|
|
45
|
+
|
|
46
|
+
const payload = {
|
|
47
|
+
sender: msg91Config.senderId,
|
|
48
|
+
route: msg91Config.route || '4', // Default to transactional
|
|
49
|
+
country: '91', // Default to India
|
|
50
|
+
sms: [
|
|
51
|
+
{
|
|
52
|
+
message: message,
|
|
53
|
+
to: [this.formatPhoneNumber(to)],
|
|
54
|
+
},
|
|
55
|
+
],
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
// Add DLT Template ID if provided
|
|
59
|
+
if (msg91Config.dltTemplateId) {
|
|
60
|
+
payload['DLT_TE_ID'] = msg91Config.dltTemplateId;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Add Entity ID if provided
|
|
64
|
+
if (msg91Config.entityId) {
|
|
65
|
+
payload['DLT_PE_ID'] = msg91Config.entityId;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const response: AxiosResponse<Msg91SmsApiResponse> = await axios.post(
|
|
69
|
+
url,
|
|
70
|
+
payload,
|
|
71
|
+
{
|
|
72
|
+
headers: {
|
|
73
|
+
authkey: msg91Config.authKey,
|
|
74
|
+
'Content-Type': 'application/json',
|
|
75
|
+
},
|
|
76
|
+
timeout: 30000,
|
|
77
|
+
},
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
if (
|
|
81
|
+
response.data?.type === 'success' ||
|
|
82
|
+
response.status === 200
|
|
83
|
+
) {
|
|
84
|
+
const messageId =
|
|
85
|
+
response.data?.request_id || new Date().getTime().toString();
|
|
86
|
+
|
|
87
|
+
this.logger.log(
|
|
88
|
+
`MSG91 SMS sent successfully to ${to}, messageId: ${messageId}`,
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
return {
|
|
92
|
+
success: true,
|
|
93
|
+
messageId: messageId,
|
|
94
|
+
provider: 'msg91',
|
|
95
|
+
service: 'SMS',
|
|
96
|
+
timestamp: new Date(),
|
|
97
|
+
};
|
|
98
|
+
} else {
|
|
99
|
+
throw new Error(
|
|
100
|
+
response.data?.error?.message ||
|
|
101
|
+
response.data?.message ||
|
|
102
|
+
'Failed to send SMS via MSG91',
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
} catch (error) {
|
|
106
|
+
this.logger.error(
|
|
107
|
+
`Failed to send MSG91 SMS to ${to}`,
|
|
108
|
+
error.response?.data || error.message,
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
return {
|
|
112
|
+
success: false,
|
|
113
|
+
provider: 'msg91',
|
|
114
|
+
service: 'SMS',
|
|
115
|
+
error: this.extractErrorMessage(error),
|
|
116
|
+
timestamp: new Date(),
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
private formatPhoneNumber(phoneNumber: string): string {
|
|
122
|
+
// Remove any non-digit characters except +
|
|
123
|
+
let formatted = phoneNumber.replace(/[^\d+]/g, '');
|
|
124
|
+
|
|
125
|
+
// Remove + if present
|
|
126
|
+
formatted = formatted.replace(/\+/g, '');
|
|
127
|
+
|
|
128
|
+
// Remove country code if it's 91 (India) as MSG91 handles it separately
|
|
129
|
+
if (formatted.startsWith('91')) {
|
|
130
|
+
formatted = formatted.substring(2);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return formatted;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
private extractErrorMessage(error: any): string {
|
|
137
|
+
if (error.response?.data?.error?.message) {
|
|
138
|
+
return error.response.data.error.message;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
if (error.response?.data?.message) {
|
|
142
|
+
return error.response.data.message;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
if (error.message) {
|
|
146
|
+
return error.message;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
return 'Unknown MSG91 SMS API error';
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
validateConfig(config: any): boolean {
|
|
153
|
+
if (!config || typeof config !== 'object') {
|
|
154
|
+
return false;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Required fields
|
|
158
|
+
if (!config.authKey || !config.senderId) {
|
|
159
|
+
return false;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
return true;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import { Injectable, Logger } from '@nestjs/common';
|
|
2
|
+
import axios, { AxiosResponse } from 'axios';
|
|
3
|
+
import {
|
|
4
|
+
IntegrationStrategy,
|
|
5
|
+
IntegrationResult,
|
|
6
|
+
} from '../integration.strategy';
|
|
7
|
+
|
|
8
|
+
interface TubelightSmsConfig {
|
|
9
|
+
userName: string;
|
|
10
|
+
password: string;
|
|
11
|
+
tenantId: string;
|
|
12
|
+
sourceNumber: string;
|
|
13
|
+
baseUrl?: string;
|
|
14
|
+
senderId?: string;
|
|
15
|
+
dltTemplateId?: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
interface TubelightSmsApiResponse {
|
|
19
|
+
status: string;
|
|
20
|
+
statusCode?: number;
|
|
21
|
+
messageId?: string;
|
|
22
|
+
data?: {
|
|
23
|
+
messageId: string;
|
|
24
|
+
status: string;
|
|
25
|
+
};
|
|
26
|
+
message?: string;
|
|
27
|
+
error?: string;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
@Injectable()
|
|
31
|
+
export class TubelightSmsStrategy implements IntegrationStrategy {
|
|
32
|
+
private readonly logger = new Logger(TubelightSmsStrategy.name);
|
|
33
|
+
private readonly defaultBaseUrl = 'https://portal.tubelightcommunications.com/sms/api/v1';
|
|
34
|
+
|
|
35
|
+
async sendMessage(
|
|
36
|
+
to: string,
|
|
37
|
+
message: string,
|
|
38
|
+
config: any,
|
|
39
|
+
): Promise<IntegrationResult> {
|
|
40
|
+
try {
|
|
41
|
+
if (!this.validateConfig(config)) {
|
|
42
|
+
throw new Error('Invalid Tubelight SMS configuration');
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const tubelightConfig = config as TubelightSmsConfig;
|
|
46
|
+
const baseUrl = tubelightConfig.baseUrl || this.defaultBaseUrl;
|
|
47
|
+
const url = `${baseUrl}/send`;
|
|
48
|
+
|
|
49
|
+
// Create Basic Auth token
|
|
50
|
+
const authToken = Buffer.from(
|
|
51
|
+
`${tubelightConfig.userName}:${tubelightConfig.password}`,
|
|
52
|
+
).toString('base64');
|
|
53
|
+
|
|
54
|
+
const payload = {
|
|
55
|
+
tenantId: tubelightConfig.tenantId,
|
|
56
|
+
from: tubelightConfig.senderId || tubelightConfig.sourceNumber,
|
|
57
|
+
to: this.formatPhoneNumber(to),
|
|
58
|
+
message: message,
|
|
59
|
+
dltTemplateId: tubelightConfig.dltTemplateId,
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
const response: AxiosResponse<TubelightSmsApiResponse> = await axios.post(
|
|
63
|
+
url,
|
|
64
|
+
payload,
|
|
65
|
+
{
|
|
66
|
+
headers: {
|
|
67
|
+
Authorization: `Basic ${authToken}`,
|
|
68
|
+
'Content-Type': 'application/json',
|
|
69
|
+
},
|
|
70
|
+
timeout: 30000,
|
|
71
|
+
},
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
if (
|
|
75
|
+
response.data?.status === 'success' ||
|
|
76
|
+
response.data?.statusCode === 200 ||
|
|
77
|
+
response.data?.data?.status === 'sent'
|
|
78
|
+
) {
|
|
79
|
+
const messageId =
|
|
80
|
+
response.data?.messageId ||
|
|
81
|
+
response.data?.data?.messageId ||
|
|
82
|
+
new Date().getTime().toString();
|
|
83
|
+
|
|
84
|
+
this.logger.log(
|
|
85
|
+
`Tubelight SMS sent successfully to ${to}, messageId: ${messageId}`,
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
return {
|
|
89
|
+
success: true,
|
|
90
|
+
messageId: messageId,
|
|
91
|
+
provider: 'tubelight',
|
|
92
|
+
service: 'SMS',
|
|
93
|
+
timestamp: new Date(),
|
|
94
|
+
};
|
|
95
|
+
} else {
|
|
96
|
+
throw new Error(
|
|
97
|
+
response.data?.error ||
|
|
98
|
+
response.data?.message ||
|
|
99
|
+
'Failed to send SMS via Tubelight',
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
} catch (error) {
|
|
103
|
+
this.logger.error(
|
|
104
|
+
`Failed to send Tubelight SMS to ${to}`,
|
|
105
|
+
error.response?.data || error.message,
|
|
106
|
+
);
|
|
107
|
+
|
|
108
|
+
return {
|
|
109
|
+
success: false,
|
|
110
|
+
provider: 'tubelight',
|
|
111
|
+
service: 'SMS',
|
|
112
|
+
error: this.extractErrorMessage(error),
|
|
113
|
+
timestamp: new Date(),
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
private formatPhoneNumber(phoneNumber: string): string {
|
|
119
|
+
// Remove any non-digit characters except +
|
|
120
|
+
let formatted = phoneNumber.replace(/[^\d+]/g, '');
|
|
121
|
+
|
|
122
|
+
// Ensure it has + prefix
|
|
123
|
+
if (!formatted.startsWith('+')) {
|
|
124
|
+
formatted = `+${formatted}`;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return formatted;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
private extractErrorMessage(error: any): string {
|
|
131
|
+
if (error.response?.data?.error) {
|
|
132
|
+
return error.response.data.error;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if (error.response?.data?.message) {
|
|
136
|
+
return error.response.data.message;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (error.message) {
|
|
140
|
+
return error.message;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return 'Unknown Tubelight SMS API error';
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
validateConfig(config: any): boolean {
|
|
147
|
+
if (!config || typeof config !== 'object') {
|
|
148
|
+
return false;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Required fields
|
|
152
|
+
if (
|
|
153
|
+
!config.userName ||
|
|
154
|
+
!config.password ||
|
|
155
|
+
!config.tenantId ||
|
|
156
|
+
!config.sourceNumber
|
|
157
|
+
) {
|
|
158
|
+
return false;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
return true;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
@@ -16,6 +16,19 @@ interface OzonetelVoiceConfig {
|
|
|
16
16
|
external_user_id?: string;
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
+
interface AgentDetailsResponse {
|
|
20
|
+
status: string;
|
|
21
|
+
message: Array<{
|
|
22
|
+
AgentId: string;
|
|
23
|
+
AgentName: string;
|
|
24
|
+
State: string;
|
|
25
|
+
PhoneNumber: string;
|
|
26
|
+
Skill: string;
|
|
27
|
+
Mode: string;
|
|
28
|
+
Since: string;
|
|
29
|
+
}>;
|
|
30
|
+
}
|
|
31
|
+
|
|
19
32
|
@Injectable()
|
|
20
33
|
export class OzonetelVoiceStrategy implements IntegrationStrategy {
|
|
21
34
|
private readonly baseUrl = 'https://in1-ccaas-api.ozonetel.com';
|
|
@@ -49,6 +62,19 @@ export class OzonetelVoiceStrategy implements IntegrationStrategy {
|
|
|
49
62
|
};
|
|
50
63
|
}
|
|
51
64
|
|
|
65
|
+
// Check agent status before making the call
|
|
66
|
+
const agentStatus = await this.checkAgentStatus(config, token);
|
|
67
|
+
if (!agentStatus.isReady) {
|
|
68
|
+
return {
|
|
69
|
+
success: false,
|
|
70
|
+
provider: 'ozonetel',
|
|
71
|
+
service: 'THIRD_PARTY',
|
|
72
|
+
error: agentStatus.error ||
|
|
73
|
+
`Agent is not ready. Current state: ${agentStatus.state || 'Unknown'}. Please login to your agent dashboard.`,
|
|
74
|
+
timestamp: new Date(),
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
52
78
|
// Then make the manual dial call
|
|
53
79
|
const url = `${this.baseUrl}/ca_apis/AgentManualDial`;
|
|
54
80
|
|
|
@@ -56,7 +82,7 @@ export class OzonetelVoiceStrategy implements IntegrationStrategy {
|
|
|
56
82
|
url,
|
|
57
83
|
{
|
|
58
84
|
userName: config.userName,
|
|
59
|
-
agentID: config.external_user_id
|
|
85
|
+
agentID: config.external_user_id,
|
|
60
86
|
campaignName: config.campaignName, // Always from config
|
|
61
87
|
customerNumber: to,
|
|
62
88
|
UCID: true,
|
|
@@ -95,7 +121,7 @@ export class OzonetelVoiceStrategy implements IntegrationStrategy {
|
|
|
95
121
|
}
|
|
96
122
|
}
|
|
97
123
|
|
|
98
|
-
|
|
124
|
+
async generateToken(
|
|
99
125
|
config: OzonetelVoiceConfig,
|
|
100
126
|
): Promise<string | null> {
|
|
101
127
|
try {
|
|
@@ -121,6 +147,53 @@ export class OzonetelVoiceStrategy implements IntegrationStrategy {
|
|
|
121
147
|
}
|
|
122
148
|
}
|
|
123
149
|
|
|
150
|
+
async checkAgentStatus(
|
|
151
|
+
config: OzonetelVoiceConfig,
|
|
152
|
+
token: string,
|
|
153
|
+
): Promise<{ isReady: boolean; state?: string; error?: string }> {
|
|
154
|
+
try {
|
|
155
|
+
const url = `${this.baseUrl}/ca_apis/AgentDetails`;
|
|
156
|
+
|
|
157
|
+
const response = await axios.post<AgentDetailsResponse>(
|
|
158
|
+
url,
|
|
159
|
+
{
|
|
160
|
+
userName: config.userName,
|
|
161
|
+
agentId: config.external_user_id,
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
headers: {
|
|
165
|
+
apiKey: config.apiKey,
|
|
166
|
+
'Content-Type': 'application/json',
|
|
167
|
+
Authorization: `Bearer ${token}`,
|
|
168
|
+
},
|
|
169
|
+
},
|
|
170
|
+
);
|
|
171
|
+
|
|
172
|
+
if (response.data?.status === 'success' && response.data?.message?.length > 0) {
|
|
173
|
+
const agentInfo = response.data.message[0];
|
|
174
|
+
const isReady = agentInfo.State?.toUpperCase() === 'READY';
|
|
175
|
+
|
|
176
|
+
return {
|
|
177
|
+
isReady,
|
|
178
|
+
state: agentInfo.State,
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
return {
|
|
183
|
+
isReady: false,
|
|
184
|
+
error: 'Failed to retrieve agent details',
|
|
185
|
+
};
|
|
186
|
+
} catch (error) {
|
|
187
|
+
console.error('Failed to check agent status:', error);
|
|
188
|
+
return {
|
|
189
|
+
isReady: false,
|
|
190
|
+
error: axios.isAxiosError(error)
|
|
191
|
+
? error.response?.data?.message || error.message
|
|
192
|
+
: 'Unknown error checking agent status',
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
124
197
|
validateConfig(config: Partial<OzonetelVoiceConfig>): boolean {
|
|
125
198
|
return !!(
|
|
126
199
|
config &&
|
|
@@ -133,24 +206,24 @@ export class OzonetelVoiceStrategy implements IntegrationStrategy {
|
|
|
133
206
|
/**
|
|
134
207
|
* Create configuration with user agent data merged with admin credentials
|
|
135
208
|
* Admin credentials (apiKey, userName, campaignName) stay in base config
|
|
136
|
-
* Only agent-specific info (
|
|
209
|
+
* Only agent-specific info (external_user_id) comes from user integration
|
|
137
210
|
*/
|
|
138
211
|
createUserSpecificConfig(
|
|
139
212
|
baseConfig: Pick<
|
|
140
213
|
OzonetelVoiceConfig,
|
|
141
214
|
'apiKey' | 'userName' | 'campaignName' | 'agentLoginUrl'
|
|
142
215
|
>,
|
|
143
|
-
|
|
216
|
+
externalUserId?: string,
|
|
144
217
|
): OzonetelVoiceConfig {
|
|
145
|
-
if (!
|
|
218
|
+
if (!externalUserId) {
|
|
146
219
|
throw new Error(
|
|
147
|
-
'
|
|
220
|
+
'External user ID is required for Ozonetel voice calls',
|
|
148
221
|
);
|
|
149
222
|
}
|
|
150
223
|
|
|
151
224
|
return {
|
|
152
225
|
...baseConfig,
|
|
153
|
-
|
|
226
|
+
external_user_id: externalUserId,
|
|
154
227
|
};
|
|
155
228
|
}
|
|
156
229
|
}
|