Skip to content

Commit 4f880a5

Browse files
Merge pull request #5 from KeyValueSoftwareSystems/develop
Add support to send awesome template messages
2 parents c213a14 + 122a6ff commit 4f880a5

File tree

6 files changed

+292
-101
lines changed

6 files changed

+292
-101
lines changed

README.md

Lines changed: 36 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,13 @@ yarn add siren-ts-sdk
2121
## Basic Usage
2222

2323
```typescript
24-
import { SirenClient } from 'siren-ts-sdk';
24+
import { SirenClient, ProviderCode } from 'siren-ts-sdk';
2525

26-
// Initialize using environment variables SIREN_API_KEY (and SIREN_ENV if available)
26+
// Initialize using environment variables SIREN_API_KEY (and optional SIREN_ENV)
2727
const client = new SirenClient();
2828

2929
// --- Send a direct message (no template) ---
3030
const directMessageId = await client.message.send(
31-
'direct',
3231
3332
'EMAIL',
3433
'Your account has been successfully verified. You can now access all features.'
@@ -37,14 +36,42 @@ console.log('Sent direct message:', directMessageId);
3736

3837
// --- Send a message using a template ---
3938
const templatedMessageId = await client.message.send(
40-
'direct',
41-
'U01UBCD06BB',
42-
'SLACK',
43-
undefined,
44-
'welcome_template',
39+
'U01UBCD06BB',
40+
'SLACK',
41+
undefined, // body
42+
'welcome_template', // template name
4543
{ user_name: 'John' }
4644
);
4745
console.log('Sent template message:', templatedMessageId);
46+
47+
// --- Send with a specific provider ---
48+
const providerMessageId = await client.message.send(
49+
50+
'EMAIL',
51+
'Your account has been successfully verified.',
52+
undefined,
53+
undefined,
54+
'email-provider', // provider name
55+
ProviderCode.EMAIL_SENDGRID // provider code
56+
);
57+
console.log('Sent with provider:', providerMessageId);
58+
59+
// --- Send using an awesome template identifier ---
60+
const awesomeMessageId = await client.message.sendAwesomeTemplate(
61+
'U01UBCD06BB',
62+
'SLACK',
63+
'awesome-templates/customer-support/escalation_required/official/casual.yaml',
64+
{
65+
ticket_id: '123456',
66+
customer_name: 'John',
67+
issue_summary: 'Payment processing issue',
68+
ticket_url: 'https://support.company.com/ticket/123456',
69+
sender_name: 'Support Team'
70+
},
71+
'slack-provider',
72+
ProviderCode.SLACK
73+
);
74+
console.log('Sent awesome template:', awesomeMessageId);
4875
```
4976

5077
## SDK Methods
@@ -64,6 +91,7 @@ The Siren TypeScript SDK provides a clean, namespaced interface to interact with
6491

6592
**Messaging** (`client.message.*`)
6693
- **`client.message.send()`** - Sends a message (with or without using a template) to a recipient via a chosen channel
94+
- **`client.message.send_awesome_template()`** - Sends a message using a template path/identifier
6795
- **`client.message.getReplies()`** - Retrieves replies for a specific message ID
6896
- **`client.message.getStatus()`** - Retrieves the status of a specific message (SENT, DELIVERED, FAILED, etc.)
6997

examples/messaging-usage.ts

Lines changed: 33 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import 'dotenv/config';
2-
import { SirenClient } from '../src';
2+
import { SirenClient, ProviderCode } from '../src';
33
import { SirenAPIError, SirenError } from '../src/common/errors';
44

55
const apiToken = process.env.SIREN_API_KEY;
@@ -12,32 +12,57 @@ const client = new SirenClient({ apiToken, env: 'dev' });
1212

