rez_core 2.3.2 → 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/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/integration.module.js +6 -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/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/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/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/app.module.ts +2 -0
- 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/integration.module.ts +10 -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/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/workflow/entity/action.entity.ts +6 -0
package/package.json
CHANGED
package/src/app.module.ts
CHANGED
|
@@ -19,6 +19,7 @@ import { DashboardModule } from './module/dashboard/dashboard.module';
|
|
|
19
19
|
import { IntegrationModule } from './module/integration/integration.module';
|
|
20
20
|
import { ScheduleModule } from '@nestjs/schedule';
|
|
21
21
|
import { AuthModule } from './module/auth/auth.module';
|
|
22
|
+
import { MapperModule } from './module/mapper/mapper.module';
|
|
22
23
|
|
|
23
24
|
@Module({
|
|
24
25
|
imports: [
|
|
@@ -41,6 +42,7 @@ import { AuthModule } from './module/auth/auth.module';
|
|
|
41
42
|
DashboardModule,
|
|
42
43
|
IntegrationModule,
|
|
43
44
|
AuthModule,
|
|
45
|
+
MapperModule,
|
|
44
46
|
ScheduleModule.forRoot(),
|
|
45
47
|
],
|
|
46
48
|
})
|
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
import { IntegrationService } from '../service/integration.service';
|
|
16
16
|
import {
|
|
17
17
|
BulkMessageDto,
|
|
18
|
+
CheckAgentStatusDto,
|
|
18
19
|
CreateConfigDto,
|
|
19
20
|
CreateUserIntegrationDto,
|
|
20
21
|
GenericSendMessageDto,
|
|
@@ -69,6 +70,29 @@ export class IntegrationController {
|
|
|
69
70
|
return this.integrationService.sendTemplateMessage(templateMessage);
|
|
70
71
|
}
|
|
71
72
|
|
|
73
|
+
@Post('check-agent-status')
|
|
74
|
+
@HttpCode(HttpStatus.OK)
|
|
75
|
+
async checkAgentStatus(@Body() checkAgentStatusDto: CheckAgentStatusDto) {
|
|
76
|
+
try {
|
|
77
|
+
const result = await this.integrationService.checkAgentStatus(
|
|
78
|
+
checkAgentStatusDto.levelId,
|
|
79
|
+
checkAgentStatusDto.levelType,
|
|
80
|
+
checkAgentStatusDto.app_code,
|
|
81
|
+
checkAgentStatusDto.user_id,
|
|
82
|
+
checkAgentStatusDto.type || 'TELEPHONE',
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
return result;
|
|
86
|
+
} catch (error) {
|
|
87
|
+
throw new BadRequestException({
|
|
88
|
+
success: false,
|
|
89
|
+
error: 'CHECK_AGENT_STATUS_ERROR',
|
|
90
|
+
message: error.message || 'Failed to check agent status',
|
|
91
|
+
code: 'AGENT_STATUS_ERROR',
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
72
96
|
@Post('config')
|
|
73
97
|
@HttpCode(HttpStatus.OK)
|
|
74
98
|
async createConfig(@Body() createConfigDto: CreateConfigDto) {
|
|
@@ -473,3 +473,25 @@ export class UpdateUserIntegrationDto {
|
|
|
473
473
|
@IsBoolean()
|
|
474
474
|
is_active?: boolean;
|
|
475
475
|
}
|
|
476
|
+
|
|
477
|
+
export class CheckAgentStatusDto {
|
|
478
|
+
@IsNotEmpty()
|
|
479
|
+
@IsNumber()
|
|
480
|
+
levelId: number;
|
|
481
|
+
|
|
482
|
+
@IsNotEmpty()
|
|
483
|
+
@IsString()
|
|
484
|
+
levelType: string;
|
|
485
|
+
|
|
486
|
+
@IsNotEmpty()
|
|
487
|
+
@IsString()
|
|
488
|
+
app_code: string;
|
|
489
|
+
|
|
490
|
+
@IsNotEmpty()
|
|
491
|
+
@IsNumber()
|
|
492
|
+
user_id: number;
|
|
493
|
+
|
|
494
|
+
@IsOptional()
|
|
495
|
+
@IsIn(['TELEPHONE'])
|
|
496
|
+
type?: 'TELEPHONE';
|
|
497
|
+
}
|
|
@@ -1,19 +1,52 @@
|
|
|
1
1
|
import { Injectable } from '@nestjs/common';
|
|
2
|
+
import { BaseFactory } from './base.factory';
|
|
2
3
|
import { IntegrationStrategy } from '../strategies/integration.strategy';
|
|
4
|
+
import { GupshupSmsStrategy } from '../strategies/sms/gupshup-sms.strategy';
|
|
5
|
+
import { TubelightSmsStrategy } from '../strategies/sms/tubelight-sms.strategy';
|
|
6
|
+
import { Msg91SmsStrategy } from '../strategies/sms/msg91-sms.strategy';
|
|
3
7
|
|
|
4
8
|
@Injectable()
|
|
5
|
-
export class SmsFactory {
|
|
9
|
+
export class SmsFactory implements BaseFactory {
|
|
10
|
+
constructor(
|
|
11
|
+
private gupshupSmsStrategy: GupshupSmsStrategy,
|
|
12
|
+
private tubelightSmsStrategy: TubelightSmsStrategy,
|
|
13
|
+
private msg91SmsStrategy: Msg91SmsStrategy,
|
|
14
|
+
) {}
|
|
15
|
+
|
|
6
16
|
createProvider(service: string, provider: string): IntegrationStrategy {
|
|
7
|
-
|
|
17
|
+
const key = `${service.toLowerCase()}_${provider.toLowerCase()}`;
|
|
18
|
+
|
|
19
|
+
switch (key) {
|
|
20
|
+
case 'api_gupshup':
|
|
21
|
+
case 'sms_gupshup':
|
|
22
|
+
return this.gupshupSmsStrategy;
|
|
23
|
+
case 'api_tubelight':
|
|
24
|
+
case 'sms_tubelight':
|
|
25
|
+
return this.tubelightSmsStrategy;
|
|
26
|
+
case 'api_msg91':
|
|
27
|
+
case 'sms_msg91':
|
|
28
|
+
return this.msg91SmsStrategy;
|
|
29
|
+
|
|
30
|
+
default:
|
|
31
|
+
throw new Error(
|
|
32
|
+
`Unsupported SMS service/provider: ${service}/${provider}`,
|
|
33
|
+
);
|
|
34
|
+
}
|
|
8
35
|
}
|
|
9
36
|
|
|
10
37
|
getSupportedCombinations(): Array<{ service: string; provider: string }> {
|
|
11
|
-
|
|
12
|
-
|
|
38
|
+
return [
|
|
39
|
+
{ service: 'API', provider: 'gupshup' },
|
|
40
|
+
{ service: 'API', provider: 'tubelight' },
|
|
41
|
+
{ service: 'API', provider: 'msg91' },
|
|
42
|
+
];
|
|
13
43
|
}
|
|
14
44
|
|
|
15
45
|
validateCombination(service: string, provider: string): boolean {
|
|
16
|
-
|
|
17
|
-
|
|
46
|
+
return this.getSupportedCombinations().some(
|
|
47
|
+
(combo) =>
|
|
48
|
+
combo.service.toLowerCase() === service.toLowerCase() &&
|
|
49
|
+
combo.provider.toLowerCase() === provider.toLowerCase(),
|
|
50
|
+
);
|
|
18
51
|
}
|
|
19
52
|
}
|
|
@@ -20,6 +20,11 @@ import { TubelightVoiceStrategy } from './strategies/telephone/tubelight-voice.s
|
|
|
20
20
|
import { GupshupWhatsAppStrategy } from './strategies/whatsapp/gupshup-whatsapp.strategy';
|
|
21
21
|
import { TubelightWhatsAppStrategy } from './strategies/whatsapp/tubelight-whatsapp.strategy';
|
|
22
22
|
|
|
23
|
+
// SMS Strategies
|
|
24
|
+
import { GupshupSmsStrategy } from './strategies/sms/gupshup-sms.strategy';
|
|
25
|
+
import { TubelightSmsStrategy } from './strategies/sms/tubelight-sms.strategy';
|
|
26
|
+
import { Msg91SmsStrategy } from './strategies/sms/msg91-sms.strategy';
|
|
27
|
+
|
|
23
28
|
// New unified strategies
|
|
24
29
|
import { OutlookStrategy } from './strategies/email/outlook.strategy';
|
|
25
30
|
import { WhatsAppStrategy } from './strategies/whatsapp/whatsapp.strategy';
|
|
@@ -66,6 +71,11 @@ import { UserIntegration } from './entity/user-integration.entity';
|
|
|
66
71
|
GupshupWhatsAppStrategy,
|
|
67
72
|
TubelightWhatsAppStrategy,
|
|
68
73
|
|
|
74
|
+
// SMS Strategies
|
|
75
|
+
GupshupSmsStrategy,
|
|
76
|
+
TubelightSmsStrategy,
|
|
77
|
+
Msg91SmsStrategy,
|
|
78
|
+
|
|
69
79
|
// New unified strategies
|
|
70
80
|
OutlookStrategy,
|
|
71
81
|
WhatsAppStrategy,
|
|
@@ -2164,4 +2164,111 @@ export class IntegrationService {
|
|
|
2164
2164
|
return null;
|
|
2165
2165
|
}
|
|
2166
2166
|
}
|
|
2167
|
+
|
|
2168
|
+
/**
|
|
2169
|
+
* Check agent status for TELEPHONE integration
|
|
2170
|
+
* Currently supports: Ozonetel
|
|
2171
|
+
*/
|
|
2172
|
+
async checkAgentStatus(
|
|
2173
|
+
levelId: number,
|
|
2174
|
+
levelType: string,
|
|
2175
|
+
appCode: string,
|
|
2176
|
+
userId: number,
|
|
2177
|
+
integrationType: 'TELEPHONE' = 'TELEPHONE',
|
|
2178
|
+
): Promise<{
|
|
2179
|
+
success: boolean;
|
|
2180
|
+
isReady?: boolean;
|
|
2181
|
+
state?: string;
|
|
2182
|
+
agentInfo?: any;
|
|
2183
|
+
error?: string;
|
|
2184
|
+
}> {
|
|
2185
|
+
try {
|
|
2186
|
+
// Get the active configuration
|
|
2187
|
+
const config = await this.getSingleActiveConfig(
|
|
2188
|
+
levelId,
|
|
2189
|
+
levelType,
|
|
2190
|
+
appCode,
|
|
2191
|
+
integrationType,
|
|
2192
|
+
);
|
|
2193
|
+
|
|
2194
|
+
if (!config) {
|
|
2195
|
+
return {
|
|
2196
|
+
success: false,
|
|
2197
|
+
error: 'No active TELEPHONE configuration found',
|
|
2198
|
+
};
|
|
2199
|
+
}
|
|
2200
|
+
|
|
2201
|
+
// Get user integration mapping
|
|
2202
|
+
const userIntegration = await this.getUserIntegrationByUserAndConfig(
|
|
2203
|
+
userId,
|
|
2204
|
+
config.id,
|
|
2205
|
+
);
|
|
2206
|
+
|
|
2207
|
+
if (!userIntegration) {
|
|
2208
|
+
return {
|
|
2209
|
+
success: false,
|
|
2210
|
+
error: 'User integration mapping not found. Please configure agent details.',
|
|
2211
|
+
};
|
|
2212
|
+
}
|
|
2213
|
+
|
|
2214
|
+
// Create strategy instance
|
|
2215
|
+
const strategy = this.integrationFactory.create(
|
|
2216
|
+
config.integration_type,
|
|
2217
|
+
'API', // service is deprecated, using default
|
|
2218
|
+
config.integration_provider,
|
|
2219
|
+
);
|
|
2220
|
+
|
|
2221
|
+
// Get the merged config with user data
|
|
2222
|
+
let mergedConfig = config.config_json;
|
|
2223
|
+
|
|
2224
|
+
if (strategy.createUserSpecificConfig) {
|
|
2225
|
+
mergedConfig = strategy.createUserSpecificConfig(
|
|
2226
|
+
config.config_json,
|
|
2227
|
+
userIntegration.external_user_id,
|
|
2228
|
+
);
|
|
2229
|
+
}
|
|
2230
|
+
|
|
2231
|
+
// Check if strategy is Ozonetel (currently only Ozonetel supports status check)
|
|
2232
|
+
if (config.integration_provider.toLowerCase() === 'ozonetel') {
|
|
2233
|
+
const ozonetelStrategy = strategy as any;
|
|
2234
|
+
|
|
2235
|
+
// Generate token
|
|
2236
|
+
const token = await ozonetelStrategy.generateToken(mergedConfig);
|
|
2237
|
+
if (!token) {
|
|
2238
|
+
return {
|
|
2239
|
+
success: false,
|
|
2240
|
+
error: 'Failed to authenticate with telephone provider',
|
|
2241
|
+
};
|
|
2242
|
+
}
|
|
2243
|
+
|
|
2244
|
+
// Check agent status
|
|
2245
|
+
const statusResult = await ozonetelStrategy.checkAgentStatus(
|
|
2246
|
+
mergedConfig,
|
|
2247
|
+
token,
|
|
2248
|
+
);
|
|
2249
|
+
|
|
2250
|
+
return {
|
|
2251
|
+
success: true,
|
|
2252
|
+
isReady: statusResult.isReady,
|
|
2253
|
+
state: statusResult.state,
|
|
2254
|
+
error: statusResult.error,
|
|
2255
|
+
};
|
|
2256
|
+
}
|
|
2257
|
+
|
|
2258
|
+
// For other providers, return not supported
|
|
2259
|
+
return {
|
|
2260
|
+
success: false,
|
|
2261
|
+
error: `Agent status check not supported for provider: ${config.integration_provider}`,
|
|
2262
|
+
};
|
|
2263
|
+
} catch (error) {
|
|
2264
|
+
this.logger.error(
|
|
2265
|
+
`Error checking agent status: ${error.message}`,
|
|
2266
|
+
error.stack,
|
|
2267
|
+
);
|
|
2268
|
+
return {
|
|
2269
|
+
success: false,
|
|
2270
|
+
error: error.message || 'Failed to check agent status',
|
|
2271
|
+
};
|
|
2272
|
+
}
|
|
2273
|
+
}
|
|
2167
2274
|
}
|
|
@@ -0,0 +1,146 @@
|
|
|
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 GupshupSmsConfig {
|
|
9
|
+
apiKey: string;
|
|
10
|
+
sourceNumber: string;
|
|
11
|
+
baseUrl?: string;
|
|
12
|
+
method?: string; // 'SendMessage' or 'SendSMS'
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
interface GupshupSmsApiResponse {
|
|
16
|
+
status: string;
|
|
17
|
+
messageId?: string;
|
|
18
|
+
response?: {
|
|
19
|
+
id: string;
|
|
20
|
+
phone: string;
|
|
21
|
+
details: string;
|
|
22
|
+
};
|
|
23
|
+
error?: {
|
|
24
|
+
code: number;
|
|
25
|
+
message: string;
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
@Injectable()
|
|
30
|
+
export class GupshupSmsStrategy implements IntegrationStrategy {
|
|
31
|
+
private readonly logger = new Logger(GupshupSmsStrategy.name);
|
|
32
|
+
private readonly defaultBaseUrl = 'https://enterprise.smsgupshup.com/GatewayAPI/rest';
|
|
33
|
+
|
|
34
|
+
async sendMessage(
|
|
35
|
+
to: string,
|
|
36
|
+
message: string,
|
|
37
|
+
config: any,
|
|
38
|
+
): Promise<IntegrationResult> {
|
|
39
|
+
try {
|
|
40
|
+
if (!this.validateConfig(config)) {
|
|
41
|
+
throw new Error('Invalid Gupshup SMS configuration');
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const gupshupConfig = config as GupshupSmsConfig;
|
|
45
|
+
const baseUrl = gupshupConfig.baseUrl || this.defaultBaseUrl;
|
|
46
|
+
const method = gupshupConfig.method || 'SendMessage';
|
|
47
|
+
|
|
48
|
+
const params = new URLSearchParams();
|
|
49
|
+
params.append('method', method);
|
|
50
|
+
params.append('send_to', this.formatPhoneNumber(to));
|
|
51
|
+
params.append('msg', message);
|
|
52
|
+
params.append('msg_type', 'TEXT');
|
|
53
|
+
params.append('userid', gupshupConfig.apiKey);
|
|
54
|
+
params.append('auth_scheme', 'plain');
|
|
55
|
+
params.append('v', '1.1');
|
|
56
|
+
params.append('format', 'text');
|
|
57
|
+
|
|
58
|
+
const url = `${baseUrl}?${params.toString()}`;
|
|
59
|
+
|
|
60
|
+
const response: AxiosResponse = await axios.get(url, {
|
|
61
|
+
timeout: 30000,
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
// Gupshup SMS API returns text response like: "success | message-id"
|
|
65
|
+
const responseText = response.data?.toString() || '';
|
|
66
|
+
|
|
67
|
+
if (responseText.toLowerCase().includes('success')) {
|
|
68
|
+
const parts = responseText.split('|');
|
|
69
|
+
const messageId = parts.length > 1 ? parts[1].trim() : new Date().getTime().toString();
|
|
70
|
+
|
|
71
|
+
this.logger.log(
|
|
72
|
+
`Gupshup SMS sent successfully to ${to}, messageId: ${messageId}`,
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
return {
|
|
76
|
+
success: true,
|
|
77
|
+
messageId: messageId,
|
|
78
|
+
provider: 'gupshup',
|
|
79
|
+
service: 'SMS',
|
|
80
|
+
timestamp: new Date(),
|
|
81
|
+
};
|
|
82
|
+
} else {
|
|
83
|
+
throw new Error(responseText || 'Failed to send SMS via Gupshup');
|
|
84
|
+
}
|
|
85
|
+
} catch (error) {
|
|
86
|
+
this.logger.error(
|
|
87
|
+
`Failed to send Gupshup SMS to ${to}`,
|
|
88
|
+
error.response?.data || error.message,
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
return {
|
|
92
|
+
success: false,
|
|
93
|
+
provider: 'gupshup',
|
|
94
|
+
service: 'SMS',
|
|
95
|
+
error: this.extractErrorMessage(error),
|
|
96
|
+
timestamp: new Date(),
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
private formatPhoneNumber(phoneNumber: string): string {
|
|
102
|
+
// Remove any non-digit characters except +
|
|
103
|
+
let formatted = phoneNumber.replace(/[^\d+]/g, '');
|
|
104
|
+
|
|
105
|
+
// Remove + if present as Gupshup expects just numbers
|
|
106
|
+
formatted = formatted.replace(/\+/g, '');
|
|
107
|
+
|
|
108
|
+
return formatted;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
private extractErrorMessage(error: any): string {
|
|
112
|
+
if (error.response?.data) {
|
|
113
|
+
const data = error.response.data.toString();
|
|
114
|
+
if (data) {
|
|
115
|
+
return data;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (error.response?.data?.error?.message) {
|
|
120
|
+
return error.response.data.error.message;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
if (error.response?.data?.message) {
|
|
124
|
+
return error.response.data.message;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if (error.message) {
|
|
128
|
+
return error.message;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return 'Unknown Gupshup SMS API error';
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
validateConfig(config: any): boolean {
|
|
135
|
+
if (!config || typeof config !== 'object') {
|
|
136
|
+
return false;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Required fields
|
|
140
|
+
if (!config.apiKey || !config.sourceNumber) {
|
|
141
|
+
return false;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
return true;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
@@ -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
|
+
}
|