@green-api/greenapi-integration 0.2.0 → 0.3.0

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.
@@ -1,6 +1,6 @@
1
1
  import { GreenApiClient } from "./green-api.client";
2
2
  import { MessageTransformer } from "./message-transformer";
3
- import { BaseUser, ForwardMessagesResponse, GreenApiWebhook, Instance, SendResponse, Settings } from "../types/types";
3
+ import { BaseUser, ForwardMessagesResponse, GreenApiWebhook, Instance, SendResponse, StateInstanceWebhook, WebhookType } from "../types/types";
4
4
  import { StorageProvider } from "./storage-provider";
5
5
  /**
6
6
  * Base adapter for platform integrations with GREEN-API.
@@ -74,6 +74,14 @@ export declare abstract class BaseAdapter<TPlatformWebhook, TPlatformMessage, TU
74
74
  * ```
75
75
  */
76
76
  abstract createPlatformClient(params: any): Promise<any>;
77
+ /**
78
+ * Handles instance state change webhooks from GREEN-API.
79
+ * Adapters MUST override this method if they need to handle instance state changes
80
+ *
81
+ * @param webhook - The state change webhook from GREEN-API
82
+ * @returns Promise resolving when the webhook is handled
83
+ */
84
+ handleStateInstanceWebhook(webhook: StateInstanceWebhook): Promise<void>;
77
85
  /**
78
86
  * Creates a GREEN-API client instance.
79
87
  *
@@ -107,18 +115,17 @@ export declare abstract class BaseAdapter<TPlatformWebhook, TPlatformMessage, TU
107
115
  * @throws {NotFoundError} If instance is not found
108
116
  * @throws {IntegrationError} If webhook handling fails
109
117
  */
110
- handleGreenApiWebhook(webhook: GreenApiWebhook, allowedTypes: string[]): Promise<void>;
118
+ handleGreenApiWebhook(webhook: GreenApiWebhook, allowedTypes: WebhookType[]): Promise<void>;
111
119
  /**
112
120
  * Creates a new instance with specified settings.
113
121
  *
114
122
  * @param instance - The instance configuration
115
- * @param settings - GREEN-API settings for the instance
116
123
  * @param userCred - User credentials
117
124
  * @returns Promise resolving to the created instance
118
125
  * @throws {NotFoundError} If user is not found
119
126
  * @throws {IntegrationError} If instance creation fails
120
127
  */
121
- createInstance(instance: Instance, settings: Settings, userCred: any): Promise<TInstance>;
128
+ createInstance(instance: Instance, userCred: any): Promise<TInstance>;
122
129
  /**
123
130
  * Removes an instance by ID.
124
131
  *
@@ -38,6 +38,17 @@ class BaseAdapter {
38
38
  this.transformer = transformer;
39
39
  this.storage = storage;
40
40
  }
41
+ /**
42
+ * Handles instance state change webhooks from GREEN-API.
43
+ * Adapters MUST override this method if they need to handle instance state changes
44
+ *
45
+ * @param webhook - The state change webhook from GREEN-API
46
+ * @returns Promise resolving when the webhook is handled
47
+ */
48
+ async handleStateInstanceWebhook(webhook) {
49
+ // Default empty implementation
50
+ return;
51
+ }
41
52
  /**
42
53
  * Creates a GREEN-API client instance.
43
54
  *
@@ -117,12 +128,17 @@ class BaseAdapter {
117
128
  return;
118
129
  }
119
130
  try {
120
- const transformedMessage = await this.transformer.toPlatformMessage(webhook);
121
- const instance = await this.storage.getInstance(webhook.instanceData.idInstance);
122
- if (!instance) {
123
- throw new errors_1.NotFoundError("Instance not found");
131
+ if (webhook.typeWebhook === "stateInstanceChanged") {
132
+ await this.handleStateInstanceWebhook(webhook);
133
+ }
134
+ else {
135
+ const transformedMessage = await this.transformer.toPlatformMessage(webhook);
136
+ const instance = await this.storage.getInstance(webhook.instanceData.idInstance);
137
+ if (!instance) {
138
+ throw new errors_1.NotFoundError("Instance not found");
139
+ }
140
+ await this.sendToPlatform(transformedMessage, instance);
124
141
  }
125
- await this.sendToPlatform(transformedMessage, instance);
126
142
  }
127
143
  catch (error) {
128
144
  this.handleError("Failed to handle GREEN-API webhook", error);
@@ -132,13 +148,12 @@ class BaseAdapter {
132
148
  * Creates a new instance with specified settings.
133
149
  *
134
150
  * @param instance - The instance configuration
135
- * @param settings - GREEN-API settings for the instance
136
151
  * @param userCred - User credentials
137
152
  * @returns Promise resolving to the created instance
138
153
  * @throws {NotFoundError} If user is not found
139
154
  * @throws {IntegrationError} If instance creation fails
140
155
  */