1313
async function messagingExamples() {
1414
try {
15+
// Send direct message without template
16+
const simpleMessageId = await client.message.send(
17+
'U01UBCD06BB', // Slack user ID
18+
'SLACK',
19+
'Hello! This is a direct message without specifying template/provider.',
20+
);
21+
console.log('Message sent:', simpleMessageId);
22+
23+
1524
// Send direct message without template
1625
const messageId = await client.message.send(
17-
'direct',
18-
'U01UBCD06BB',
26+
'U01UBCD06BB', // Slack user ID
1927
'SLACK',
20-
'Hello! This is a direct message without template.'
28+
'Hello! This is a direct message without template.',
29+
undefined, // templateName
30+
undefined, // templateVariables
31+
'slack-test-py-sdk', // provider name (optional)
32+
ProviderCode.SLACK // provider code (optional)
2133
);
2234
console.log('Message sent:', messageId);
2335

2436
// Send message using template
2537
const templateMessageId = await client.message.send(
26-
'direct',
2738
'U01UBCD06BB',
2839
'SLACK',
2940
undefined,
3041
'sampleTemplate',
31-
{ user_name: 'Alan' }
42+
{ user_name: 'Alan' },
3243
);
3344
console.log('Template message sent:', templateMessageId);
3445

46+
// // Send message using awesome template identifier
47+
const awesomeMessageId = await client.message.sendAwesomeTemplate(
48+
'U01UBCD06BB',
49+
'SLACK',
50+
'awesome-templates/customer-support/refund_complete/official/default.yaml',
51+
{
52+
"reference_id": "123456",
53+
"customer_name": "John"
54+
},
55+
'slack-test-py-sdk',
56+
ProviderCode.SLACK
57+
);
58+
console.log('Awesome template message sent:', awesomeMessageId);
59+
3560
// Get message status
36-
const status = await client.message.getStatus(messageId);
61+
const status = await client.message.getStatus(awesomeMessageId);
3762
console.log('Message status:', status);
3863

3964
// Get message replies
40-
const replies = await client.message.getReplies(messageId);
65+
const replies = await client.message.getReplies(awesomeMessageId);
4166
console.log('Message replies:', replies);
4267
} catch (error) {
4368
if (error instanceof SirenAPIError) {

src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,4 @@ export * from './template/types';
55
export * from './workflow/types';
66
export * from './common/types';
77
export * from './common/errors';
8+
export * from './messaging/types';

src/messaging/client.ts

Lines changed: 104 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,88 +2,172 @@ import { BaseClient } from '../base/client';
22
import { SirenConfig } from '../common/types';
33
import { SirenValidationError } from '../common/errors';
44
import {
5-
RecipientType,
6-
ReplyData,
5+
ProviderCode,
6+
ProviderIntegration,
7+
Recipient,
78
SendMessageRequest,
89
MessageData,
9-
StatusData
10+
StatusData,
11+
ReplyData
1012
} from './types';
1113

14+
/** Mapping between channel names and recipient field keys */
15+
const CHANNEL_RECIPIENT_KEY: Record<string, keyof Recipient> = {
16+
EMAIL: 'email',
17+
SMS: 'sms',
18+
WHATSAPP: 'whatsapp',
19+
SLACK: 'slack',
20+
TEAMS: 'teams',
21+
DISCORD: 'discord',
22+
LINE: 'line',
23+
IN_APP: 'inApp',
24+
PUSH: 'pushToken'
25+
};
26+
1227
export class MessageClient extends BaseClient {
1328
constructor(config: SirenConfig) {
1429
super(config);
1530
}
1631

1732
/**
1833
* Send a message either using a template or directly.
19-
* @param recipientType - The type of recipient ("user_id" or "direct")
20-
* @param recipientValue - The identifier for the recipient (e.g., Slack user ID, email address)
21-
* @param channel - The channel to send the message through (e.g., "SLACK", "EMAIL")
22-
* @param body - Optional message body text (required if no template)
34+
* @param recipientValue - Identifier for the recipient (e.g., Slack user ID, email address)
35+
* @param channel - Channel to send the message through (e.g., "SLACK", "EMAIL")
36+
* @param body - Optional raw body text (required if no template)
2337
* @param templateName - Optional template name (required if no body)
24-
* @param templateVariables - Optional template variables for template-based messages
25-
* @returns The notification ID of the sent message
38+
* @param templateVariables - Optional variables for template-based messages
39+
* @param providerName - Optional provider integration name (must be provided with providerCode)
40+
* @param providerCode - Optional provider integration code (must be provided with providerName)
41+
* @returns Notification ID of the sent message
2642
*/
2743
async send(
28-
recipientType: RecipientType,
2944
recipientValue: string,
3045
channel: string,
3146
body?: string,
3247
templateName?: string,
33-
templateVariables?: Record<string, any>
48+
templateVariables?: Record<string, any>,
49+
providerName?: string,
50+
providerCode?: ProviderCode
3451
): Promise<string> {
3552
if (!body && !templateName) {
3653
throw new SirenValidationError('Either body or templateName must be provided');
3754
}
3855

56+
if ((providerName !== undefined) !== (providerCode !== undefined)) {
57+
throw new SirenValidationError('Both providerName and providerCode must be provided together');
58+
}
59+
60+
const recipient = this.createRecipient(channel, recipientValue);
61+
3962
const payload: SendMessageRequest = {
40-
recipient: {
41-
type: recipientType,
42-
value: recipientValue
43-
},
63+
recipient,
4464
channel
4565
};
4666

4767
if (body) {
4868
payload.body = body;
4969
} else if (templateName) {
5070
payload.template = { name: templateName };
71+
}
72+
5173
if (templateVariables) {
5274
payload.templateVariables = templateVariables;
53-
}
75+
}
76+
77+
if (providerName && providerCode) {
78+
payload.providerIntegration = {
79+
name: providerName,
80+
code: providerCode
81+
} as ProviderIntegration;
5482
}
5583

5684
const response = await this.makeRequest<SendMessageRequest, MessageData>(
5785
'POST',
5886
'/api/v1/public/send-messages',
5987
payload
6088
);
89+
90+
return response.data!.notificationId;
91+
}
92+
93+
/**
94+
* Send a message using an awesome template path/identifier.
95+
*/
96+
async sendAwesomeTemplate(
97+
recipientValue: string,
98+
channel: string,
99+
templateIdentifier: string,
100+
templateVariables?: Record<string, any>,
101+
providerName?: string,
102+
providerCode?: ProviderCode
103+
): Promise<string> {
104+
if ((providerName !== undefined) !== (providerCode !== undefined)) {
105+
throw new SirenValidationError('Both providerName and providerCode must be provided together');
106+
}
107+
108+
const recipient = this.createRecipient(channel, recipientValue);
109+
110+
const payload: SendMessageRequest = {
111+
channel,
112+
templateIdentifier,
113+
recipient
114+
};
115+
116+
if (templateVariables) {
117+
payload.templateVariables = templateVariables;
118+
}
119+
120+
if (providerName && providerCode) {
121+
payload.providerIntegration = {
122+
name: providerName,
123+
code: providerCode
124+
} as ProviderIntegration;
125+
}
126+
127+
const response = await this.makeRequest<SendMessageRequest, MessageData>(
128+
'POST',
129+
'/api/v1/public/send-awesome-messages',
130+
payload
131+
);
132+
61133
return response.data!.notificationId;
62134
}
63135

64136
/**
65137
* Retrieve the status of a specific message.
66-
* @param messageId - The ID of the message for which to retrieve the status
67-
* @returns The status of the message (e.g., "DELIVERED", "PENDING")
68138
*/
69139
async getStatus(messageId: string): Promise<string> {
70140
const response = await this.makeRequest<null, StatusData>(
71141
'GET',
72142
`/api/v1/public/message-status/${messageId}`
73143
);
144+
74145
return response.data!.status;
75146
}
76147

77148
/**
78149
* Retrieve replies for a specific message.
79-
* @param messageId - The ID of the message for which to retrieve replies
80-
* @returns A list of reply objects containing message details
81150
*/
82151
async getReplies(messageId: string): Promise<ReplyData[]> {
83152
const response = await this.makeRequest<null, ReplyData[]>(
84153
'GET',
85154
`/api/v1/public/get-reply/${messageId}`
86155
);
156+
87157
return response.data!;
88158
}
159+
160+
/**
161+
* Create the recipient object for the payload based on channel.
162+
*/
163+
private createRecipient(channel: string, recipientValue: string): Recipient {
164+
const key = CHANNEL_RECIPIENT_KEY[channel.toUpperCase()];
165+
if (!key) {
166+
throw new SirenValidationError(`Unsupported channel: ${channel}`);
167+
}
168+
169+
return {
170+
[key]: recipientValue
171+
} as Recipient;
172+
}
89173
}

0 commit comments

Comments
 (0)