@flink-app/sms-plugin 0.12.1-alpha.4 → 0.12.1-alpha.40

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.
Files changed (2) hide show
  1. package/package.json +4 -4
  2. package/readme.md +367 -27
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "@flink-app/sms-plugin",
3
- "version": "0.12.1-alpha.4",
3
+ "version": "0.12.1-alpha.40",
4
4
  "description": "Flink plugin that makes it possible to send sms",
5
5
  "scripts": {
6
6
  "test": "echo \"Error: no test specified\"",
7
- "prepublish": "tsc"
7
+ "prepare": "tsc"
8
8
  },
9
9
  "author": "johan@frost.se",
10
10
  "publishConfig": {
@@ -17,10 +17,10 @@
17
17
  "axios": "^0.27.2"
18
18
  },
19
19
  "devDependencies": {
20
- "@flink-app/flink": "^0.12.1-alpha.4",
20
+ "@flink-app/flink": "^0.12.1-alpha.40",
21
21
  "@types/node": "22.13.10",
22
22
  "ts-node": "^9.1.1",
23
23
  "typescript": "5.4.5"
24
24
  },
25
- "gitHead": "8f06a4ab98e7179322d36546756d4305fd196f5a"
25
+ "gitHead": "456502f273fe9473df05b71a803f3eda1a2f8931"
26
26
  }
package/readme.md CHANGED
@@ -1,60 +1,400 @@
1
- # Flink API Docs
1
+ # SMS Plugin
2
2
 
3
- A FLINK plugin that makes it possible to send sms
3
+ A Flink plugin that provides SMS sending capabilities with support for 46elks and other SMS providers through a unified client interface.
4
4
 
5
- ## Usage
5
+ ## Installation
6
6
 
7
- Install plugin to your flink app project:
7
+ Install the plugin to your Flink app project:
8
8
 
9
- ```
10
- npm i -S @flink-app/sms-plugin
9
+ ```bash
10
+ npm install @flink-app/sms-plugin
11
11
  ```
12
12
 
13
- ## Setup
13
+ ## Configuration
14
14
 
15
- ### With 46 elks
15
+ ### Using 46elks
16
16
 
17
- Add and configure plugin in your app startup (probable the `index.ts` in root project):
17
+ Configure the plugin with 46elks (a Swedish SMS service provider):
18
18
 
19
- ```
19
+ ```typescript
20
+ import { FlinkApp, FlinkContext } from "@flink-app/flink";
20
21
  import { smsPlugin, sms46elksClient } from "@flink-app/sms-plugin";
21
22
 
22
23
  function start() {
23
24
  new FlinkApp<AppContext>({
24
25
  name: "My app",
25
26
  plugins: [
26
- // Register plugin
27
- smsPlugin({
28
- client: new sms46elksClient({
29
- username: "XX",
30
- password: "YY",
31
- }),
32
- }),
27
+ smsPlugin({
28
+ client: new sms46elksClient({
29
+ username: process.env.SMS_46ELKS_USERNAME!,
30
+ password: process.env.SMS_46ELKS_PASSWORD!
31
+ })
32
+ })
33
33
  ],
34
34
  }).start();
35
35
  }
36
-
37
36
  ```
38
37
 
39
- Add plugin ctx to `Ctx.ts` in root project
38
+ **46elks Client Options:**
40
39
 
40
+ ```typescript
41
+ interface sms46elksClientOptions {
42
+ username: string; // Your 46elks API username
43
+ password: string; // Your 46elks API password
44
+ }
41
45
  ```
46
+
47
+ ## TypeScript Setup
48
+
49
+ Add the plugin context to your app's context type (usually in `Ctx.ts`):
50
+
51
+ ```typescript
52
+ import { FlinkContext } from "@flink-app/flink";
42
53
  import { smsPluginContext } from "@flink-app/sms-plugin";
43
54
 
44
55
  export interface Ctx extends FlinkContext<smsPluginContext> {
56
+ // Your other context properties
57
+ }
58
+ ```
59
+
60
+ **Plugin Context Interface:**
45
61
 
