From 1591b71e1c9a0001d2d0cbf0b9791c4aece6103c Mon Sep 17 00:00:00 2001 From: Luan Cazarine Date: Thu, 10 Oct 2024 17:38:56 -0300 Subject: [PATCH 1/5] wati init --- .../wati/actions/add-contact/add-contact.mjs | 24 +++ .../send-template-message.mjs | 34 ++++ .../update-contact-attribute.mjs | 34 ++++ components/wati/app/wati.app.ts | 2 +- components/wati/package.json | 2 +- .../new-contact-created-instant.mjs | 59 +++++++ .../new-incoming-message-instant.mjs | 86 +++++++++++ components/wati/wati.app.mjs | 146 ++++++++++++++++++ 8 files changed, 385 insertions(+), 2 deletions(-) create mode 100644 components/wati/actions/add-contact/add-contact.mjs create mode 100644 components/wati/actions/send-template-message/send-template-message.mjs create mode 100644 components/wati/actions/update-contact-attribute/update-contact-attribute.mjs create mode 100644 components/wati/sources/new-contact-created-instant/new-contact-created-instant.mjs create mode 100644 components/wati/sources/new-incoming-message-instant/new-incoming-message-instant.mjs create mode 100644 components/wati/wati.app.mjs diff --git a/components/wati/actions/add-contact/add-contact.mjs b/components/wati/actions/add-contact/add-contact.mjs new file mode 100644 index 0000000000000..ce82d8ac1c12c --- /dev/null +++ b/components/wati/actions/add-contact/add-contact.mjs @@ -0,0 +1,24 @@ +import wati from "../../wati.app.mjs"; +import { axios } from "@pipedream/platform"; + +export default { + key: "wati-add-contact", + name: "Add Contact", + description: "Adds a new contact on the WATI platform. [See the documentation](https://docs.wati.io/reference/post_api-v1-addcontact-whatsappnumber)", + version: "0.0.1", + type: "action", + props: { + wati, + contactDetails: { + propDefinition: [ + wati, + "contactDetails", + ], + }, + }, + async run({ $ }) { + const response = await this.wati.addContact(this.contactDetails); + $.export("$summary", `Successfully added contact with name: ${this.contactDetails.name}`); + return response; + }, +}; diff --git a/components/wati/actions/send-template-message/send-template-message.mjs b/components/wati/actions/send-template-message/send-template-message.mjs new file mode 100644 index 0000000000000..4d812755007a6 --- /dev/null +++ b/components/wati/actions/send-template-message/send-template-message.mjs @@ -0,0 +1,34 @@ +import wati from "../../wati.app.mjs"; +import { axios } from "@pipedream/platform"; + +export default { + key: "wati-send-template-message", + name: "Send WhatsApp Template Message", + description: "Enables sending of WhatsApp messages using a pre-approved template. [See the documentation](https://docs.wati.io/reference/post_api-v2-sendtemplatemessage)", + version: "0.0.{{ts}}", + type: "action", + props: { + wati, + contactDetails: { + propDefinition: [ + wati, + "contactDetails", + ], + }, + templateDetails: { + propDefinition: [ + wati, + "templateDetails", + ], + }, + }, + async run({ $ }) { + const response = await this.wati.sendTemplateMessage({ + contactDetails: this.contactDetails, + templateDetails: this.templateDetails, + }); + + $.export("$summary", `Successfully sent template message to ${this.contactDetails.name || this.contactDetails.number}`); + return response; + }, +}; diff --git a/components/wati/actions/update-contact-attribute/update-contact-attribute.mjs b/components/wati/actions/update-contact-attribute/update-contact-attribute.mjs new file mode 100644 index 0000000000000..d4bd4adc75701 --- /dev/null +++ b/components/wati/actions/update-contact-attribute/update-contact-attribute.mjs @@ -0,0 +1,34 @@ +import wati from "../../wati.app.mjs"; +import { axios } from "@pipedream/platform"; + +export default { + key: "wati-update-contact-attribute", + name: "Update Contact Attribute", + description: "Allows updating attributes/tags related to an existing contact. [See the documentation](https://docs.wati.io/reference/post_api-v1-updatecontactattributes-whatsappnumber)", + version: "0.0.{{ts}}", + type: "action", + props: { + wati, + contactDetails: { + propDefinition: [ + wati, + "contactDetails", + ], + }, + attributeDetails: { + propDefinition: [ + wati, + "attributeDetails", + ], + }, + }, + async run({ $ }) { + const response = await this.wati.updateContactAttributes({ + contactDetails: this.contactDetails, + attributeDetails: this.attributeDetails, + }); + + $.export("$summary", `Successfully updated attributes for contact ${this.contactDetails.name}`); + return response; + }, +}; diff --git a/components/wati/app/wati.app.ts b/components/wati/app/wati.app.ts index 208ee0d199f21..3530d21df0998 100644 --- a/components/wati/app/wati.app.ts +++ b/components/wati/app/wati.app.ts @@ -10,4 +10,4 @@ export default defineApp({ console.log(Object.keys(this.$auth)); }, }, -}); \ No newline at end of file +}); diff --git a/components/wati/package.json b/components/wati/package.json index 031b83cd95569..093bef26c673f 100644 --- a/components/wati/package.json +++ b/components/wati/package.json @@ -13,4 +13,4 @@ "publishConfig": { "access": "public" } -} \ No newline at end of file +} diff --git a/components/wati/sources/new-contact-created-instant/new-contact-created-instant.mjs b/components/wati/sources/new-contact-created-instant/new-contact-created-instant.mjs new file mode 100644 index 0000000000000..67490dfa16665 --- /dev/null +++ b/components/wati/sources/new-contact-created-instant/new-contact-created-instant.mjs @@ -0,0 +1,59 @@ +import wati from "../../wati.app.mjs"; +import { axios } from "@pipedream/platform"; + +export default { + key: "wati-new-contact-created-instant", + name: "New Contact Created", + description: "Emit new event when a contact is created from an incoming WhatsApp message. [See the documentation](https://docs.wati.io/reference/)", + version: "0.0.{{ts}}", + type: "source", + dedupe: "unique", + props: { + wati: { + type: "app", + app: "wati", + }, + http: { + type: "$.interface.http", + customResponse: false, + }, + db: "$.service.db", + whatsappNumber: { + propDefinition: [ + wati, + "whatsappNumber", + ], + }, + contactName: { + propDefinition: [ + wati, + "contactName", + ], + optional: true, + }, + messageContent: { + propDefinition: [ + wati, + "messageContent", + ], + optional: true, + }, + }, + async run(event) { + const { + whatsappNumber, contactName, messageContent, + } = event.body; + + await this.wati.emitContactCreatedEvent({ + whatsappNumber, + contactName, + messageContent, + }); + + this.$emit(event.body, { + id: Date.now(), + summary: `New contact created: ${contactName || whatsappNumber}`, + ts: Date.now(), + }); + }, +}; diff --git a/components/wati/sources/new-incoming-message-instant/new-incoming-message-instant.mjs b/components/wati/sources/new-incoming-message-instant/new-incoming-message-instant.mjs new file mode 100644 index 0000000000000..0d4a67364236a --- /dev/null +++ b/components/wati/sources/new-incoming-message-instant/new-incoming-message-instant.mjs @@ -0,0 +1,86 @@ +import wati from "../../wati.app.mjs"; +import { axios } from "@pipedream/platform"; + +export default { + key: "wati-new-incoming-message-instant", + name: "New Incoming Message Instant", + description: "Emit new event when there is an incoming message on your number. [See the documentation](https://docs.wati.io/reference/get_api-v1-getmessages-whatsappnumber)", + version: "0.0.{{ts}}", + type: "source", + dedupe: "unique", + props: { + wati: { + type: "app", + app: "wati", + }, + http: { + type: "$.interface.http", + customResponse: false, + }, + db: "$.service.db", + whatsappNumber: { + propDefinition: [ + wati, + "whatsappNumber", + ], + }, + messageContent: { + propDefinition: [ + wati, + "messageContent", + { + optional: true, + }, + ], + }, + timestamp: { + propDefinition: [ + wati, + "timestamp", + { + optional: true, + }, + ], + }, + }, + hooks: { + async deploy() { + const messages = await this.wati._makeRequest({ + path: `/getMessages/${this.whatsappNumber}`, + params: { + pageSize: 50, + pageNumber: 1, + }, + }); + if (messages && messages.messages && messages.messages.items) { + const items = messages.messages.items.slice(-50).reverse(); + for (const item of items) { + this.$emit(item, { + id: item.id, + summary: `New message: ${item.messageContent || "No content"}`, + ts: new Date(item.created).getTime(), + }); + } + } + }, + async activate() { + this.http.endpointUrl(); + }, + async deactivate() { + // No specific deactivation hook for this component + }, + }, + async run(event) { + const { + whatsappNumber, messageContent, timestamp, + } = event.body; + + this.$emit(event.body, { + id: event.body.id, + summary: `New message from ${whatsappNumber}`, + ts: timestamp + ? new Date(timestamp).getTime() + : Date.now(), + }); + }, +}; diff --git a/components/wati/wati.app.mjs b/components/wati/wati.app.mjs new file mode 100644 index 0000000000000..c9c8062eadd68 --- /dev/null +++ b/components/wati/wati.app.mjs @@ -0,0 +1,146 @@ +import { axios } from "@pipedream/platform"; + +export default { + type: "app", + app: "wati", + propDefinitions: { + whatsappNumber: { + type: "string", + label: "WhatsApp Number", + description: "Your WhatsApp number with country code.", + }, + messageContent: { + type: "string", + label: "Message Content", + description: "Content of the message.", + optional: true, + }, + contactName: { + type: "string", + label: "Contact Name", + description: "Name of the contact.", + optional: true, + }, + contactDetails: { + type: "object", + label: "Contact Details", + description: "Contact details, including name and WhatsApp number.", + properties: { + name: { + type: "string", + label: "Name", + }, + number: { + type: "string", + label: "Number", + }, + }, + }, + templateDetails: { + type: "object", + label: "Template Details", + description: "Details of the pre-approved template, including name and placeholders.", + properties: { + templateName: { + type: "string", + label: "Template Name", + }, + placeholders: { + type: "string[]", + label: "Placeholders", + description: "Template placeholders as an array of objects.", + }, + }, + }, + attributeDetails: { + type: "object", + label: "Attribute Details", + description: "Details of attributes to update.", + properties: { + attributeName: { + type: "string", + label: "Attribute Name", + }, + attributeValue: { + type: "string", + label: "Attribute Value", + }, + }, + }, + timestamp: { + type: "string", + label: "Timestamp", + description: "The timestamp of the message.", + optional: true, + }, + }, + methods: { + _baseUrl() { + return "https://your_wati_api_endpoint/api/v1"; + }, + async _makeRequest(opts = {}) { + const { + $ = this, method = "GET", path = "/", headers, ...otherOpts + } = opts; + return axios($, { + ...otherOpts, + method, + url: this._baseUrl() + path, + headers: { + ...headers, + Authorization: `Bearer ${this.$auth.oauth_access_token}`, + }, + }); + }, + async emitContactCreatedEvent({ + whatsappNumber, contactName, messageContent, + }) { + // Logic to emit event using given parameters + }, + async emitIncomingMessageEvent({ + whatsappNumber, messageContent, timestamp, + }) { + // Logic to emit event using given parameters + }, + async addContact(contactDetails) { + const { + number, name, + } = contactDetails; + return this._makeRequest({ + method: "POST", + path: `/addContact/${number}`, + data: { + name, + }, + }); + }, + async sendTemplateMessage({ + contactDetails, templateDetails, + }) { + return this._makeRequest({ + method: "POST", + path: "/sendTemplateMessage", + params: { + whatsappNumber: contactDetails.number, + }, + data: templateDetails, + }); + }, + async updateContactAttributes({ + contactDetails, attributeDetails, + }) { + return this._makeRequest({ + method: "POST", + path: `/updateContactAttributes/${contactDetails.number}`, + data: { + customParams: [ + { + name: attributeDetails.attributeName, + value: attributeDetails.attributeValue, + }, + ], + }, + }); + }, + }, +}; From 4372e80ac91a6612043fd74f8f366b2c45a5ad70 Mon Sep 17 00:00:00 2001 From: Luan Cazarine Date: Mon, 14 Oct 2024 11:00:36 -0300 Subject: [PATCH 2/5] [Components] wati #13213 Actions - Add Contact - Send Template Message - Update Contact Attribute Source - New Contact Created - New Incoming Message --- components/wati/.gitignore | 3 - .../wati/actions/add-contact/add-contact.mjs | 39 +++- .../send-template-message.mjs | 47 +++- .../update-contact-attribute.mjs | 31 ++- components/wati/app/wati.app.ts | 13 -- components/wati/package.json | 8 +- components/wati/sources/common/base.mjs | 68 ++++++ .../new-contact-created-instant.mjs | 59 ----- .../new-contact-created.mjs | 33 +++ .../new-contact-created/test-event.mjs | 33 +++ .../new-incoming-message-instant.mjs | 86 -------- .../new-incoming-message.mjs | 51 +++++ .../new-incoming-message/test-event.mjs | 26 +++ components/wati/wati.app.mjs | 204 +++++++++--------- 14 files changed, 410 insertions(+), 291 deletions(-) delete mode 100644 components/wati/.gitignore delete mode 100644 components/wati/app/wati.app.ts create mode 100644 components/wati/sources/common/base.mjs delete mode 100644 components/wati/sources/new-contact-created-instant/new-contact-created-instant.mjs create mode 100644 components/wati/sources/new-contact-created/new-contact-created.mjs create mode 100644 components/wati/sources/new-contact-created/test-event.mjs delete mode 100644 components/wati/sources/new-incoming-message-instant/new-incoming-message-instant.mjs create mode 100644 components/wati/sources/new-incoming-message/new-incoming-message.mjs create mode 100644 components/wati/sources/new-incoming-message/test-event.mjs diff --git a/components/wati/.gitignore b/components/wati/.gitignore deleted file mode 100644 index ec761ccab7595..0000000000000 --- a/components/wati/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -*.js -*.mjs -dist \ No newline at end of file diff --git a/components/wati/actions/add-contact/add-contact.mjs b/components/wati/actions/add-contact/add-contact.mjs index ce82d8ac1c12c..ec617afa928f9 100644 --- a/components/wati/actions/add-contact/add-contact.mjs +++ b/components/wati/actions/add-contact/add-contact.mjs @@ -1,5 +1,5 @@ +import { ConfigurationError } from "@pipedream/platform"; import wati from "../../wati.app.mjs"; -import { axios } from "@pipedream/platform"; export default { key: "wati-add-contact", @@ -9,16 +9,45 @@ export default { type: "action", props: { wati, - contactDetails: { + whatsappNumber: { propDefinition: [ wati, - "contactDetails", + "whatsappNumber", ], }, + name: { + type: "string", + label: "Name", + description: "The name of the contact", + optional: true, + }, + customParams: { + propDefinition: [ + wati, + "customParams", + ], + optional: true, + }, }, async run({ $ }) { - const response = await this.wati.addContact(this.contactDetails); - $.export("$summary", `Successfully added contact with name: ${this.contactDetails.name}`); + const response = await this.wati.addContact({ + $, + whatsappNumber: this.whatsappNumber, + data: { + name: this.name, + customParams: this.customParams && Object.entries(this.customParams).map(([ + key, + value, + ]) => ({ + name: key, + value, + })), + }, + }); + if (!response.result) { + throw new ConfigurationError(response.info); + } + $.export("$summary", `Successfully added contact with phone number: ${this.whatsappNumber}`); return response; }, }; diff --git a/components/wati/actions/send-template-message/send-template-message.mjs b/components/wati/actions/send-template-message/send-template-message.mjs index 4d812755007a6..3c02cc3974538 100644 --- a/components/wati/actions/send-template-message/send-template-message.mjs +++ b/components/wati/actions/send-template-message/send-template-message.mjs @@ -1,34 +1,63 @@ +import { ConfigurationError } from "@pipedream/platform"; import wati from "../../wati.app.mjs"; -import { axios } from "@pipedream/platform"; export default { key: "wati-send-template-message", name: "Send WhatsApp Template Message", description: "Enables sending of WhatsApp messages using a pre-approved template. [See the documentation](https://docs.wati.io/reference/post_api-v2-sendtemplatemessage)", - version: "0.0.{{ts}}", + version: "0.0.1", type: "action", props: { wati, - contactDetails: { + whatsappNumber: { propDefinition: [ wati, - "contactDetails", + "whatsappNumber", ], }, - templateDetails: { + customParams: { propDefinition: [ wati, - "templateDetails", + "customParams", ], + label: "Parameters", + description: "An object with template's custom params.", + }, + templateName: { + propDefinition: [ + wati, + "templateName", + ], + }, + broadcastName: { + type: "string", + label: "Broadcast Name", + description: "The name of broadcast.", }, }, async run({ $ }) { const response = await this.wati.sendTemplateMessage({ - contactDetails: this.contactDetails, - templateDetails: this.templateDetails, + $, + params: { + whatsappNumber: this.whatsappNumber, + }, + data: { + parameters: this.customParams && Object.entries(this.customParams).map(([ + key, + value, + ]) => ({ + name: key, + value, + })), + template_name: this.templateName, + broadcast_name: this.broadcastName, + }, }); + if (!response.result) { + throw new ConfigurationError(response.info); + } - $.export("$summary", `Successfully sent template message to ${this.contactDetails.name || this.contactDetails.number}`); + $.export("$summary", `Successfully sent template message to ${this.whatsappNumber}`); return response; }, }; diff --git a/components/wati/actions/update-contact-attribute/update-contact-attribute.mjs b/components/wati/actions/update-contact-attribute/update-contact-attribute.mjs index d4bd4adc75701..d1254ea3183bc 100644 --- a/components/wati/actions/update-contact-attribute/update-contact-attribute.mjs +++ b/components/wati/actions/update-contact-attribute/update-contact-attribute.mjs @@ -1,34 +1,47 @@ +import { ConfigurationError } from "@pipedream/platform"; import wati from "../../wati.app.mjs"; -import { axios } from "@pipedream/platform"; export default { key: "wati-update-contact-attribute", name: "Update Contact Attribute", description: "Allows updating attributes/tags related to an existing contact. [See the documentation](https://docs.wati.io/reference/post_api-v1-updatecontactattributes-whatsappnumber)", - version: "0.0.{{ts}}", + version: "0.0.1", type: "action", props: { wati, - contactDetails: { + whatsappNumber: { propDefinition: [ wati, - "contactDetails", + "whatsappNumber", ], }, - attributeDetails: { + customParams: { propDefinition: [ wati, - "attributeDetails", + "customParams", ], + optional: true, }, }, async run({ $ }) { const response = await this.wati.updateContactAttributes({ - contactDetails: this.contactDetails, - attributeDetails: this.attributeDetails, + $, + whatsappNumber: this.whatsappNumber, + data: { + customParams: this.customParams && Object.entries(this.customParams).map(([ + key, + value, + ]) => ({ + name: key, + value, + })), + }, }); + if (!response.result) { + throw new ConfigurationError(response.info); + } - $.export("$summary", `Successfully updated attributes for contact ${this.contactDetails.name}`); + $.export("$summary", `Successfully updated attributes for contact ${this.whatsappNumber}`); return response; }, }; diff --git a/components/wati/app/wati.app.ts b/components/wati/app/wati.app.ts deleted file mode 100644 index 3530d21df0998..0000000000000 --- a/components/wati/app/wati.app.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { defineApp } from "@pipedream/types"; - -export default defineApp({ - type: "app", - app: "wati", - propDefinitions: {}, - methods: { - // this.$auth contains connected account data - authKeys() { - console.log(Object.keys(this.$auth)); - }, - }, -}); diff --git a/components/wati/package.json b/components/wati/package.json index 093bef26c673f..90b471a941a4e 100644 --- a/components/wati/package.json +++ b/components/wati/package.json @@ -1,16 +1,18 @@ { "name": "@pipedream/wati", - "version": "0.0.1", + "version": "0.1.0", "description": "Pipedream WATI Components", - "main": "dist/app/wati.app.mjs", + "main": "wati.app.mjs", "keywords": [ "pipedream", "wati" ], - "files": ["dist"], "homepage": "https://pipedream.com/apps/wati", "author": "Pipedream (https://pipedream.com/)", "publishConfig": { "access": "public" + }, + "dependencies": { + "@pipedream/platform": "^3.0.3" } } diff --git a/components/wati/sources/common/base.mjs b/components/wati/sources/common/base.mjs new file mode 100644 index 0000000000000..50973654bf8bd --- /dev/null +++ b/components/wati/sources/common/base.mjs @@ -0,0 +1,68 @@ +import { DEFAULT_POLLING_SOURCE_TIMER_INTERVAL } from "@pipedream/platform"; +import wati from "../../wati.app.mjs"; + +export default { + props: { + wati, + db: "$.service.db", + timer: { + type: "$.interface.timer", + default: { + intervalSeconds: DEFAULT_POLLING_SOURCE_TIMER_INTERVAL, + }, + }, + }, + methods: { + getOpts() { + return {}; + }, + filterItems() { + () => true; + }, + _getLastDate() { + return this.db.get("lastDate") || 0; + }, + _setLastDate(lastDate) { + this.db.set("lastDate", lastDate); + }, + async emitEvent(maxResults = false) { + const lastDate = this._getLastDate(); + const dateField = this.getDateField(); + + const response = this.wati.paginate({ + fn: this.getFunction(), + ...this.getOpts(), + itemsField: this.getItemsField(), + maxResults, + }); + + let responseArray = []; + for await (const item of response) { + if (this.checkBreak(item, lastDate)) break; + responseArray.push(item); + } + + responseArray = responseArray.filter(this.filterItems); + + if (responseArray.length) { + this._setLastDate(responseArray[0][dateField]); + } + + for (const item of responseArray.reverse()) { + this.$emit(item, { + id: item.id, + summary: this.getSummary(item), + ts: Date.parse(item[dateField]), + }); + } + }, + }, + hooks: { + async deploy() { + await this.emitEvent(25); + }, + }, + async run() { + await this.emitEvent(); + }, +}; diff --git a/components/wati/sources/new-contact-created-instant/new-contact-created-instant.mjs b/components/wati/sources/new-contact-created-instant/new-contact-created-instant.mjs deleted file mode 100644 index 67490dfa16665..0000000000000 --- a/components/wati/sources/new-contact-created-instant/new-contact-created-instant.mjs +++ /dev/null @@ -1,59 +0,0 @@ -import wati from "../../wati.app.mjs"; -import { axios } from "@pipedream/platform"; - -export default { - key: "wati-new-contact-created-instant", - name: "New Contact Created", - description: "Emit new event when a contact is created from an incoming WhatsApp message. [See the documentation](https://docs.wati.io/reference/)", - version: "0.0.{{ts}}", - type: "source", - dedupe: "unique", - props: { - wati: { - type: "app", - app: "wati", - }, - http: { - type: "$.interface.http", - customResponse: false, - }, - db: "$.service.db", - whatsappNumber: { - propDefinition: [ - wati, - "whatsappNumber", - ], - }, - contactName: { - propDefinition: [ - wati, - "contactName", - ], - optional: true, - }, - messageContent: { - propDefinition: [ - wati, - "messageContent", - ], - optional: true, - }, - }, - async run(event) { - const { - whatsappNumber, contactName, messageContent, - } = event.body; - - await this.wati.emitContactCreatedEvent({ - whatsappNumber, - contactName, - messageContent, - }); - - this.$emit(event.body, { - id: Date.now(), - summary: `New contact created: ${contactName || whatsappNumber}`, - ts: Date.now(), - }); - }, -}; diff --git a/components/wati/sources/new-contact-created/new-contact-created.mjs b/components/wati/sources/new-contact-created/new-contact-created.mjs new file mode 100644 index 0000000000000..941c87f64730d --- /dev/null +++ b/components/wati/sources/new-contact-created/new-contact-created.mjs @@ -0,0 +1,33 @@ +import common from "../common/base.mjs"; +import sampleEmit from "./test-event.mjs"; + +export default { + ...common, + key: "wati-new-contact-created", + name: "New Contact Created", + description: "Emit new event when a contact is created from an incoming WhatsApp message.", + version: "0.0.1", + type: "source", + dedupe: "unique", + methods: { + ...common.methods, + getDateField() { + return "created"; + }, + getItemsField() { + return [ + "result", + ]; + }, + checkBreak(item, lastDate) { + return Date.parse(item.created) < lastDate; + }, + getFunction() { + return this.wati.listContacts; + }, + getSummary(item) { + return `New contact created: ${item.wAid}`; + }, + }, + sampleEmit, +}; diff --git a/components/wati/sources/new-contact-created/test-event.mjs b/components/wati/sources/new-contact-created/test-event.mjs new file mode 100644 index 0000000000000..3a696c5a7d163 --- /dev/null +++ b/components/wati/sources/new-contact-created/test-event.mjs @@ -0,0 +1,33 @@ +export default { + "id": "670934c1d464c11dd46c3b7f", + "wAid": "17759865200", + "firstName": "+17759865200", + "fullName": "+17759865200", + "phone": "17759865200", + "source": null, + "contactStatus": "VALID", + "photo": null, + "created": "Oct-11-2024", + "customParams": [ + { + "name": "name", + "value": "+17759865200" + }, + { + "name": "phone", + "value": "17759865200" + } + ], + "optedIn": false, + "isDeleted": false, + "lastUpdated": "2024-10-11T15:09:36.047Z", + "allowBroadcast": true, + "allowSMS": true, + "teamIds": [ + "6708393ad464c11dd46b3d73" + ], + "isInFlow": false, + "lastFlowId": null, + "currentFlowNodeId": null, + "selectedHubspotId": null +} \ No newline at end of file diff --git a/components/wati/sources/new-incoming-message-instant/new-incoming-message-instant.mjs b/components/wati/sources/new-incoming-message-instant/new-incoming-message-instant.mjs deleted file mode 100644 index 0d4a67364236a..0000000000000 --- a/components/wati/sources/new-incoming-message-instant/new-incoming-message-instant.mjs +++ /dev/null @@ -1,86 +0,0 @@ -import wati from "../../wati.app.mjs"; -import { axios } from "@pipedream/platform"; - -export default { - key: "wati-new-incoming-message-instant", - name: "New Incoming Message Instant", - description: "Emit new event when there is an incoming message on your number. [See the documentation](https://docs.wati.io/reference/get_api-v1-getmessages-whatsappnumber)", - version: "0.0.{{ts}}", - type: "source", - dedupe: "unique", - props: { - wati: { - type: "app", - app: "wati", - }, - http: { - type: "$.interface.http", - customResponse: false, - }, - db: "$.service.db", - whatsappNumber: { - propDefinition: [ - wati, - "whatsappNumber", - ], - }, - messageContent: { - propDefinition: [ - wati, - "messageContent", - { - optional: true, - }, - ], - }, - timestamp: { - propDefinition: [ - wati, - "timestamp", - { - optional: true, - }, - ], - }, - }, - hooks: { - async deploy() { - const messages = await this.wati._makeRequest({ - path: `/getMessages/${this.whatsappNumber}`, - params: { - pageSize: 50, - pageNumber: 1, - }, - }); - if (messages && messages.messages && messages.messages.items) { - const items = messages.messages.items.slice(-50).reverse(); - for (const item of items) { - this.$emit(item, { - id: item.id, - summary: `New message: ${item.messageContent || "No content"}`, - ts: new Date(item.created).getTime(), - }); - } - } - }, - async activate() { - this.http.endpointUrl(); - }, - async deactivate() { - // No specific deactivation hook for this component - }, - }, - async run(event) { - const { - whatsappNumber, messageContent, timestamp, - } = event.body; - - this.$emit(event.body, { - id: event.body.id, - summary: `New message from ${whatsappNumber}`, - ts: timestamp - ? new Date(timestamp).getTime() - : Date.now(), - }); - }, -}; diff --git a/components/wati/sources/new-incoming-message/new-incoming-message.mjs b/components/wati/sources/new-incoming-message/new-incoming-message.mjs new file mode 100644 index 0000000000000..0fd70c04a4ad5 --- /dev/null +++ b/components/wati/sources/new-incoming-message/new-incoming-message.mjs @@ -0,0 +1,51 @@ +import common from "../common/base.mjs"; +import sampleEmit from "./test-event.mjs"; + +export default { + ...common, + key: "wati-new-incoming-message", + name: "New Incoming Message", + description: "Emit new event when there is an incoming message on your number.", + version: "0.0.1", + type: "source", + dedupe: "unique", + props: { + ...common.props, + contactId: { + propDefinition: [ + common.props.wati, + "contactId", + ], + }, + }, + methods: { + ...common.methods, + getOpts() { + return { + whatsappNumber: `+${this.contactId}`, + }; + }, + getDateField() { + return "timestamp"; + }, + getItemsField() { + return [ + "messages", + "items", + ]; + }, + filterItems(item) { + return item.statusString === "SENT"; + }, + checkBreak(item, lastDate) { + return Date.parse(item.timestamp) < lastDate; + }, + getFunction() { + return this.wati.listContactMessages; + }, + getSummary(item) { + return `New message: ${item.text || "No content"}`; + }, + }, + sampleEmit, +}; diff --git a/components/wati/sources/new-incoming-message/test-event.mjs b/components/wati/sources/new-incoming-message/test-event.mjs new file mode 100644 index 0000000000000..c478a5e1f2f00 --- /dev/null +++ b/components/wati/sources/new-incoming-message/test-event.mjs @@ -0,0 +1,26 @@ +export default { + "replySourceMessage": null, + "messageReferral": null, + "text": "Know the Pricing", + "type": "text", + "data": null, + "timestamp": "1728656646", + "owner": false, + "statusString": "SENT", + "avatarUrl": null, + "assignedId": null, + "operatorName": null, + "localMessageId": null, + "failedDetail": null, + "referenceOrderId": null, + "contacts": null, + "messageProducts": null, + "orderProducts": null, + "interactiveData": null, + "orderDetailsViewModel": null, + "id": "67093506d464c11dd46c3bcf", + "created": "2024-10-11T14:24:06.891Z", + "conversationId": "670934f5d464c11dd46c3bc6", + "ticketId": "67093506d464c11dd46c3bcc", + "eventType": "message" +} \ No newline at end of file diff --git a/components/wati/wati.app.mjs b/components/wati/wati.app.mjs index c9c8062eadd68..1f052100a6a32 100644 --- a/components/wati/wati.app.mjs +++ b/components/wati/wati.app.mjs @@ -4,143 +4,139 @@ export default { type: "app", app: "wati", propDefinitions: { + contactId: { + type: "string", + label: "Contact Id", + description: "The Id of the contact.", + async options({ page }) { + const { result } = await this.listContacts({ + params: { + pageSize: page + 1, + }, + }); + + return result.map(({ wAid }) => wAid); + }, + }, whatsappNumber: { type: "string", label: "WhatsApp Number", description: "Your WhatsApp number with country code.", }, - messageContent: { - type: "string", - label: "Message Content", - description: "Content of the message.", - optional: true, - }, - contactName: { - type: "string", - label: "Contact Name", - description: "Name of the contact.", - optional: true, - }, - contactDetails: { + customParams: { type: "object", - label: "Contact Details", - description: "Contact details, including name and WhatsApp number.", - properties: { - name: { - type: "string", - label: "Name", - }, - number: { - type: "string", - label: "Number", - }, - }, + label: "Custom Params", + description: "An object with contact's custom fields.", }, - templateDetails: { - type: "object", - label: "Template Details", - description: "Details of the pre-approved template, including name and placeholders.", - properties: { - templateName: { - type: "string", - label: "Template Name", - }, - placeholders: { - type: "string[]", - label: "Placeholders", - description: "Template placeholders as an array of objects.", - }, - }, - }, - attributeDetails: { - type: "object", - label: "Attribute Details", - description: "Details of attributes to update.", - properties: { - attributeName: { - type: "string", - label: "Attribute Name", - }, - attributeValue: { - type: "string", - label: "Attribute Value", - }, - }, - }, - timestamp: { + templateName: { type: "string", - label: "Timestamp", - description: "The timestamp of the message.", - optional: true, + label: "Template Name", + description: "The name of template.", + async options({ page }) { + const { messageTemplates: data } = await this.listTemplates({ + params: { + pageSize: page + 1, + }, + }); + + return data.map(({ elementName }) => elementName); + }, }, }, methods: { _baseUrl() { - return "https://your_wati_api_endpoint/api/v1"; + return `${this.$auth.api_endpoint}/api/v1`; }, - async _makeRequest(opts = {}) { - const { - $ = this, method = "GET", path = "/", headers, ...otherOpts - } = opts; + _headers() { + return { + Authorization: `${this.$auth.access_token}`, + }; + }, + _makeRequest({ + $ = this, path, ...opts + }) { return axios($, { - ...otherOpts, - method, url: this._baseUrl() + path, - headers: { - ...headers, - Authorization: `Bearer ${this.$auth.oauth_access_token}`, - }, + headers: this._headers(), + ...opts, }); }, - async emitContactCreatedEvent({ - whatsappNumber, contactName, messageContent, - }) { - // Logic to emit event using given parameters + listContacts(opts = {}) { + return this._makeRequest({ + path: "/contacts", + ...opts, + }); }, - async emitIncomingMessageEvent({ - whatsappNumber, messageContent, timestamp, + listContactMessages({ + whatsappNumber, ...opts }) { - // Logic to emit event using given parameters + return this._makeRequest({ + path: `/getMessages/${whatsappNumber}`, + ...opts, + }); }, - async addContact(contactDetails) { - const { - number, name, - } = contactDetails; + listTemplates(opts = {}) { return this._makeRequest({ - method: "POST", - path: `/addContact/${number}`, - data: { - name, - }, + path: "/getMessageTemplates", + ...opts, }); }, - async sendTemplateMessage({ - contactDetails, templateDetails, + addContact({ + whatsappNumber, ...opts }) { + return this._makeRequest({ + method: "POST", + path: `/addContact/${whatsappNumber}`, + ...opts, + }); + }, + sendTemplateMessage(opts = {}) { return this._makeRequest({ method: "POST", path: "/sendTemplateMessage", - params: { - whatsappNumber: contactDetails.number, - }, - data: templateDetails, + ...opts, }); }, - async updateContactAttributes({ - contactDetails, attributeDetails, + updateContactAttributes({ + whatsappNumber, ...opts }) { return this._makeRequest({ method: "POST", - path: `/updateContactAttributes/${contactDetails.number}`, - data: { - customParams: [ - { - name: attributeDetails.attributeName, - value: attributeDetails.attributeValue, - }, - ], - }, + path: `/updateContactAttributes/${whatsappNumber}`, + ...opts, }); }, + async *paginate({ + fn, params = {}, itemsField, maxResults = null, ...opts + }) { + let hasMore = false; + let count = 0; + let page = 0; + + do { + params.pageNumber = ++page; + const response = await fn({ + params, + ...opts, + }); + + let data = response; + + for (const field of itemsField) { + data = data[field]; + } + + for (const d of data) { + yield d; + + if (maxResults && ++count === maxResults) { + return count; + } + } + + hasMore = !data.length; + + } while (hasMore); + }, }, }; From 1ab602e02cbcdea510eb5fd5ada036590eecf103 Mon Sep 17 00:00:00 2001 From: Luan Cazarine Date: Mon, 14 Oct 2024 11:02:34 -0300 Subject: [PATCH 3/5] pnpm update --- pnpm-lock.yaml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 897ec0ec5e7d9..72b98b721420d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -10926,7 +10926,10 @@ importers: specifiers: {} components/wati: - specifiers: {} + specifiers: + '@pipedream/platform': ^3.0.3 + dependencies: + '@pipedream/platform': 3.0.3 components/watsonx_ai: specifiers: {} From bc0fff10b7bcfdcb0ea4e6f6cea689a867d314ac Mon Sep 17 00:00:00 2001 From: Luan Cazarine Date: Mon, 14 Oct 2024 14:32:53 -0300 Subject: [PATCH 4/5] Update components/wati/wati.app.mjs Co-authored-by: michelle0927 --- components/wati/wati.app.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/wati/wati.app.mjs b/components/wati/wati.app.mjs index 1f052100a6a32..693b718dadba3 100644 --- a/components/wati/wati.app.mjs +++ b/components/wati/wati.app.mjs @@ -134,7 +134,7 @@ export default { } } - hasMore = !data.length; + hasMore = data.length; } while (hasMore); }, From d8b8309dac1ad2a82dc548eaf6be9c8238c9c1e3 Mon Sep 17 00:00:00 2001 From: Luan Cazarine Date: Tue, 15 Oct 2024 16:18:22 -0300 Subject: [PATCH 5/5] some adjusts --- components/wati/sources/common/base.mjs | 19 ++++------ .../new-contact-created.mjs | 16 ++++----- .../new-incoming-message.mjs | 25 ++++++------- components/wati/wati.app.mjs | 36 ++++++++++--------- 4 files changed, 46 insertions(+), 50 deletions(-) diff --git a/components/wati/sources/common/base.mjs b/components/wati/sources/common/base.mjs index 50973654bf8bd..8e10715b02532 100644 --- a/components/wati/sources/common/base.mjs +++ b/components/wati/sources/common/base.mjs @@ -13,11 +13,8 @@ export default { }, }, methods: { - getOpts() { - return {}; - }, - filterItems() { - () => true; + prepareData(data) { + return data; }, _getLastDate() { return this.db.get("lastDate") || 0; @@ -29,20 +26,16 @@ export default { const lastDate = this._getLastDate(); const dateField = this.getDateField(); - const response = this.wati.paginate({ - fn: this.getFunction(), - ...this.getOpts(), - itemsField: this.getItemsField(), - maxResults, - }); + const response = this.wati.paginate( + this.getPaginateOpts(maxResults), + ); let responseArray = []; for await (const item of response) { - if (this.checkBreak(item, lastDate)) break; responseArray.push(item); } - responseArray = responseArray.filter(this.filterItems); + responseArray = this.prepareData(responseArray, lastDate, maxResults); if (responseArray.length) { this._setLastDate(responseArray[0][dateField]); diff --git a/components/wati/sources/new-contact-created/new-contact-created.mjs b/components/wati/sources/new-contact-created/new-contact-created.mjs index 941c87f64730d..0b5c33e00917d 100644 --- a/components/wati/sources/new-contact-created/new-contact-created.mjs +++ b/components/wati/sources/new-contact-created/new-contact-created.mjs @@ -11,20 +11,20 @@ export default { dedupe: "unique", methods: { ...common.methods, + getPaginateOpts(maxResults) { + return { + fn: this.wati.listContacts, + itemsField: "result", + optsField: "data", + maxResults, + }; + }, getDateField() { return "created"; }, - getItemsField() { - return [ - "result", - ]; - }, checkBreak(item, lastDate) { return Date.parse(item.created) < lastDate; }, - getFunction() { - return this.wati.listContacts; - }, getSummary(item) { return `New contact created: ${item.wAid}`; }, diff --git a/components/wati/sources/new-incoming-message/new-incoming-message.mjs b/components/wati/sources/new-incoming-message/new-incoming-message.mjs index 0fd70c04a4ad5..448456aa51882 100644 --- a/components/wati/sources/new-incoming-message/new-incoming-message.mjs +++ b/components/wati/sources/new-incoming-message/new-incoming-message.mjs @@ -20,29 +20,30 @@ export default { }, methods: { ...common.methods, - getOpts() { + getPaginateOpts() { return { + fn: this.wati.listContactMessages, whatsappNumber: `+${this.contactId}`, + itemsField: [ + "messages", + ], + optsField: "params", }; }, getDateField() { return "timestamp"; }, - getItemsField() { - return [ - "messages", - "items", - ]; - }, - filterItems(item) { - return item.statusString === "SENT"; + prepareData(data, lastDate, maxResults) { + data = data + .filter((item) => item.statusString === "SENT" && Date.parse(item.created) > lastDate) + .sort((a, b) => Date.parse(b.created) - Date.parse(a.created)); + + if (maxResults && data.length > maxResults) data.length = maxResults; + return data; }, checkBreak(item, lastDate) { return Date.parse(item.timestamp) < lastDate; }, - getFunction() { - return this.wati.listContactMessages; - }, getSummary(item) { return `New message: ${item.text || "No content"}`; }, diff --git a/components/wati/wati.app.mjs b/components/wati/wati.app.mjs index 693b718dadba3..295552c153504 100644 --- a/components/wati/wati.app.mjs +++ b/components/wati/wati.app.mjs @@ -9,13 +9,14 @@ export default { label: "Contact Id", description: "The Id of the contact.", async options({ page }) { - const { result } = await this.listContacts({ - params: { - pageSize: page + 1, + const { result: { items } } = await this.listContacts({ + data: { + pageSize: 100, + pageNumber: page, }, }); - return result.map(({ wAid }) => wAid); + return items.map(({ wAid }) => wAid); }, }, whatsappNumber: { @@ -63,6 +64,7 @@ export default { }, listContacts(opts = {}) { return this._makeRequest({ + method: "POST", path: "/contacts", ...opts, }); @@ -107,26 +109,26 @@ export default { }); }, async *paginate({ - fn, params = {}, itemsField, maxResults = null, ...opts + fn, itemsField, optsField, maxResults = null, data = {}, params = {}, ...otherOpts }) { let hasMore = false; let count = 0; let page = 0; - do { - params.pageNumber = ++page; - const response = await fn({ - params, - ...opts, - }); + const opts = { + data, + params, + ...otherOpts, + }; - let data = response; + opts[optsField].pageSize = 100; - for (const field of itemsField) { - data = data[field]; - } + do { + opts[optsField].pageNumber = page++; + const response = await fn(opts); + const items = response[itemsField].items; - for (const d of data) { + for (const d of items) { yield d; if (maxResults && ++count === maxResults) { @@ -134,7 +136,7 @@ export default { } } - hasMore = data.length; + hasMore = items.length; } while (hasMore); },