@rempays/shared-core 1.0.2-beta.13 → 1.0.2-beta.15

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.
@@ -24,7 +24,7 @@ class DynamoDBClientWrapper {
24
24
  ExpressionAttributeNames: options.expressionAttributeNames,
25
25
  }),
26
26
  ...(options?.expressionAttributeValues && {
27
- ExpressionAttributeValues: (0, util_dynamodb_1.marshall)(options.expressionAttributeValues),
27
+ ExpressionAttributeValues: (0, util_dynamodb_1.marshall)(options.expressionAttributeValues, { removeUndefinedValues: true }),
28
28
  }),
29
29
  });
30
30
  await this.client.send(command);
@@ -58,7 +58,7 @@ class DynamoDBClientWrapper {
58
58
  ExpressionAttributeNames: options.expressionAttributeNames,
59
59
  }),
60
60
  ...(options.expressionAttributeValues && {
61
- ExpressionAttributeValues: (0, util_dynamodb_1.marshall)(options.expressionAttributeValues),
61
+ ExpressionAttributeValues: (0, util_dynamodb_1.marshall)(options.expressionAttributeValues, { removeUndefinedValues: true }),
62
62
  }),
63
63
  ReturnValues: 'ALL_NEW',
64
64
  });
@@ -90,7 +90,7 @@ class DynamoDBClientWrapper {
90
90
  ExpressionAttributeNames: options.expressionAttributeNames,
91
91
  }),
92
92
  ...(options.expressionAttributeValues && {
93
- ExpressionAttributeValues: (0, util_dynamodb_1.marshall)(options.expressionAttributeValues),
93
+ ExpressionAttributeValues: (0, util_dynamodb_1.marshall)(options.expressionAttributeValues, { removeUndefinedValues: true }),
94
94
  }),
95
95
  ...(options.limit && { Limit: options.limit }),
96
96
  ...(options.scanIndexForward !== undefined && {
@@ -27,7 +27,7 @@ class ChatService {
27
27
  };
28
28
  await this.client.send(new client_dynamodb_1.PutItemCommand({
29
29
  TableName: this.tableName,
30
- Item: (0, util_dynamodb_1.marshall)(item),
30
+ Item: (0, util_dynamodb_1.marshall)(item, { removeUndefinedValues: true }),
31
31
  }));
32
32
  return newChat;
33
33
  }
@@ -86,7 +86,7 @@ class ChatService {
86
86
  }),
87
87
  UpdateExpression: `SET ${updateExpressions.join(', ')}`,
88
88
  ExpressionAttributeNames: expressionAttributeNames,
89
- ExpressionAttributeValues: (0, util_dynamodb_1.marshall)(expressionAttributeValues),
89
+ ExpressionAttributeValues: (0, util_dynamodb_1.marshall)(expressionAttributeValues, { removeUndefinedValues: true }),
90
90
  }));
91
91
  return this.getChat(chatId);
92
92
  }
@@ -140,7 +140,7 @@ class ChatService {
140
140
  };
141
141
  await this.client.send(new client_dynamodb_1.PutItemCommand({
142
142
  TableName: this.tableName,
143
- Item: (0, util_dynamodb_1.marshall)(item),
143
+ Item: (0, util_dynamodb_1.marshall)(item, { removeUndefinedValues: true }),
144
144
  }));
145
145
  // Actualizar metadata del chat
146
146
  const chat = await this.getChat(chatId);
@@ -27,7 +27,7 @@ class SystemPromptService {
27
27
  };
28
28
  await this.client.send(new client_dynamodb_1.PutItemCommand({
29
29
  TableName: this.tableName,
30
- Item: (0, util_dynamodb_1.marshall)(item),
30
+ Item: (0, util_dynamodb_1.marshall)(item, { removeUndefinedValues: true }),
31
31
  }));
32
32
  return newPrompt;
33
33
  }
@@ -133,7 +133,7 @@ class SystemPromptService {
133
133
  }),
134
134
  UpdateExpression: `SET ${updateExpressions.join(', ')}`,
135
135
  ExpressionAttributeNames: expressionAttributeNames,
136
- ExpressionAttributeValues: (0, util_dynamodb_1.marshall)(expressionAttributeValues),
136
+ ExpressionAttributeValues: (0, util_dynamodb_1.marshall)(expressionAttributeValues, { removeUndefinedValues: true }),
137
137
  }));