62
+ ```typescript
63
+ interface smsPluginContext {
64
+ smsPlugin: {
65
+ client: client; // Unified SMS client interface
66
+ };
46
67
  }
68
+ ```
69
+
70
+ ## Usage in Handlers
71
+
72
+ ### Basic SMS
47
73
 
74
+ Send a simple SMS message:
75
+
76
+ ```typescript
77
+ import { Handler } from "@flink-app/flink";
78
+ import { Ctx } from "../Ctx";
79
+
80
+ const SendVerificationCode: Handler<Ctx, any, any> = async ({ ctx, req }) => {
81
+ await ctx.plugins.smsPlugin.client.send({
82
+ from: "MyApp",
83
+ to: ["+46701234567"],
84
+ message: "Your verification code is: 123456"
85
+ });
86
+
87
+ return { data: { success: true } };
88
+ };
89
+
90
+ export default SendVerificationCode;
48
91
  ```
49
- ## Send sms
50
- Send email from your handlers by using the the context
92
+
93
+ ### SMS to Multiple Recipients
94
+
95
+ Send the same message to multiple phone numbers:
96
+
97
+ ```typescript
98
+ await ctx.plugins.smsPlugin.client.send({
99
+ from: "MyCompany",
100
+ to: ["+46701234567", "+46709876543", "+46708765432"],
101
+ message: "Important notification: Service maintenance tonight at 10 PM."
102
+ });
51
103
  ```
52
104
 