141
- async createInstance(instance, settings, userCred) {
156
+ async createInstance(instance, userCred) {
142
157
  try {
143
158
  const user = await this.storage.findUser(userCred);
144
159
  if (!user) {
@@ -151,8 +166,10 @@ class BaseAdapter {
151
166
  catch (error) {
152
167
  throw new errors_1.IntegrationError(`Failed to get settings for instance ${instance.idInstance}: ${error.message}`, "INTEGRATION_ERROR");
153
168
  }
154
- const createdInstance = await this.storage.createInstance(instance, user.id, settings);
155
- await client.setSettings(settings);
169
+ const createdInstance = await this.storage.createInstance(instance, user.id);
170
+ if (instance.settings) {
171
+ await client.setSettings(instance.settings);
172
+ }
156
173
  return createdInstance;
157
174
  }
158
175
  catch (error) {
@@ -51,7 +51,7 @@ class GreenApiClient {
51
51
  return response.data;
52
52
  }
53
53
  catch (error) {
54
- throw new Error(`Failed to ${endpoint.replace(/([A-Z])/g, " $1").toLowerCase()}: ${error.message}`);
54
+ throw new Error(`Failed to ${endpoint.replace(/([A-Z])/g, " $1").toLowerCase()}: ${error.message}. ${JSON.stringify(error.response?.data)}`);
55
55
  }
56
56
  }
57
57
  async makeFileUploadRequest(endpoint, formData, headers) {
@@ -1,4 +1,4 @@
1
- import { BaseUser, Instance, Settings } from "../types/types";
1
+ import { BaseUser, Instance } from "../types/types";
2
2
  /**
3
3
  * Abstract class for managing instance and user data storage.
4
4
  * Implement this class to define how your integration stores and retrieves data.
@@ -26,10 +26,9 @@ export declare abstract class StorageProvider<TUser extends BaseUser = BaseUser,
26
26
  *
27
27
  * @param instance - The instance data to store
28
28
  * @param userId - ID of the user who owns this instance
29
- * @param settings - Optional GREEN-API settings for the instance
30
29
  * @returns Promise resolving to the created instance
31
30
  */
32
- abstract createInstance(instance: Instance, userId: bigint | number, settings?: Settings): Promise<TInstance>;
31
+ abstract createInstance(instance: Instance, userId: bigint | number): Promise<TInstance>;
33
32
  /**
34
33
  * Retrieves an instance by its ID.
35
34
  *
@@ -5,7 +5,8 @@
5
5
  interface BaseInstance {
6
6
  idInstance: number | bigint;
7
7
  apiTokenInstance: string;
8
- settings?: Settings;
8
+ stateInstance?: InstanceState;
9
+ settings?: Settings | Record<string, any>;
9
10
  }
10
11
  /**
11
12
  * Extended instance interface that allows for additional platform-specific properties.
@@ -172,6 +173,7 @@ export interface PollUpdateMessageData {
172
173
  multipleAnswers: boolean;
173
174
  }
174
175
  export type OutgoingMessageStatus = "sent" | "delivered" | "read" | "failed" | "noAccount" | "notInGroup" | "yellowCard";
176
+ export type WebhookType = "stateInstanceChanged" | "outgoingMessageStatus" | "outgoingAPIMessageReceived" | "outgoingMessageReceived" | "incomingMessageReceived";
175
177
  /**
176
178
  * Webhook payload received when a message status changes.
177
179
  * Used to track delivery and read receipts.
@@ -190,6 +192,16 @@ export interface OutgoingMessageStatusWebhook {
190
192
  description?: string;
191
193
  sendByApi: boolean;
192
194
  }
195
+ export interface StateInstanceWebhook {
196
+ typeWebhook: "stateInstanceChanged";
197
+ instanceData: {
198
+ idInstance: number;
199
+ wid: string;
200
+ typeInstance: string;
201
+ };
202
+ timestamp: number;
203
+ stateInstance: InstanceState;
204
+ }
193
205
  export type WebhookMessageData = {
194
206
  typeMessage: "textMessage";
195
207
  textMessageData: TextMessageData;
@@ -235,7 +247,7 @@ export interface MessageWebhook {
235
247
  /**
236
248
  * Primary webhook types received from GREEN-API.
237
249
  */
238
- export type GreenApiWebhook = MessageWebhook | OutgoingMessageStatusWebhook;
250
+ export type GreenApiWebhook = MessageWebhook | OutgoingMessageStatusWebhook | StateInstanceWebhook;
239
251
  /**
240
252
  * Configuration settings for a GREEN-API instance.
241
253
  * Controls webhook behavior, message handling, and other instance features.
@@ -1,3 +1,4 @@
1
+ import { Settings } from "../types/types";
1
2
  /**
2
3
  * Utility functions for working with phone numbers, tokens, and vCards.
3
4
  *
@@ -39,3 +40,34 @@ export declare function generateRandomToken(length?: number): string;
39
40
  * extractPhoneNumberFromVCard(vcard) // Returns '+1234567890'
40
41
  */
41
42
  export declare function extractPhoneNumberFromVCard(vcard: string): string | null;
43
+ /**
44
+ * Validates if a value is appropriate for a specific Settings interface key.
45
+ * Checks type compatibility for numbers, strings, and yes/no enums.
46
+ *
47
+ * @param key - The settings key to validate
48
+ * @param value - The value to check against the key's expected type
49
+ * @returns Boolean indicating if the value is valid for the given key
50
+ *
51
+ * @example
52
+ * isValidSettingValue('delaySendMessagesMilliseconds', 1000) // Returns true
53
+ * isValidSettingValue('outgoingWebhook', 'maybe') // Returns false
54
+ * isValidSettingValue('webhookUrl', 'https://example.com') // Returns true
55
+ */
56
+ export declare function isValidSettingValue(key: keyof Settings, value: any): boolean;
57
+ /**
58
+ * Validates and cleans a settings object against the Settings interface.
59
+ * Removes any properties that don't match the interface or have invalid values.
60
+ *
61
+ * @param settings - The settings object to validate and clean
62
+ * @returns A new Settings object containing only valid properties and values
63
+ *
64
+ * @example
65
+ * const input = {
66
+ * webhookUrl: 'https://example.com',
67
+ * outgoingWebhook: 'yes',
68
+ * invalidKey: 'value',
69
+ * delaySendMessagesMilliseconds: 'invalid'
70
+ * };
71
+ * validateAndCleanSettings(input) // Returns { webhookUrl: 'https://example.com', outgoingWebhook: 'yes' }
72
+ */
73
+ export declare function validateAndCleanSettings(settings: any): Settings;
@@ -36,6 +36,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.formatPhoneNumber = formatPhoneNumber;
37
37
  exports.generateRandomToken = generateRandomToken;
38
38
  exports.extractPhoneNumberFromVCard = extractPhoneNumberFromVCard;
39
+ exports.isValidSettingValue = isValidSettingValue;
40
+ exports.validateAndCleanSettings = validateAndCleanSettings;
39
41
  const crypto = __importStar(require("crypto"));
40
42
  /**
41
43
  * Utility functions for working with phone numbers, tokens, and vCards.
@@ -86,3 +88,65 @@ function extractPhoneNumberFromVCard(vcard) {
86
88
  const phoneMatch = vcard.match(/TEL(?:;[^:]+)?:([+\d\s-]+)/);
87
89
  return phoneMatch ? phoneMatch[1] : null;
88
90
  }
91
+ /**
92
+ * Validates if a value is appropriate for a specific Settings interface key.
93
+ * Checks type compatibility for numbers, strings, and yes/no enums.
94
+ *
95
+ * @param key - The settings key to validate
96
+ * @param value - The value to check against the key's expected type
97
+ * @returns Boolean indicating if the value is valid for the given key
98
+ *
99
+ * @example
100
+ * isValidSettingValue('delaySendMessagesMilliseconds', 1000) // Returns true
101
+ * isValidSettingValue('outgoingWebhook', 'maybe') // Returns false
102
+ * isValidSettingValue('webhookUrl', 'https://example.com') // Returns true
103
+ */
104
+ function isValidSettingValue(key, value) {
105
+ switch (key) {
106
+ case "delaySendMessagesMilliseconds":
107
+ return typeof value === "number";
108
+ case "wid":
109
+ case "webhookUrl":
110
+ case "webhookUrlToken":
111
+ return typeof value === "string";
112
+ case "markIncomingMessagesReaded":
113
+ case "markIncomingMessagesReadedOnReply":
114
+ case "outgoingWebhook":
115
+ case "outgoingMessageWebhook":
116
+ case "outgoingAPIMessageWebhook":
117
+ case "stateWebhook":
118
+ case "incomingWebhook":
119
+ case "keepOnlineStatus":
120
+ case "pollMessageWebhook":
121
+ case "incomingCallWebhook":
122
+ return value === "yes" || value === "no";
123
+ default:
124
+ return false;
125
+ }
126
+ }
127
+ /**
128
+ * Validates and cleans a settings object against the Settings interface.
129
+ * Removes any properties that don't match the interface or have invalid values.
130
+ *
131
+ * @param settings - The settings object to validate and clean
132
+ * @returns A new Settings object containing only valid properties and values
133
+ *
134
+ * @example
135
+ * const input = {
136
+ * webhookUrl: 'https://example.com',
137
+ * outgoingWebhook: 'yes',
138
+ * invalidKey: 'value',
139
+ * delaySendMessagesMilliseconds: 'invalid'
140
+ * };
141
+ * validateAndCleanSettings(input) // Returns { webhookUrl: 'https://example.com', outgoingWebhook: 'yes' }
142
+ */
143
+ function validateAndCleanSettings(settings) {
144
+ const validSettings = {};
145
+ const settingsKeys = Object.keys(settings);
146
+ for (const key of settingsKeys) {
147
+ if (key in validSettings && isValidSettingValue(key, settings[key])) {
148
+ validSettings[key] = settings[key];
149
+ }
150
+ }
151
+ return validSettings;
152
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@green-api/greenapi-integration",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "GREEN-API Integration library",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",