138
138
  return this.getPrompt(templateCode, version);
139
139
  }
@@ -1,9 +1,10 @@
1
- import type { SendTextParams, SendTemplateParams, SendInteractiveParams, SendInteractiveListParams, SendImageParams, SendDocumentParams, SendLocationParams, SetTypingParams, MarkAsReadParams, SendAudioParams, SendVideoParams, MediaInfoResponse, CreateTemplateParams } from "./types";
1
+ import type { SendTextParams, SendTemplateParams, SendInteractiveParams, SendInteractiveListParams, SendImageParams, SendDocumentParams, SendLocationParams, SetTypingParams, MarkAsReadParams, SendAudioParams, SendVideoParams, MediaInfoResponse, CreateTemplateParams, BusinessProfileParams, BusinessProfileResponse, UpdateDisplayNameParams, DisplayNameStatusResponse } from "./types";
2
2
  export declare class FacebookApi {
3
3
  private static token;
4
4
  private static phoneNumberId;
5
5
  private static apiVersion;
6
- constructor(token?: string, phoneNumberId?: string, apiVersion?: string);
6
+ private static appId;
7
+ constructor(token?: string, phoneNumberId?: string, apiVersion?: string, appId?: string);
7
8
  private static init;
8
9
  private static post;
9
10
  static sendText(params: SendTextParams): Promise<any>;
@@ -23,4 +24,22 @@ export declare class FacebookApi {
23
24
  static listTemplates(wabaId: string): Promise<any>;
24
25
  static createTemplate(params: CreateTemplateParams): Promise<any>;
25
26
  static deleteTemplate(wabaId: string, name: string): Promise<any>;
27
+ /**
28
+ * Uploads an image and updates the business profile picture.
29
+ * @param buffer Image binary data
30
+ * @param mimeType Image MIME type (image/jpeg or image/png)
31
+ */
32
+ static updateProfilePicture(buffer: Buffer, mimeType: string): Promise<any>;
33
+ /**
34
+ * Uploads media for chat (messages).
35
+ * @param buffer Media binary data
36
+ * @param mimeType MIME type of the file
37
+ */
38
+ static uploadMedia(buffer: Buffer, mimeType: string): Promise<{
39
+ id: string;
40
+ }>;
41
+ static getBusinessProfile(): Promise<BusinessProfileResponse>;
42
+ static updateBusinessProfile(params: BusinessProfileParams): Promise<any>;
43
+ static updateDisplayName(params: UpdateDisplayNameParams): Promise<any>;
44
+ static getDisplayNameStatus(): Promise<DisplayNameStatusResponse>;
26
45
  }
@@ -3,13 +3,15 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.FacebookApi = void 0;
4
4
  const http_1 = require("./http");
5
5
  class FacebookApi {
6
- constructor(token, phoneNumberId, apiVersion) {
6
+ constructor(token, phoneNumberId, apiVersion, appId) {
7
7
  if (token)
8
8
  FacebookApi.token = token;
9
9
  if (phoneNumberId)
10
10
  FacebookApi.phoneNumberId = phoneNumberId;
11
11
  if (apiVersion)
12
12
  FacebookApi.apiVersion = apiVersion;
13
+ if (appId)
14
+ FacebookApi.appId = appId;
13
15
  }
14
16
  static async init() {
15
17
  if (this.token && this.phoneNumberId && this.apiVersion)
@@ -19,6 +21,7 @@ class FacebookApi {
19
21
  this.phoneNumberId || process.env.FACEBOOK_PHONE_NUMBER_ID || null;
20
22
  this.apiVersion =
21
23
  this.apiVersion || process.env.FACEBOOK_API_VERSION || "v18.0";
24
+ this.appId = this.appId || process.env.FACEBOOK_APP_ID || null;
22
25
  if (!this.token || !this.phoneNumberId) {
23
26
  throw new Error("Facebook API environment variables are not properly configured (FACEBOOK_API_TOKEN, FACEBOOK_PHONE_NUMBER_ID)");
24
27
  }
@@ -142,7 +145,11 @@ class FacebookApi {
142
145
  messaging_product: "whatsapp",
143
146
  to: params.to,
144
147
  type: "image",
145
- image: { link: params.link, caption: params.caption },
148
+ image: {
149
+ link: params.link,
150
+ id: params.id,
151
+ caption: params.caption
152
+ },
146
153
  };
147
154
  return await this.post("messages", payload);
148
155
  }
@@ -156,6 +163,7 @@ class FacebookApi {
156
163
  type: "document",
157
164
  document: {
158
165
  link: params.link,
166
+ id: params.id,
159
167
  caption: params.caption,
160
168
  filename: params.filename,
161
169
  },
@@ -172,6 +180,7 @@ class FacebookApi {
172
180
  type: "audio",
173
181
  audio: {
174
182
  link: params.link,
183
+ id: params.id,
175
184
  },
176
185
  };
177
186
  return await this.post("messages", payload);
@@ -186,6 +195,7 @@ class FacebookApi {
186
195
  type: "video",
187
196
  video: {
188
197
  link: params.link,
198
+ id: params.id,
189
199
  caption: params.caption,
190
200
  },
191
201
  };
@@ -293,8 +303,120 @@ class FacebookApi {
293
303
  });
294
304
  return data;
295
305
  }
306
+ /*
307
+ PROFILE PICTURE UPLOAD (TWO-STEP)
308
+ */
309
+ /**
310
+ * Uploads an image and updates the business profile picture.
311
+ * @param buffer Image binary data
312
+ * @param mimeType Image MIME type (image/jpeg or image/png)
313
+ */
314
+ static async updateProfilePicture(buffer, mimeType) {
315
+ await this.init();
316
+ if (!this.appId) {
317
+ throw new Error("FACEBOOK_APP_ID is required for profile picture uploads");
318
+ }
319
+ const http = (0, http_1.getHttpClient)(this.token);
320
+ // 1. Create upload session
321
+ const sessionUrl = `https://graph.facebook.com/${this.apiVersion}/${this.appId}/uploads`;
322
+ const { data: sessionData } = await http.post(sessionUrl, null, {
323
+ params: {
324
+ file_length: buffer.length,
325
+ file_type: mimeType,
326
+ access_token: this.token
327
+ }
328
+ });
329
+ const sessionId = sessionData.id;
330
+ // 2. Upload the file data
331
+ const uploadUrl = `https://graph.facebook.com/${this.apiVersion}/${sessionId}`;
332
+ const { data: uploadData } = await http.post(uploadUrl, buffer, {
333
+ headers: {
334
+ 'file_offset': 0,
335
+ 'Content-Type': mimeType
336
+ }
337
+ });
338
+ const handle = uploadData.h;
339
+ // 3. Update the business profile with the handle
340
+ return await this.updateBusinessProfile({
341
+ profile_picture_handle: handle
342
+ });
343
+ }
344
+ /*
345
+ CHAT MEDIA UPLOAD
346
+ */
347
+ /**
348
+ * Uploads media for chat (messages).
349
+ * @param buffer Media binary data
350
+ * @param mimeType MIME type of the file
351
+ */
352
+ static async uploadMedia(buffer, mimeType) {
353
+ await this.init();
354
+ const http = (0, http_1.getHttpClient)(this.token);
355
+ const url = `https://graph.facebook.com/${this.apiVersion}/${this.phoneNumberId}/media`;
356
+ const formData = new FormData();
357
+ const blob = new Blob([buffer], { type: mimeType });
358
+ formData.append('file', blob, 'media');
359
+ formData.append('type', mimeType);
360
+ formData.append('messaging_product', 'whatsapp');
361
+ const { data } = await http.post(url, formData, {
362
+ headers: {
363
+ 'Content-Type': 'multipart/form-data'
364
+ }
365
+ });
366
+ return data; // Returns { id: "media_id" }
367
+ }
368
+ /*
369
+ BUSINESS PROFILE
370
+ */
371
+ static async getBusinessProfile() {
372
+ await this.init();
373
+ const http = (0, http_1.getHttpClient)(this.token);
374
+ const url = `https://graph.facebook.com/${this.apiVersion}/${this.phoneNumberId}/whatsapp_business_profile`;
375
+ const { data } = await http.get(url, {
376
+ params: {
377
+ fields: "about,address,description,email,profile_picture_url,websites,vertical"
378
+ }
379
+ });
380
+ // The response is usually { data: [ { ... } ] }
381
+ return data.data?.[0] || data;
382
+ }
383
+ static async updateBusinessProfile(params) {
384
+ const payload = {
385
+ messaging_product: "whatsapp",
386
+ ...params
387
+ };
388
+ return await this.post("whatsapp_business_profile", payload);
389
+ }
390
+ /*
391
+ DISPLAY NAME
392
+ */
393
+ static async updateDisplayName(params) {
394
+ await this.init();
395
+ const http = (0, http_1.getHttpClient)(this.token);
396
+ // Using phone number ID directly with query params as per Meta documentation
397
+ const url = `https://graph.facebook.com/${this.apiVersion}/${this.phoneNumberId}`;
398
+ const { data } = await http.post(url, null, {
399
+ params: {
400
+ new_display_name: params.new_display_name
401
+ }
402
+ });
403
+ return data;
404
+ }
405
+ static async getDisplayNameStatus() {
406
+ await this.init();
407
+ const http = (0, http_1.getHttpClient)(this.token);
408
+ // Using phone number ID to get status of display names
409
+ const url = `https://graph.facebook.com/${this.apiVersion}/${this.phoneNumberId}`;
410
+ const { data } = await http.get(url, {
411
+ params: {
412
+ fields: "verified_name,name_status,new_display_name,new_name_status"
413
+ }
414
+ });
415
+ return data;
416
+ }
296
417
  }
297
418
  exports.FacebookApi = FacebookApi;
298
419
  FacebookApi.token = null;
299
420
  FacebookApi.phoneNumberId = null;
300
421
  FacebookApi.apiVersion = null;
422
+ FacebookApi.appId = null;
@@ -49,12 +49,14 @@ export interface SendInteractiveListParams {
49
49
  }
50
50
  export interface SendImageParams {
51
51
  to: string;
52
- link: string;
52
+ link?: string;
53
+ id?: string;
53
54
  caption?: string;
54
55
  }
55
56
  export interface SendDocumentParams {
56
57
  to: string;
57
- link: string;
58
+ link?: string;
59
+ id?: string;
58
60
  filename?: string;
59
61
  caption?: string;
60
62
  }
@@ -115,11 +117,13 @@ export interface PhoneNumberInfo {
115
117
  }
116
118
  export interface SendAudioParams {
117
119
  to: string;
118
- link: string;
120
+ link?: string;
121
+ id?: string;
119
122
  }
120
123
  export interface SendVideoParams {
121
124
  to: string;
122
- link: string;
125
+ link?: string;
126
+ id?: string;
123
127
  caption?: string;
124
128
  }
125
129
  export interface MediaInfoResponse {
@@ -135,3 +139,33 @@ export interface CreateTemplateParams {
135
139
  language: string;
136
140
  bodyText: string;
137
141
  }
142
+ export interface BusinessProfileParams {
143
+ about?: string;
144
+ address?: string;
145
+ description?: string;
146
+ email?: string;
147
+ websites?: string[];
148
+ vertical?: WhatsAppBusinessVertical;
149
+ profile_picture_handle?: string;
150
+ }
151
+ export type WhatsAppBusinessVertical = "" | "UNDEFINED" | "OTHER" | "AUTO" | "BEAUTY" | "APPAREL" | "EDU" | "ENTERTAIN" | "EVENT_PLAN" | "FINANCE" | "GROCERY" | "GOVT" | "HOTEL" | "HEALTH" | "NONPROFIT" | "PROF_SERVICES" | "RETAIL" | "TRAVEL" | "RESTAURANT" | "NOT_A_BIZ";
152
+ export interface BusinessProfileResponse {
153
+ messaging_product: "whatsapp";
154
+ about: string;
155
+ address: string;
156
+ description: string;
157
+ email: string;
158
+ websites: string[];
159
+ vertical: WhatsAppBusinessVertical;
160
+ profile_picture_url?: string;
161
+ }
162
+ export interface UpdateDisplayNameParams {
163
+ new_display_name: string;
164
+ }
165
+ export interface DisplayNameStatusResponse {
166
+ verified_name: string;
167
+ name_status: string;
168
+ new_display_name?: string;
169
+ new_name_status?: string;
170
+ id: string;
171
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rempays/shared-core",
3
- "version": "1.0.2-beta.13",
3
+ "version": "1.0.2-beta.15",
4
4
  "description": "Core utilities layer for RemPays platform with AWS services integration (Cognito, S3, Secrets Manager, Textract, Facebook API, DynamoDB)",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",