53
- await ctx.plugins.smsPlugin.client.send({
54
- to : ["+4612345678"],
55
- from : "Sender",
56
- message : "Hello world"
57
-
58
- })
105
+ ### SMS with Dynamic Content
106
+
107
+ ```typescript
108
+ const SendOrderConfirmation: Handler<Ctx, OrderRequest, any> = async ({ ctx, req }) => {
109
+ const { phoneNumber, orderNumber, totalAmount } = req.body;
110
+
111
+ await ctx.plugins.smsPlugin.client.send({
112
+ from: "Shop",
113
+ to: [phoneNumber],
114
+ message: `Order ${orderNumber} confirmed! Total: ${totalAmount} SEK. Thank you for your purchase!`
115
+ });
116
+
117
+ return { data: { sent: true } };
118
+ };
119
+ ```
120
+
121
+ ## API Reference
122
+
123
+ ### SMS Type
124
+
125
+ ```typescript
126
+ interface sms {
127
+ from: string; // Sender name or phone number (max 11 alphanumeric characters or a valid phone number)
128
+ to: string[]; // Array of recipient phone numbers (E.164 format recommended, e.g., "+46701234567")
129
+ message: string; // SMS message content (max 160 characters for single SMS, longer messages will be split)
130
+ }
131
+ ```
132
+
133
+ ### Client Interface
134
+
135
+ All SMS clients implement this unified interface:
136
+
137
+ ```typescript
138
+ interface client {
139
+ send(sms: sms): Promise<boolean>;
140
+ }
141
+ ```
142
+
143
+ The `send` method returns:
144
+ - `true` - SMS sent successfully to all recipients
145
+ - `false` - SMS sending failed (errors are logged to console)
146
+
147
+ ## Complete Example
148
+
149
+ Here's a complete example of an SMS handler with error handling:
150
+
151
+ ```typescript
152
+ import { Handler } from "@flink-app/flink";
153
+ import { Ctx } from "../Ctx";
154
+
155
+ interface SendSMSRequest {
156
+ phoneNumber: string;
157
+ messageType: "verification" | "notification" | "alert";
158
+ data?: {
159
+ code?: string;
160
+ message?: string;
161
+ };
162
+ }
163
+
164
+ interface SendSMSResponse {
165
+ success: boolean;
166
+ error?: string;
167
+ }
168
+
169
+ const SendSMS: Handler<Ctx, SendSMSRequest, SendSMSResponse> = async ({ ctx, req }) => {
170
+ const { phoneNumber, messageType, data } = req.body;
171
+
172
+ // Validate phone number format
173
+ if (!phoneNumber.startsWith("+")) {
174
+ return {
175
+ data: {
176
+ success: false,
177
+ error: "Phone number must be in E.164 format (e.g., +46701234567)"
178
+ }
179
+ };
180
+ }
181
+
182
+ let message: string;
183
+
184
+ // Generate message based on type
185
+ switch (messageType) {
186
+ case "verification":
187
+ message = `Your verification code is: ${data?.code || "000000"}`;
188
+ break;
189
+ case "notification":
190
+ message = data?.message || "You have a new notification";
191
+ break;
192
+ case "alert":
193
+ message = `ALERT: ${data?.message || "Important system notification"}`;
194
+ break;
195
+ default:
196
+ return {
197
+ data: {
198
+ success: false,
199
+ error: "Invalid message type"
200
+ }
201
+ };
202
+ }
203
+
204
+ try {
205
+ const success = await ctx.plugins.smsPlugin.client.send({
206
+ from: "MyApp",
207
+ to: [phoneNumber],
208
+ message
209
+ });
210
+
211
+ return {
212
+ data: {
213
+ success,
214
+ error: success ? undefined : "Failed to send SMS"
215
+ }
216
+ };
217
+ } catch (error) {
218
+ console.error("SMS sending error:", error);
219
+ return {
220
+ data: {
221
+ success: false,
222
+ error: "An error occurred while sending SMS"
223
+ }
224
+ };
225
+ }
226
+ };
227
+
228
+ export default SendSMS;
229
+ ```
230
+
231
+ ## Error Handling
232
+
233
+ The SMS client handles errors internally and returns `false` on failure. Errors are logged to the console with `JSON.stringify(ex)`. For production use, you should implement additional error handling and monitoring:
234
+
235
+ ```typescript
236
+ const success = await ctx.plugins.smsPlugin.client.send({
237
+ from: "MyApp",
238
+ to: ["+46701234567"],
239
+ message: "Test message"
240
+ });
241
+
242
+ if (!success) {
243
+ // Handle SMS sending failure
244
+ console.log("Failed to send SMS - check logs for details");
245
+ // You might want to:
246
+ // - Log to an error tracking service
247
+ // - Retry with exponential backoff
248
+ // - Alert administrators
249
+ // - Store for later retry
250
+ }
251
+ ```
252
+
253
+ ## Best Practices
254
+
255
+ ### Phone Number Formatting
256
+
257
+ Always use E.164 format for phone numbers:
258
+ - Include the country code (e.g., +46 for Sweden)
259
+ - Remove spaces, dashes, and parentheses
260
+ - Example: `+46701234567` (not `070-123 45 67`)
261
+
262
+ ```typescript
263
+ // Good
264
+ to: ["+46701234567"]
265
+
266
+ // Bad
267
+ to: ["070-123 45 67"] // Missing country code and has formatting
268
+ ```
269
+
270
+ ### Sender ID
271
+
272
+ The `from` field can be either:
273
+ - **Alphanumeric**: Up to 11 characters (e.g., "MyCompany")
274
+ - **Numeric**: A valid phone number in E.164 format
275
+
276
+ ```typescript
277
+ // Good
278
+ from: "MyApp" // Short, memorable, alphanumeric
279
+ from: "+46701234567" // Valid phone number
280
+
281
+ // Avoid
282
+ from: "MyVeryLongCompanyName" // Too long (>11 chars)
283
+ ```
284
+
285
+ ### Message Length
286
+
287
+ - Single SMS: Up to 160 characters
288
+ - Longer messages will be automatically split into multiple SMS
289
+ - Special characters (emojis, etc.) may reduce the character limit
290
+
291
+ ### Rate Limiting
292
+
293
+ Consider implementing rate limiting to avoid:
294
+ - API throttling
295
+ - Excessive costs
296
+ - Spam complaints
297
+
298
+ ```typescript
299
+ // Example: Track SMS sending per user
300
+ const recentSMS = await ctx.repos.smsLogRepo.findByUser(userId);
301
+ if (recentSMS.length >= 5) {
302
+ return { data: { success: false, error: "Too many SMS sent. Please try again later." } };
303
+ }
304
+ ```
59
305
 
306
+ ## Multiple Recipients
307
+
308
+ The plugin sends SMS to each recipient individually. Keep in mind:
309
+ - Each recipient counts as a separate SMS (costs apply per message)
310
+ - Messages are sent sequentially to each number
311
+ - Failure for one recipient doesn't stop delivery to others
312
+
313
+ ```typescript
314
+ const result = await ctx.plugins.smsPlugin.client.send({
315
+ from: "MyApp",
316
+ to: ["+46701111111", "+46702222222", "+46703333333"], // 3 separate SMS
317
+ message: "Meeting reminder: Tomorrow at 2 PM"
318
+ });
319
+ // Returns true only if ALL messages were sent successfully
320
+ ```
321
+
322
+ ## Implementation Details
323
+
324
+ ### 46elks Provider
325
+
326
+ The 46elks client:
327
+ - Uses Basic Authentication with username/password
328
+ - Sends to the 46elks API endpoint: `https://api.46elks.com/a1/sms`
329
+ - Processes each recipient individually in a loop
330
+ - Returns `false` if any recipient fails
331
+
332
+ ## Custom SMS Provider
333
+
334
+ You can implement your own SMS client by implementing the `client` interface:
335
+
336
+ ```typescript
337
+ import { client } from "@flink-app/sms-plugin";
338
+ import { sms } from "@flink-app/sms-plugin";
339
+
340
+ export class customSMSClient implements client {
341
+ private apiKey: string;
342
+
343
+ constructor(options: { apiKey: string }) {
344
+ this.apiKey = options.apiKey;
345
+ }
346
+
347
+ async send(sms: sms): Promise<boolean> {
348
+ try {
349
+ // Your custom SMS sending logic here
350
+ for (const recipient of sms.to) {
351
+ // Send to your provider's API
352
+ await yourSMSAPI.send({
353
+ from: sms.from,
354
+ to: recipient,
355
+ text: sms.message
356
+ });
357
+ }
358
+ return true;
359
+ } catch (error) {
360
+ console.log(JSON.stringify(error));
361
+ return false;
362
+ }
363
+ }
364
+ }
60
365
  ```
366
+
367
+ Then use it with the plugin:
368
+
369
+ ```typescript
370
+ import { smsPlugin } from "@flink-app/sms-plugin";
371
+ import { customSMSClient } from "./customSMSClient";
372
+
373
+ smsPlugin({
374
+ client: new customSMSClient({
375
+ apiKey: process.env.CUSTOM_SMS_API_KEY!
376
+ })
377
+ })
378
+ ```
379
+
380
+ ## Troubleshooting
381
+
382
+ ### Messages Not Sending
383
+
384
+ 1. Check your API credentials are correct
385
+ 2. Verify phone numbers are in E.164 format
386
+ 3. Check the console logs for error details
387
+ 4. Ensure you have sufficient account balance with your SMS provider
388
+ 5. Verify the sender ID meets provider requirements
389
+
390
+ ### Failed Delivery
391
+
392
+ - The plugin returns `false` if sending fails
393
+ - Check provider-specific error codes in console logs
394
+ - Some providers may delay delivery - check your provider's dashboard
395
+
396
+ ### Character Encoding
397
+
398
+ - The plugin sends messages in UTF-8 encoding
399
+ - Special characters and emojis may count as multiple characters
400
+ - Some providers may have different encoding rules