From 9dbb4f1699324f04bbd6b800219b117915753c6b Mon Sep 17 00:00:00 2001 From: Luan Cazarine Date: Fri, 10 Jan 2025 18:04:14 -0300 Subject: [PATCH 1/7] dixa init --- .../dixa/actions/add-message/add-message.mjs | 58 +++++ .../create-conversation.mjs | 51 ++++ .../set-custom-contact-attributes.mjs | 34 +++ .../tag-conversation/tag-conversation.mjs | 36 +++ components/dixa/dixa.app.mjs | 237 +++++++++++++++++- components/dixa/package.json | 2 +- .../conversation-status-changed-instant.mjs | 192 ++++++++++++++ .../new-conversation-created-instant.mjs | 88 +++++++ ...w-customer-satisfaction-rating-instant.mjs | 126 ++++++++++ .../new-message-added-instant.mjs | 85 +++++++ .../new-tag-added-instant.mjs | 110 ++++++++ 11 files changed, 1015 insertions(+), 4 deletions(-) create mode 100644 components/dixa/actions/add-message/add-message.mjs create mode 100644 components/dixa/actions/create-conversation/create-conversation.mjs create mode 100644 components/dixa/actions/set-custom-contact-attributes/set-custom-contact-attributes.mjs create mode 100644 components/dixa/actions/tag-conversation/tag-conversation.mjs create mode 100644 components/dixa/sources/conversation-status-changed-instant/conversation-status-changed-instant.mjs create mode 100644 components/dixa/sources/new-conversation-created-instant/new-conversation-created-instant.mjs create mode 100644 components/dixa/sources/new-customer-satisfaction-rating-instant/new-customer-satisfaction-rating-instant.mjs create mode 100644 components/dixa/sources/new-message-added-instant/new-message-added-instant.mjs create mode 100644 components/dixa/sources/new-tag-added-instant/new-tag-added-instant.mjs diff --git a/components/dixa/actions/add-message/add-message.mjs b/components/dixa/actions/add-message/add-message.mjs new file mode 100644 index 0000000000000..24ab829e3c9cc --- /dev/null +++ b/components/dixa/actions/add-message/add-message.mjs @@ -0,0 +1,58 @@ +import dixa from "../../dixa.app.mjs"; +import { axios } from "@pipedream/platform"; + +export default { + key: "dixa-add-message", + name: "Add Message to Conversation", + description: "Adds a message to an existing conversation. [See the documentation]().", + version: "0.0.{{ts}}", + type: "action", + props: { + dixa, + conversationId: { + propDefinition: [ + dixa, + "conversationId", + ], + }, + attachments: { + propDefinition: [ + dixa, + "attachments", + ], + optional: true, + }, + content: { + propDefinition: [ + dixa, + "content", + ], + optional: true, + }, + externalId: { + propDefinition: [ + dixa, + "externalId", + ], + optional: true, + }, + integrationEmail: { + propDefinition: [ + dixa, + "integrationEmail", + ], + optional: true, + }, + }, + async run({ $ }) { + const response = await this.dixa.addMessage({ + conversationId: this.conversationId, + attachments: this.attachments, + content: this.content, + externalId: this.externalId, + integrationEmail: this.integrationEmail, + }); + $.export("$summary", `Added message to conversation ${this.conversationId}`); + return response; + }, +}; diff --git a/components/dixa/actions/create-conversation/create-conversation.mjs b/components/dixa/actions/create-conversation/create-conversation.mjs new file mode 100644 index 0000000000000..048a9d93bd639 --- /dev/null +++ b/components/dixa/actions/create-conversation/create-conversation.mjs @@ -0,0 +1,51 @@ +import dixa from "../../dixa.app.mjs"; +import { axios } from "@pipedream/platform"; + +export default { + key: "dixa-create-conversation", + name: "Create Conversation", + description: "Creates a new email or contact form-based conversation. [See the documentation](https://docs.dixa.io/openapi).", + version: "0.0.{{ts}}", + type: "action", + props: { + dixa: { + type: "app", + app: "dixa", + }, + subject: { + propDefinition: [ + "dixa", + "subject", + ], + }, + emailIntegrationId: { + propDefinition: [ + "dixa", + "emailIntegrationId", + ], + }, + messageType: { + propDefinition: [ + "dixa", + "messageType", + ], + }, + language: { + propDefinition: [ + "dixa", + "language", + ], + optional: true, + }, + }, + async run({ $ }) { + const response = await this.dixa.createConversation({ + subject: this.subject, + emailIntegrationId: this.emailIntegrationId, + messageType: this.messageType, + language: this.language, + }); + $.export("$summary", `Created conversation with subject: ${response.subject} and ID: ${response.id}`); + return response; + }, +}; diff --git a/components/dixa/actions/set-custom-contact-attributes/set-custom-contact-attributes.mjs b/components/dixa/actions/set-custom-contact-attributes/set-custom-contact-attributes.mjs new file mode 100644 index 0000000000000..defa404390c9a --- /dev/null +++ b/components/dixa/actions/set-custom-contact-attributes/set-custom-contact-attributes.mjs @@ -0,0 +1,34 @@ +import dixa from "../../dixa.app.mjs"; +import { axios } from "@pipedream/platform"; + +export default { + key: "dixa-set-custom-contact-attributes", + name: "Set Custom Contact Attributes", + description: "Updates custom attributes for a specified user. [See the documentation]()", + version: "0.0.{{ts}}", + type: "action", + props: { + dixa, + userId: { + propDefinition: [ + dixa, + "userId", + ], + }, + attributes: { + propDefinition: [ + dixa, + "attributes", + ], + optional: true, + }, + }, + async run({ $ }) { + const response = await this.dixa.updateCustomAttributes({ + userId: this.userId, + attributes: this.attributes, + }); + $.export("$summary", `Updated custom attributes for user ${this.userId}`); + return response; + }, +}; diff --git a/components/dixa/actions/tag-conversation/tag-conversation.mjs b/components/dixa/actions/tag-conversation/tag-conversation.mjs new file mode 100644 index 0000000000000..58b2d693ac0d5 --- /dev/null +++ b/components/dixa/actions/tag-conversation/tag-conversation.mjs @@ -0,0 +1,36 @@ +import dixa from "../../dixa.app.mjs"; +import { axios } from "@pipedream/platform"; + +export default { + key: "dixa-tag-conversation", + name: "Add Tag to Conversation", + description: "Adds a tag to a conversation. [See the documentation]()", + version: "0.0.{{ts}}", + type: "action", + props: { + dixa: { + type: "app", + app: "dixa", + }, + conversationId: { + propDefinition: [ + dixa, + "conversationId", + ], + }, + tagId: { + propDefinition: [ + dixa, + "tagId", + ], + }, + }, + async run({ $ }) { + const response = await this.dixa.addTag({ + conversationId: this.conversationId, + tagId: this.tagId, + }); + $.export("$summary", `Added tag ${this.tagId} to conversation ${this.conversationId}`); + return response; + }, +}; diff --git a/components/dixa/dixa.app.mjs b/components/dixa/dixa.app.mjs index bd8332a61a29b..faf08675d573a 100644 --- a/components/dixa/dixa.app.mjs +++ b/components/dixa/dixa.app.mjs @@ -1,11 +1,242 @@ +import { axios } from "@pipedream/platform"; + export default { type: "app", app: "dixa", - propDefinitions: {}, + version: "0.0.{{ts}}", + propDefinitions: { + name: { + type: "string", + label: "Name", + description: "Name of the event", + }, + subject: { + type: "string", + label: "Subject", + description: "The subject of the conversation", + }, + emailIntegrationId: { + type: "string", + label: "Email Integration ID", + description: "The ID of the email integration", + }, + messageType: { + type: "string", + label: "Message Type", + description: "The type of the message", + options: [ + { + label: "Inbound", + value: "inbound", + }, + { + label: "Outbound", + value: "outbound", + }, + ], + }, + language: { + type: "string", + label: "Language", + description: "Language of the conversation", + optional: true, + }, + conversationId: { + type: "string", + label: "Conversation ID", + description: "The ID of the conversation", + async options() { + const conversations = await this.listConversations(); + return conversations.map((conv) => ({ + label: conv.name || `Conversation ${conv.id}`, + value: conv.id, + })); + }, + }, + attachments: { + type: "string[]", + label: "Attachments", + description: "List of files to include in the message", + optional: true, + }, + content: { + type: "string", + label: "Content", + description: "Content of the message", + optional: true, + }, + externalId: { + type: "string", + label: "External ID", + description: "External ID of the message", + optional: true, + }, + integrationEmail: { + type: "string", + label: "Integration Email", + description: "Email for the integration", + optional: true, + }, + userId: { + type: "string", + label: "User ID", + description: "The ID of the user", + async options() { + const users = await this.listUsers(); + return users.map((user) => ({ + label: user.name || `User ${user.id}`, + value: user.id, + })); + }, + }, + attributes: { + type: "string", + label: "Attributes", + description: "Key-value pairs of attributes to update in JSON format", + optional: true, + }, + tagId: { + type: "string", + label: "Tag ID", + description: "The ID of the tag to add", + async options() { + const tags = await this.listTags(); + return tags.map((tag) => ({ + label: tag.name || `Tag ${tag.id}`, + value: tag.id, + })); + }, + }, + }, methods: { - // this.$auth contains connected account data authKeys() { console.log(Object.keys(this.$auth)); }, + _baseUrl() { + return "https://api.dixa.io/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.api_token}`, + "Content-Type": "application/json", + }, + }); + }, + async listConversations(opts = {}) { + return this._makeRequest({ + method: "GET", + path: "/conversations", + ...opts, + }); + }, + async listUsers(opts = {}) { + return this._makeRequest({ + method: "GET", + path: "/users", + ...opts, + }); + }, + async listTags(opts = {}) { + return this._makeRequest({ + method: "GET", + path: "/tags", + ...opts, + }); + }, + async createConversation({ + subject, emailIntegrationId, messageType, language, + }) { + const data = { + subject: this.subject, + email_integration_id: this.emailIntegrationId, + message_type: this.messageType, + }; + if (this.language) { + data.language = this.language; + } + return this._makeRequest({ + method: "POST", + path: "/conversations", + data, + }); + }, + async addMessage({ + conversationId, attachments, content, externalId, integrationEmail, + }) { + const data = {}; + if (this.attachments && this.attachments.length > 0) { + data.attachments = this.attachments.map((file) => ({ + file, + })); + } + if (this.content) { + data.content = this.content; + } + if (this.externalId) { + data.external_id = this.externalId; + } + if (this.integrationEmail) { + data.integration_email = this.integrationEmail; + } + return this._makeRequest({ + method: "POST", + path: `/conversations/${this.conversationId}/messages`, + data, + }); + }, + async updateCustomAttributes({ + userId, attributes, + }) { + let parsedAttributes = {}; + if (this.attributes) { + try { + parsedAttributes = JSON.parse(this.attributes); + } catch (e) { + throw new Error("Attributes must be a valid JSON string."); + } + } + return this._makeRequest({ + method: "PUT", + path: `/users/${this.userId}/attributes`, + data: parsedAttributes, + }); + }, + async addTag({ + conversationId, tagId, + }) { + return this._makeRequest({ + method: "POST", + path: `/conversations/${this.conversationId}/tags`, + data: { + tag_id: this.tagId, + }, + }); + }, + async paginate(fn, ...opts) { + let results = []; + let hasMore = true; + let page = 1; + while (hasMore) { + const response = await fn({ + ...opts, + page, + }); + if (response.length === 0) { + hasMore = false; + } else { + results = results.concat(response); + page += 1; + } + } + return results; + }, }, -}; \ No newline at end of file +}; diff --git a/components/dixa/package.json b/components/dixa/package.json index 3434eb1846aad..3de8be52d2fb4 100644 --- a/components/dixa/package.json +++ b/components/dixa/package.json @@ -12,4 +12,4 @@ "publishConfig": { "access": "public" } -} \ No newline at end of file +} diff --git a/components/dixa/sources/conversation-status-changed-instant/conversation-status-changed-instant.mjs b/components/dixa/sources/conversation-status-changed-instant/conversation-status-changed-instant.mjs new file mode 100644 index 0000000000000..a80fafe86c81e --- /dev/null +++ b/components/dixa/sources/conversation-status-changed-instant/conversation-status-changed-instant.mjs @@ -0,0 +1,192 @@ +import dixa from "../../dixa.app.mjs"; +import { axios } from "@pipedream/platform"; +import crypto from "crypto"; + +export default { + key: "dixa-conversation-status-changed-instant", + name: "Conversation Status Changed Instant", + description: "Emit new events when the status of a conversation changes (e.g., open, closed, or follow up). [See the documentation]()", + version: "0.0.{{ts}}", + type: "source", + dedupe: "unique", + props: { + dixa, + name: { + type: "string", + label: "Name", + description: "Name of the event", + options: [ + { + label: "Conversation Pending", + value: "conversationpending", + }, + { + label: "Conversation Message Added", + value: "conversationmessageadded", + }, + { + label: "Conversation Tag Added", + value: "conversationtagadded", + }, + { + label: "Conversation Assigned", + value: "conversationassigned", + }, + { + label: "Conversation Pending Expired", + value: "conversationpendingexpired", + }, + { + label: "Conversation Transferred", + value: "conversationtransferred", + }, + { + label: "Conversation Enqueued", + value: "conversationenqueued", + }, + { + label: "Conversation Created", + value: "conversationcreated", + }, + { + label: "Conversation Unassigned", + value: "conversationunassigned", + }, + { + label: "Conversation Open", + value: "conversationopen", + }, + { + label: "Conversation Abandoned", + value: "conversationabandoned", + }, + { + label: "Conversation Closed", + value: "conversationclosed", + }, + { + label: "Conversation Note Added", + value: "conversationnoteadded", + }, + { + label: "Conversation End User Replaced", + value: "conversationenduserreplaced", + }, + { + label: "Conversation Tag Removed", + value: "conversationtagremoved", + }, + { + label: "Conversation Rated", + value: "conversationrated", + }, + ], + }, + http: { + type: "$.interface.http", + customResponse: true, + label: "Webhook HTTP Endpoint", + description: "Receive webhooks from Dixa", + }, + db: "$.service.db", + }, + hooks: { + async activate() { + const webhookUrl = this.http.endpoint; + const events = [ + this.name, + ]; + const data = { + url: webhookUrl, + events: events, + }; + + try { + const response = await this.dixa._makeRequest({ + method: "POST", + path: "/webhooks", + data, + }); + const webhookId = response.id; + await this.db.set("webhookId", webhookId); + } catch (error) { + throw new Error(`Failed to create webhook: ${error.message}`); + } + }, + async deactivate() { + const webhookId = await this.db.get("webhookId"); + if (webhookId) { + try { + await this.dixa._makeRequest({ + method: "DELETE", + path: `/webhooks/${webhookId}`, + }); + await this.db.delete("webhookId"); + } catch (error) { + throw new Error(`Failed to delete webhook: ${error.message}`); + } + } + }, + async deploy() { + try { + const conversations = await this.dixa.listConversations({ + paginate: true, + limit: 50, + }); + for (const conv of conversations) { + if (conv.status) { + this.$emit(conv, { + id: conv.id, + summary: `Conversation ${conv.id} status changed to ${conv.status}`, + ts: Date.parse(conv.updatedAt) || Date.now(), + }); + } + } + } catch (error) { + throw new Error(`Failed to deploy historical events: ${error.message}`); + } + }, + }, + async run(event) { + const signature = event.headers["X-Dixa-Signature"] || event.headers["x-dixa-signature"]; + const rawBody = event.raw_body; + + if (!signature || !rawBody) { + this.http.respond({ + status: 400, + body: "Bad Request", + }); + return; + } + + const secret = this.dixa.$auth.api_token; + const computedSignature = crypto + .createHmac("sha256", secret) + .update(rawBody) + .digest("base64"); + + if (computedSignature !== signature) { + this.http.respond({ + status: 401, + body: "Unauthorized", + }); + return; + } + + const eventData = JSON.parse(rawBody); + const conversation = eventData.conversation; + + if (conversation && conversation.status) { + this.$emit(conversation, { + id: conversation.id, + summary: `Conversation ${conversation.id} status changed to ${conversation.status}`, + ts: Date.parse(conversation.updatedAt) || Date.now(), + }); + } + + this.http.respond({ + status: 200, + body: "OK", + }); + }, +}; diff --git a/components/dixa/sources/new-conversation-created-instant/new-conversation-created-instant.mjs b/components/dixa/sources/new-conversation-created-instant/new-conversation-created-instant.mjs new file mode 100644 index 0000000000000..c71b3a9f6141d --- /dev/null +++ b/components/dixa/sources/new-conversation-created-instant/new-conversation-created-instant.mjs @@ -0,0 +1,88 @@ +import dixa from "../../dixa.app.mjs"; +import crypto from "crypto"; +import { axios } from "@pipedream/platform"; + +export default { + key: "dixa-new-conversation-created-instant", + name: "New Conversation Created", + description: "Emit new event when a conversation is created in Dixa. [See the documentation]()", + version: "0.0.{{ts}}", + type: "source", + dedupe: "unique", + props: { + dixa: { + type: "app", + app: "dixa", + }, + http: { + type: "$.interface.http", + customResponse: true, + }, + db: "$.service.db", + name: { + propDefinition: [ + "dixa", + "name", + ], + }, + }, + hooks: { + async deploy() { + const conversations = await this.dixa.listConversations(); + const latestConversations = conversations.slice(-50).reverse(); + for (const conversation of latestConversations) { + this.$emit(conversation, { + id: conversation.id, + summary: `New conversation created: ${this.name}`, + ts: new Date(conversation.created_at).getTime(), + }); + } + }, + async activate() { + const endpoint = this.http.endpoint; + const webhookData = { + event: "conversation.created", + callback_url: endpoint, + }; + const response = await this.dixa._makeRequest({ + method: "POST", + path: "/webhooks", + data: webhookData, + }); + const webhookId = response.id; + await this.db.set("webhookId", webhookId); + }, + async deactivate() { + const webhookId = await this.db.get("webhookId"); + if (webhookId) { + await this.dixa._makeRequest({ + method: "DELETE", + path: `/webhooks/${webhookId}`, + }); + await this.db.set("webhookId", null); + } + }, + }, + async run(event) { + const signature = event.headers["X-Dixa-Signature"]; + const secret = this.dixa.$auth.api_token; + const rawBody = JSON.stringify(event.body); + const computedSignature = crypto.createHmac("sha256", secret).update(rawBody) + .digest("hex"); + + if (computedSignature !== signature) { + this.http.respond({ + status: 401, + body: "Unauthorized", + }); + return; + } + + const conversation = event.body; + this.$emit(conversation, { + id: conversation.id, + summary: `New conversation created: ${this.name}`, + ts: new Date(conversation.created_at).getTime(), + }); + }, +}; diff --git a/components/dixa/sources/new-customer-satisfaction-rating-instant/new-customer-satisfaction-rating-instant.mjs b/components/dixa/sources/new-customer-satisfaction-rating-instant/new-customer-satisfaction-rating-instant.mjs new file mode 100644 index 0000000000000..0a2ee815b12f3 --- /dev/null +++ b/components/dixa/sources/new-customer-satisfaction-rating-instant/new-customer-satisfaction-rating-instant.mjs @@ -0,0 +1,126 @@ +import dixa from "../../dixa.app.mjs"; +import crypto from "crypto"; +import { axios } from "@pipedream/platform"; + +export default { + key: "dixa-new-customer-satisfaction-rating-instant", + name: "New Customer Satisfaction Rating", + description: "Emit new event when a customer submits a satisfaction rating for a conversation. [See the documentation]()", + version: "0.0.{{ts}}", + type: "source", + dedupe: "unique", + props: { + dixa, + http: { + type: "$.interface.http", + customResponse: true, + }, + db: "$.service.db", + name: { + type: "string", + label: "Name", + description: "Name of the event", + }, + }, + methods: { + _getWebhookId() { + return this.db.get("webhookId"); + }, + _setWebhookId(id) { + this.db.set("webhookId", id); + }, + async createWebhook() { + const data = { + name: this.name, + event_type: "conversationrated", + callback_url: this.http.endpoint, + }; + const response = await this.dixa._makeRequest({ + method: "POST", + path: "/webhooks", + data, + }); + return response.id; + }, + async deleteWebhook(id) { + await this.dixa._makeRequest({ + method: "DELETE", + path: `/webhooks/${id}`, + }); + }, + async listRecentRatings() { + const ratings = await this.dixa._makeRequest({ + method: "GET", + path: "/ratings", + params: { + limit: 50, + sort: "created_at_desc", + }, + }); + return ratings; + }, + }, + hooks: { + async deploy() { + const recentRatings = await this.listRecentRatings(); + const sortedRatings = recentRatings.sort((a, b) => new Date(a.created_at) - new Date(b.created_at)); + for (const rating of sortedRatings) { + this.$emit(rating, { + id: rating.id || `${rating.created_at}`, + summary: `New satisfaction rating for conversation ${rating.conversation_id}`, + ts: Date.parse(rating.created_at) || Date.now(), + }); + } + }, + async activate() { + const webhookId = await this._getWebhookId(); + if (!webhookId) { + const id = await this.createWebhook(); + this._setWebhookId(id); + } + }, + async deactivate() { + const webhookId = await this._getWebhookId(); + if (webhookId) { + await this.deleteWebhook(webhookId); + this._setWebhookId(null); + } + }, + }, + async run(event) { + const signature = event.headers["x-dixa-signature"]; + const secret = this.dixa.$auth.webhook_secret; + const hash = crypto.createHmac("sha256", secret).update(event.body) + .digest("hex"); + + if (hash !== signature) { + this.http.respond({ + status: 401, + body: "Unauthorized", + }); + return; + } + + const eventData = JSON.parse(event.body); + if (eventData.event !== "conversationrated") { + this.http.respond({ + status: 400, + body: "Invalid event", + }); + return; + } + + const rating = eventData.data; + + this.$emit(rating, { + id: rating.id || `${rating.created_at}`, + summary: `New customer satisfaction rating received for conversation ${rating.conversation_id}`, + ts: Date.parse(rating.created_at) || Date.now(), + }); + + this.http.respond({ + status: 200, + body: "OK", + }); + }, +}; diff --git a/components/dixa/sources/new-message-added-instant/new-message-added-instant.mjs b/components/dixa/sources/new-message-added-instant/new-message-added-instant.mjs new file mode 100644 index 0000000000000..c1ce7924a5785 --- /dev/null +++ b/components/dixa/sources/new-message-added-instant/new-message-added-instant.mjs @@ -0,0 +1,85 @@ +import dixa from "../../dixa.app.mjs"; +import { axios } from "@pipedream/platform"; + +export default { + key: "dixa-new-message-added-instant", + name: "New Message Added to Conversation", + description: "Emit a new event when a new message is added to a conversation. [See the documentation]()", + version: "0.0.{{ts}}", + type: "source", + dedupe: "unique", + props: { + name: { + type: "string", + label: "Name", + description: "Name of the event", + }, + dixa: { + type: "app", + app: "dixa", + }, + db: "$.service.db", + http: { + type: "$.interface.http", + customResponse: false, + }, + }, + hooks: { + async deploy() { + try { + const messages = await this.dixa.paginate(this.dixa.listMessages, { + limit: 50, + }); + for (const message of messages) { + this.$emit(message, { + id: message.id, + summary: `New message in conversation ${message.conversationId}`, + ts: Date.parse(message.createdAt), + }); + } + } catch (error) { + this.$emit(error, "Error deploying component: " + error.message); + } + }, + async activate() { + try { + const webhookUrl = this.http.endpoint; + const webhook = await this.dixa._makeRequest({ + method: "POST", + path: "/webhooks", + data: { + url: webhookUrl, + events: [ + "conversationmessageadded", + ], + }, + }); + await this.db.set("webhookId", webhook.id); + } catch (error) { + this.$emit(error, "Error activating webhook: " + error.message); + } + }, + async deactivate() { + try { + const webhookId = await this.db.get("webhookId"); + if (webhookId) { + await this.dixa._makeRequest({ + method: "DELETE", + path: `/webhooks/${webhookId}`, + }); + await this.db.delete("webhookId"); + } + } catch (error) { + this.$emit(error, "Error deactivating webhook: " + error.message); + } + }, + }, + async run(event) { + const message = event.body; + this.$emit(message, { + id: message.id, + summary: `New message in conversation ${message.conversationId}`, + ts: Date.parse(message.createdAt) || Date.now(), + }); + }, +}; diff --git a/components/dixa/sources/new-tag-added-instant/new-tag-added-instant.mjs b/components/dixa/sources/new-tag-added-instant/new-tag-added-instant.mjs new file mode 100644 index 0000000000000..b0bb6108863ca --- /dev/null +++ b/components/dixa/sources/new-tag-added-instant/new-tag-added-instant.mjs @@ -0,0 +1,110 @@ +import dixa from "../../dixa.app.mjs"; +import crypto from "crypto"; +import { axios } from "@pipedream/platform"; + +export default { + key: "dixa-new-tag-added-instant", + name: "New Tag Added in Conversation", + description: "Emit new event when a tag is added to a conversation. [See the documentation]()", + version: "0.0.{{ts}}", + type: "source", + dedupe: "unique", + props: { + dixa: { + type: "app", + app: "dixa", + }, + db: "$.service.db", + http: { + type: "$.interface.http", + customResponse: true, + }, + name: { + propDefinition: [ + dixa, + "name", + ], + }, + }, + methods: { + async createWebhook() { + const webhookData = { + event: "conversationtagadded", + callback_url: this.http.endpoint, + name: this.name, + }; + const response = await this.dixa._makeRequest({ + method: "POST", + path: "/webhooks", + data: webhookData, + }); + return response.id; + }, + async deleteWebhook(webhookId) { + await this.dixa._makeRequest({ + method: "DELETE", + path: `/webhooks/${webhookId}`, + }); + }, + async fetchRecentTags() { + const events = await this.dixa.paginate(this.dixa.listEvents, { + eventType: "conversationtagadded", + limit: 50, + }); + return events; + }, + }, + hooks: { + async deploy() { + const recentTags = await this.fetchRecentTags(); + recentTags.forEach((tag) => { + this.$emit(tag, { + id: tag.id || tag.timestamp, + summary: `Tag "${tag.tagName}" added to conversation ${tag.conversationId}`, + ts: tag.timestamp + ? Date.parse(tag.timestamp) + : Date.now(), + }); + }); + }, + async activate() { + const webhookId = await this.createWebhook(); + await this.db.set("webhookId", webhookId); + }, + async deactivate() { + const webhookId = await this.db.get("webhookId"); + if (webhookId) { + await this.deleteWebhook(webhookId); + } + }, + }, + async run(event) { + const signature = event.headers["X-Dixa-Signature"] || event.headers["x-dixa-signature"]; + const secret = this.dixa.$auth.webhook_secret; + const computedSignature = crypto.createHmac("sha256", secret).update(event.rawBody) + .digest("hex"); + + if (computedSignature !== signature) { + await this.http.respond({ + status: 401, + body: "Unauthorized", + }); + return; + } + + const tagEvent = event.body; + + this.$emit(tagEvent, { + id: tagEvent.id || tagEvent.timestamp, + summary: `Tag "${tagEvent.tagName}" added to conversation ${tagEvent.conversationId}`, + ts: tagEvent.timestamp + ? Date.parse(tagEvent.timestamp) + : Date.now(), + }); + + await this.http.respond({ + status: 200, + body: "OK", + }); + }, +}; From 389b3b1a7b3dd968b954791a9cb502b6b8428bce Mon Sep 17 00:00:00 2001 From: Luan Cazarine Date: Tue, 14 Jan 2025 20:18:40 -0300 Subject: [PATCH 2/7] [Components] dixa #15252 Sources - New Customer Satisfaction Rating (Instant) - New Conversation Status Changed (Instant) - New Message Added (Instant) - New Tag Added (Instant) Actions - Create Conversation - Add Message - Set Custom Contact Attributes - Tag Conversation --- .../dixa/actions/add-message/add-message.mjs | 55 ++-- .../create-conversation.mjs | 89 +++-- .../set-custom-contact-attributes.mjs | 88 ++++- .../tag-conversation/tag-conversation.mjs | 18 +- components/dixa/common/utils.mjs | 24 ++ components/dixa/dixa.app.mjs | 306 ++++++++---------- components/dixa/package.json | 5 +- components/dixa/sources/common/base.mjs | 49 +++ .../conversation-status-changed-instant.mjs | 213 ++---------- .../test-event.mjs | 42 +++ .../new-conversation-created-instant.mjs | 94 +----- .../test-event.mjs | 35 ++ ...w-customer-satisfaction-rating-instant.mjs | 130 +------- .../test-event.mjs | 53 +++ .../new-message-added-instant.mjs | 91 +----- .../new-message-added-instant/test-event.mjs | 62 ++++ .../new-tag-added-instant.mjs | 114 +------ .../new-tag-added-instant/test-event.mjs | 57 ++++ 18 files changed, 743 insertions(+), 782 deletions(-) create mode 100644 components/dixa/common/utils.mjs create mode 100644 components/dixa/sources/common/base.mjs create mode 100644 components/dixa/sources/conversation-status-changed-instant/test-event.mjs create mode 100644 components/dixa/sources/new-conversation-created-instant/test-event.mjs create mode 100644 components/dixa/sources/new-customer-satisfaction-rating-instant/test-event.mjs create mode 100644 components/dixa/sources/new-message-added-instant/test-event.mjs create mode 100644 components/dixa/sources/new-tag-added-instant/test-event.mjs diff --git a/components/dixa/actions/add-message/add-message.mjs b/components/dixa/actions/add-message/add-message.mjs index 24ab829e3c9cc..ee28d0e5f1fdd 100644 --- a/components/dixa/actions/add-message/add-message.mjs +++ b/components/dixa/actions/add-message/add-message.mjs @@ -1,56 +1,65 @@ import dixa from "../../dixa.app.mjs"; -import { axios } from "@pipedream/platform"; export default { key: "dixa-add-message", name: "Add Message to Conversation", - description: "Adds a message to an existing conversation. [See the documentation]().", - version: "0.0.{{ts}}", + description: "Adds a message to an existing conversation. [See the documentation](https://docs.dixa.io/openapi/dixa-api/v1/tag/Conversations/#tag/Conversations/operation/postConversationsConversationidMessages).", + version: "0.0.1", type: "action", props: { dixa, - conversationId: { + endUserId: { propDefinition: [ dixa, - "conversationId", + "endUserId", ], }, - attachments: { + conversationId: { propDefinition: [ dixa, - "attachments", + "conversationId", + ({ endUserId }) => ({ + endUserId, + }), ], - optional: true, }, content: { - propDefinition: [ - dixa, - "content", - ], - optional: true, + type: "string", + label: "Content", + description: "Content of the message", }, - externalId: { + direction: { propDefinition: [ dixa, - "externalId", + "direction", ], - optional: true, + reloadProps: true, }, - integrationEmail: { + agentId: { propDefinition: [ dixa, - "integrationEmail", + "agentId", ], - optional: true, }, }, + async additionalProps(props) { + props.agentId.hidden = !this.direction === "Outbound"; + return {}; + }, async run({ $ }) { const response = await this.dixa.addMessage({ + $, conversationId: this.conversationId, - attachments: this.attachments, - content: this.content, - externalId: this.externalId, - integrationEmail: this.integrationEmail, + data: { + content: { + agentId: this.direction === "Outbound" + ? this.agentId + : undefined, + value: this.content, + _type: "Text", + }, + _type: this.direction, + }, }); $.export("$summary", `Added message to conversation ${this.conversationId}`); return response; diff --git a/components/dixa/actions/create-conversation/create-conversation.mjs b/components/dixa/actions/create-conversation/create-conversation.mjs index 048a9d93bd639..fd516cb4a12c9 100644 --- a/components/dixa/actions/create-conversation/create-conversation.mjs +++ b/components/dixa/actions/create-conversation/create-conversation.mjs @@ -1,51 +1,102 @@ import dixa from "../../dixa.app.mjs"; -import { axios } from "@pipedream/platform"; export default { key: "dixa-create-conversation", name: "Create Conversation", - description: "Creates a new email or contact form-based conversation. [See the documentation](https://docs.dixa.io/openapi).", - version: "0.0.{{ts}}", + description: "Creates a new email or contact form-based conversation. [See the documentation](https://docs.dixa.io/openapi/dixa-api/v1/tag/Conversations/#tag/Conversations/operation/postConversations).", + version: "0.0.1", type: "action", props: { - dixa: { - type: "app", - app: "dixa", + dixa, + requesterId: { + propDefinition: [ + dixa, + "endUserId", + ], + label: "Requester Id", }, - subject: { + direction: { propDefinition: [ - "dixa", - "subject", + dixa, + "direction", + ], + reloadProps: true, + }, + channel: { + type: "string", + label: "Channel", + description: "For outbound, only Email is supported. Inbound also supports ContactForm.", + options: [ + "Email", + "ContactForm", ], }, emailIntegrationId: { propDefinition: [ - "dixa", + dixa, "emailIntegrationId", ], }, - messageType: { + subject: { propDefinition: [ - "dixa", - "messageType", + dixa, + "subject", ], }, + message: { + type: "string", + label: "Message", + description: "The content message.", + }, language: { propDefinition: [ - "dixa", + dixa, "language", ], optional: true, }, + agentId: { + propDefinition: [ + dixa, + "agentId", + ], + optional: true, + }, + }, + async additionalProps(props) { + props.agentId.hidden = !this.direction === "Outbound"; + props.channel.options = this.direction === "Outbound" + ? [ + "Email", + ] + : [ + "ContactForm", + "Email", + ]; + return {}; }, async run({ $ }) { const response = await this.dixa.createConversation({ - subject: this.subject, - emailIntegrationId: this.emailIntegrationId, - messageType: this.messageType, - language: this.language, + $, + data: { + subject: this.subject, + emailIntegrationId: this.emailIntegrationId, + language: this.language, + requesterId: this.requesterId, + message: { + agentId: this.direction === "Outbound" + ? this.agentId + : undefined, + content: { + _type: "Text", + value: this.message, + }, + _type: this.direction, + }, + _type: this.channel, + }, }); - $.export("$summary", `Created conversation with subject: ${response.subject} and ID: ${response.id}`); + $.export("$summary", `Created conversation with Id: ${response.data.id}`); return response; }, }; diff --git a/components/dixa/actions/set-custom-contact-attributes/set-custom-contact-attributes.mjs b/components/dixa/actions/set-custom-contact-attributes/set-custom-contact-attributes.mjs index defa404390c9a..215126aba9506 100644 --- a/components/dixa/actions/set-custom-contact-attributes/set-custom-contact-attributes.mjs +++ b/components/dixa/actions/set-custom-contact-attributes/set-custom-contact-attributes.mjs @@ -1,32 +1,96 @@ import dixa from "../../dixa.app.mjs"; -import { axios } from "@pipedream/platform"; export default { key: "dixa-set-custom-contact-attributes", name: "Set Custom Contact Attributes", description: "Updates custom attributes for a specified user. [See the documentation]()", - version: "0.0.{{ts}}", + version: "0.0.1", type: "action", props: { dixa, userId: { propDefinition: [ dixa, - "userId", + "endUserId", ], + reloadProps: true, }, - attributes: { - propDefinition: [ - dixa, - "attributes", - ], - optional: true, + }, + async additionalProps() { + const props = {}; + const { data } = await this.dixa.listCustomAttributes(); + + for (const item of data) { + if (item.isDeactivated || item.isArchived || item.entityType != "Contact") continue; + + props[item.id] = { + type: "string", + label: item.label, + description: item.description, + optional: !item.isRequired, + default: item.inputDefinition.placeholder, + }; + + if (item.inputDefinition._type === "Select") { + props[item.id].options = this.prepareOptions(item.inputDefinition.options); + } + } + return props; + }, + methods: { + prepareOptions(options, parentVal = "", parentLabel = "") { + const newOptions = []; + + for (const opt of options) { + const newLabel = parentLabel + ? `${parentLabel} - ${opt.label}` + : opt.label; + + const newVal = parentVal + ? `${parentVal}/${opt.value}` + : opt.value; + + if (opt.nestedOptions.length) { + newOptions.push(...this.prepareOptions(opt.nestedOptions, newVal, newLabel)); + } else { + newOptions.push({ + label: newLabel, + value: newVal, + }); + } + } + return newOptions; + }, + async prepareData(data) { + const response = {}; + const { data: customAttributes } = await this.dixa.listCustomAttributes(); + Object.entries(data).map(([ + key, + val, + ]) => { + const customAttribute = customAttributes.find((attr) => attr.id === key); + + response[key] = customAttribute.inputDefinition._type != "Text" + ? val.split("/") + : val; + }); + return response; }, }, async run({ $ }) { - const response = await this.dixa.updateCustomAttributes({ - userId: this.userId, - attributes: this.attributes, + const { + dixa, + // eslint-disable-next-line no-unused-vars + prepareOptions, + prepareData, + userId, + ...data + } = this; + + const response = await dixa.updateCustomAttributes({ + $, + userId, + data: await prepareData(data), }); $.export("$summary", `Updated custom attributes for user ${this.userId}`); return response; diff --git a/components/dixa/actions/tag-conversation/tag-conversation.mjs b/components/dixa/actions/tag-conversation/tag-conversation.mjs index 58b2d693ac0d5..5427ff70441a3 100644 --- a/components/dixa/actions/tag-conversation/tag-conversation.mjs +++ b/components/dixa/actions/tag-conversation/tag-conversation.mjs @@ -1,21 +1,26 @@ import dixa from "../../dixa.app.mjs"; -import { axios } from "@pipedream/platform"; export default { key: "dixa-tag-conversation", name: "Add Tag to Conversation", - description: "Adds a tag to a conversation. [See the documentation]()", - version: "0.0.{{ts}}", + description: "Adds a tag to a conversation. [See the documentation](https://docs.dixa.io/openapi/dixa-api/v1/tag/Tags/#tag/Tags/operation/putConversationsConversationidTagsTagid)", + version: "0.0.1", type: "action", props: { - dixa: { - type: "app", - app: "dixa", + dixa, + endUserId: { + propDefinition: [ + dixa, + "endUserId", + ], }, conversationId: { propDefinition: [ dixa, "conversationId", + ({ endUserId }) => ({ + endUserId, + }), ], }, tagId: { @@ -27,6 +32,7 @@ export default { }, async run({ $ }) { const response = await this.dixa.addTag({ + $, conversationId: this.conversationId, tagId: this.tagId, }); diff --git a/components/dixa/common/utils.mjs b/components/dixa/common/utils.mjs new file mode 100644 index 0000000000000..dcc9cc61f6f41 --- /dev/null +++ b/components/dixa/common/utils.mjs @@ -0,0 +1,24 @@ +export const parseObject = (obj) => { + if (!obj) return undefined; + + if (Array.isArray(obj)) { + return obj.map((item) => { + if (typeof item === "string") { + try { + return JSON.parse(item); + } catch (e) { + return item; + } + } + return item; + }); + } + if (typeof obj === "string") { + try { + return JSON.parse(obj); + } catch (e) { + return obj; + } + } + return obj; +}; diff --git a/components/dixa/dixa.app.mjs b/components/dixa/dixa.app.mjs index faf08675d573a..d637977931ddf 100644 --- a/components/dixa/dixa.app.mjs +++ b/components/dixa/dixa.app.mjs @@ -3,240 +3,210 @@ import { axios } from "@pipedream/platform"; export default { type: "app", app: "dixa", - version: "0.0.{{ts}}", propDefinitions: { - name: { + endUserId: { type: "string", - label: "Name", - description: "Name of the event", + label: "End User Id", + description: "The id of the end user.", + async options({ page }) { + const { data } = await this.listEndUsers({ + params: { + page: page + 1, + }, + }); + return data.map(({ + id: value, displayName: label, + }) => ({ + label, + value, + })); + }, }, - subject: { + direction: { type: "string", - label: "Subject", - description: "The subject of the conversation", + label: "Direction", + description: "The direction of the message", + options: [ + "Inbound", + "Outbound", + ], }, emailIntegrationId: { type: "string", label: "Email Integration ID", - description: "The ID of the email integration", + description: "The contact endpoint in the organization.", + async options() { + const { data } = await this.listContactEndpoints(); + return data.filter(({ _type }) => _type === "EmailEndpoint").map(({ address }) => address); + }, }, - messageType: { + subject: { type: "string", - label: "Message Type", - description: "The type of the message", - options: [ - { - label: "Inbound", - value: "inbound", - }, - { - label: "Outbound", - value: "outbound", - }, - ], + label: "Subject", + description: "The subject of the conversation", }, language: { type: "string", label: "Language", - description: "Language of the conversation", - optional: true, + description: "The [2-letter ISO 639-1](https://en.wikipedia.org/wiki/List_of_ISO_639_language_codes) language of the conversation", }, - conversationId: { + agentId: { type: "string", - label: "Conversation ID", - description: "The ID of the conversation", - async options() { - const conversations = await this.listConversations(); - return conversations.map((conv) => ({ - label: conv.name || `Conversation ${conv.id}`, - value: conv.id, + label: "Agent Id", + description: "The id of the agent.", + hidden: true, + async options({ page }) { + const { data } = await this.listAgents({ + params: { + page: page + 1, + }, + }); + + return data.map(({ + id: value, displayName: label, + }) => ({ + label, + value, })); }, }, - attachments: { - type: "string[]", - label: "Attachments", - description: "List of files to include in the message", - optional: true, - }, - content: { - type: "string", - label: "Content", - description: "Content of the message", - optional: true, - }, - externalId: { - type: "string", - label: "External ID", - description: "External ID of the message", - optional: true, - }, - integrationEmail: { - type: "string", - label: "Integration Email", - description: "Email for the integration", - optional: true, - }, - userId: { + conversationId: { type: "string", - label: "User ID", - description: "The ID of the user", - async options() { - const users = await this.listUsers(); - return users.map((user) => ({ - label: user.name || `User ${user.id}`, - value: user.id, - })); + label: "Conversation ID", + description: "The ID of the conversation", + async options({ endUserId }) { + const { data } = await this.listConversations({ + endUserId, + }); + return data.map(({ + id: value, direction, toEmail, fromEmail, + }) => { + return { + label: `${direction} - ${direction === "Inbound" + ? fromEmail + : toEmail}`, + value, + }; + }); }, }, - attributes: { - type: "string", - label: "Attributes", - description: "Key-value pairs of attributes to update in JSON format", - optional: true, - }, tagId: { type: "string", label: "Tag ID", description: "The ID of the tag to add", async options() { - const tags = await this.listTags(); - return tags.map((tag) => ({ - label: tag.name || `Tag ${tag.id}`, - value: tag.id, - })); + const { data } = await this.listTags(); + return data + .filter(({ state }) => state === "Active") + .map(({ + id: value, name: label, + }) => ({ + label, + value, + })); }, }, }, methods: { - authKeys() { - console.log(Object.keys(this.$auth)); - }, _baseUrl() { - return "https://api.dixa.io/v1"; + return "https://dev.dixa.io/v1"; }, - async _makeRequest(opts = {}) { - const { - $ = this, method = "GET", path = "/", headers = {}, ...otherOpts - } = opts; + _headers() { + return { + "authorization": `${this.$auth.api_key}`, + }; + }, + _makeRequest({ + $ = this, path, ...opts + }) { return axios($, { - ...otherOpts, - method, url: this._baseUrl() + path, - headers: { - ...headers, - "Authorization": `Bearer ${this.$auth.api_token}`, - "Content-Type": "application/json", - }, + headers: this._headers(), + ...opts, }); }, - async listConversations(opts = {}) { + listEndUsers(opts = {}) { return this._makeRequest({ - method: "GET", - path: "/conversations", + path: "/endusers", ...opts, }); }, - async listUsers(opts = {}) { + listAgents(opts = {}) { return this._makeRequest({ - method: "GET", - path: "/users", + path: "/agents", ...opts, }); }, - async listTags(opts = {}) { + listContactEndpoints(opts = {}) { return this._makeRequest({ - method: "GET", - path: "/tags", + path: "/contact-endpoints", ...opts, }); }, - async createConversation({ - subject, emailIntegrationId, messageType, language, + listConversations({ + endUserId, ...opts }) { - const data = { - subject: this.subject, - email_integration_id: this.emailIntegrationId, - message_type: this.messageType, - }; - if (this.language) { - data.language = this.language; - } return this._makeRequest({ - method: "POST", - path: "/conversations", - data, + path: `/endusers/${endUserId}/conversations`, + ...opts, }); }, - async addMessage({ - conversationId, attachments, content, externalId, integrationEmail, - }) { - const data = {}; - if (this.attachments && this.attachments.length > 0) { - data.attachments = this.attachments.map((file) => ({ - file, - })); - } - if (this.content) { - data.content = this.content; - } - if (this.externalId) { - data.external_id = this.externalId; - } - if (this.integrationEmail) { - data.integration_email = this.integrationEmail; - } + listCustomAttributes() { return this._makeRequest({ - method: "POST", - path: `/conversations/${this.conversationId}/messages`, - data, + path: "/custom-attributes", }); }, - async updateCustomAttributes({ - userId, attributes, + addTag({ + conversationId, tagId, }) { - let parsedAttributes = {}; - if (this.attributes) { - try { - parsedAttributes = JSON.parse(this.attributes); - } catch (e) { - throw new Error("Attributes must be a valid JSON string."); - } - } return this._makeRequest({ method: "PUT", - path: `/users/${this.userId}/attributes`, - data: parsedAttributes, + path: `/conversations/${conversationId}/tags/${tagId}`, }); }, - async addTag({ - conversationId, tagId, + updateCustomAttributes({ + userId, ...opts }) { + return this._makeRequest({ + method: "PATCH", + path: `/endusers/${userId}/custom-attributes`, + ...opts, + }); + }, + createWebhook(opts = {}) { return this._makeRequest({ method: "POST", - path: `/conversations/${this.conversationId}/tags`, - data: { - tag_id: this.tagId, - }, + path: "/webhooks", + ...opts, }); }, - async paginate(fn, ...opts) { - let results = []; - let hasMore = true; - let page = 1; - while (hasMore) { - const response = await fn({ - ...opts, - page, - }); - if (response.length === 0) { - hasMore = false; - } else { - results = results.concat(response); - page += 1; - } - } - return results; + deleteWebhook(webhookId) { + return this._makeRequest({ + method: "DELETE", + path: `/webhooks/${webhookId}`, + }); + }, + listTags(opts = {}) { + return this._makeRequest({ + path: "/tags", + ...opts, + }); + }, + createConversation(opts = {}) { + return this._makeRequest({ + method: "POST", + path: "/conversations", + ...opts, + }); + }, + addMessage({ + conversationId, ...opts + }) { + return this._makeRequest({ + method: "POST", + path: `/conversations/${conversationId}/messages`, + ...opts, + }); }, }, }; diff --git a/components/dixa/package.json b/components/dixa/package.json index 3de8be52d2fb4..f7012f0fe3697 100644 --- a/components/dixa/package.json +++ b/components/dixa/package.json @@ -1,6 +1,6 @@ { "name": "@pipedream/dixa", - "version": "0.0.1", + "version": "0.1.0", "description": "Pipedream Dixa Components", "main": "dixa.app.mjs", "keywords": [ @@ -11,5 +11,8 @@ "author": "Pipedream (https://pipedream.com/)", "publishConfig": { "access": "public" + }, + "dependencies": { + "@pipedream/platform": "^3.0.3" } } diff --git a/components/dixa/sources/common/base.mjs b/components/dixa/sources/common/base.mjs new file mode 100644 index 0000000000000..5c9784d277e39 --- /dev/null +++ b/components/dixa/sources/common/base.mjs @@ -0,0 +1,49 @@ +import dixa from "../../dixa.app.mjs"; + +export default { + props: { + dixa, + http: "$.interface.http", + db: "$.service.db", + name: { + type: "string", + label: "Name", + description: "The Webhook Subscription name", + }, + }, + methods: { + _getHookId() { + return this.db.get("hookId"); + }, + _setHookId(hookId) { + this.db.set("hookId", hookId); + }, + }, + hooks: { + async activate() { + const response = await this.dixa.createWebhook({ + data: { + name: this.name, + url: this.http.endpoint, + events: this.getEventType(), + authorization: { + _type: "NoAuth", + }, + }, + }); + this._setHookId(response.data.id); + }, + async deactivate() { + const webhookId = this._getHookId(); + await this.dixa.deleteWebhook(webhookId); + }, + }, + async run({ body }) { + const ts = Date.parse(new Date()); + this.$emit(body, { + id: body.event_id, + summary: this.getSummary(body), + ts: ts, + }); + }, +}; diff --git a/components/dixa/sources/conversation-status-changed-instant/conversation-status-changed-instant.mjs b/components/dixa/sources/conversation-status-changed-instant/conversation-status-changed-instant.mjs index a80fafe86c81e..91d39915ae677 100644 --- a/components/dixa/sources/conversation-status-changed-instant/conversation-status-changed-instant.mjs +++ b/components/dixa/sources/conversation-status-changed-instant/conversation-status-changed-instant.mjs @@ -1,192 +1,41 @@ -import dixa from "../../dixa.app.mjs"; -import { axios } from "@pipedream/platform"; -import crypto from "crypto"; +import common from "../common/base.mjs"; +import sampleEmit from "./test-event.mjs"; export default { + ...common, key: "dixa-conversation-status-changed-instant", - name: "Conversation Status Changed Instant", - description: "Emit new events when the status of a conversation changes (e.g., open, closed, or follow up). [See the documentation]()", - version: "0.0.{{ts}}", + name: "New Conversation Status Changed (Instant)", + description: "Emit new events when the status of a conversation changes (e.g., open, closed, or abandoned).", + version: "0.0.1", type: "source", dedupe: "unique", - props: { - dixa, - name: { - type: "string", - label: "Name", - description: "Name of the event", - options: [ - { - label: "Conversation Pending", - value: "conversationpending", - }, - { - label: "Conversation Message Added", - value: "conversationmessageadded", - }, - { - label: "Conversation Tag Added", - value: "conversationtagadded", - }, - { - label: "Conversation Assigned", - value: "conversationassigned", - }, - { - label: "Conversation Pending Expired", - value: "conversationpendingexpired", - }, - { - label: "Conversation Transferred", - value: "conversationtransferred", - }, - { - label: "Conversation Enqueued", - value: "conversationenqueued", - }, - { - label: "Conversation Created", - value: "conversationcreated", - }, - { - label: "Conversation Unassigned", - value: "conversationunassigned", - }, - { - label: "Conversation Open", - value: "conversationopen", - }, - { - label: "Conversation Abandoned", - value: "conversationabandoned", - }, - { - label: "Conversation Closed", - value: "conversationclosed", - }, - { - label: "Conversation Note Added", - value: "conversationnoteadded", - }, - { - label: "Conversation End User Replaced", - value: "conversationenduserreplaced", - }, - { - label: "Conversation Tag Removed", - value: "conversationtagremoved", - }, - { - label: "Conversation Rated", - value: "conversationrated", - }, - ], - }, - http: { - type: "$.interface.http", - customResponse: true, - label: "Webhook HTTP Endpoint", - description: "Receive webhooks from Dixa", - }, - db: "$.service.db", - }, - hooks: { - async activate() { - const webhookUrl = this.http.endpoint; - const events = [ - this.name, + methods: { + ...common.methods, + getEventType() { + return [ + "ConversationPending", + "ConversationMessageAdded", + "ConversationTagAdded", + "ConversationAssigned", + "ConversationPendingExpired", + "ConversationTransferred", + "ConversationEnqueued", + "ConversationCreated", + "ConversationUnassigned", + "ConversationOpen", + "ConversationAbandoned", + "ConversationClosed", + "ConversationNoteAdded", + "ConversationEndUserReplaced", + "ConversationTagRemoved", + "ConversationRated", ]; - const data = { - url: webhookUrl, - events: events, - }; - - try { - const response = await this.dixa._makeRequest({ - method: "POST", - path: "/webhooks", - data, - }); - const webhookId = response.id; - await this.db.set("webhookId", webhookId); - } catch (error) { - throw new Error(`Failed to create webhook: ${error.message}`); - } - }, - async deactivate() { - const webhookId = await this.db.get("webhookId"); - if (webhookId) { - try { - await this.dixa._makeRequest({ - method: "DELETE", - path: `/webhooks/${webhookId}`, - }); - await this.db.delete("webhookId"); - } catch (error) { - throw new Error(`Failed to delete webhook: ${error.message}`); - } - } }, - async deploy() { - try { - const conversations = await this.dixa.listConversations({ - paginate: true, - limit: 50, - }); - for (const conv of conversations) { - if (conv.status) { - this.$emit(conv, { - id: conv.id, - summary: `Conversation ${conv.id} status changed to ${conv.status}`, - ts: Date.parse(conv.updatedAt) || Date.now(), - }); - } - } - } catch (error) { - throw new Error(`Failed to deploy historical events: ${error.message}`); - } + getSummary({ + data, event_fqn: eventType, + }) { + return `Conversation ${data.conversation.csid} status changed to ${eventType}`; }, }, - async run(event) { - const signature = event.headers["X-Dixa-Signature"] || event.headers["x-dixa-signature"]; - const rawBody = event.raw_body; - - if (!signature || !rawBody) { - this.http.respond({ - status: 400, - body: "Bad Request", - }); - return; - } - - const secret = this.dixa.$auth.api_token; - const computedSignature = crypto - .createHmac("sha256", secret) - .update(rawBody) - .digest("base64"); - - if (computedSignature !== signature) { - this.http.respond({ - status: 401, - body: "Unauthorized", - }); - return; - } - - const eventData = JSON.parse(rawBody); - const conversation = eventData.conversation; - - if (conversation && conversation.status) { - this.$emit(conversation, { - id: conversation.id, - summary: `Conversation ${conversation.id} status changed to ${conversation.status}`, - ts: Date.parse(conversation.updatedAt) || Date.now(), - }); - } - - this.http.respond({ - status: 200, - body: "OK", - }); - }, + sampleEmit, }; diff --git a/components/dixa/sources/conversation-status-changed-instant/test-event.mjs b/components/dixa/sources/conversation-status-changed-instant/test-event.mjs new file mode 100644 index 0000000000000..f853fc35bbcd3 --- /dev/null +++ b/components/dixa/sources/conversation-status-changed-instant/test-event.mjs @@ -0,0 +1,42 @@ +export default { + "event_fqn": "CONVERSATION_PENDING", + "event_id": "12345678-1234-1234-1234-1234567890", + "event_timestamp": "2025-01-14T22:01:05.429Z", + "event_version": "1", + "organization": { + "id": "12345678-1234-1234-1234-1234567890", + "name": "Org Name" + }, + "data": { + "conversation": { + "assignee": { + "email": "email@dixa.com", + "id": "12345678-1234-1234-1234-1234567890", + "name": "Agent Name", + "phone": "+123456789", + "roles": [], + "user_type": "Member" + }, + "channel": "EMAIL", + "contact_point": "contact@email.dixa.io", + "created_at": "2025-01-13T19:48:33.178Z", + "csid": 2, + "direction": "OUTBOUND", + "queue": { + "id": "12345678-1234-1234-1234-1234567890", + "name": "default" + }, + "requester": { + "email": "contact@email.com", + "id": "12345678-1234-1234-1234-1234567890", + "name": "Contact Name", + "phone": null, + "roles": [], + "user_type": "Contact" + }, + "status": "PENDING", + "subject": "Subject Text", + "tags": [] + } + } +} \ No newline at end of file diff --git a/components/dixa/sources/new-conversation-created-instant/new-conversation-created-instant.mjs b/components/dixa/sources/new-conversation-created-instant/new-conversation-created-instant.mjs index c71b3a9f6141d..5621505271fd5 100644 --- a/components/dixa/sources/new-conversation-created-instant/new-conversation-created-instant.mjs +++ b/components/dixa/sources/new-conversation-created-instant/new-conversation-created-instant.mjs @@ -1,88 +1,24 @@ -import dixa from "../../dixa.app.mjs"; -import crypto from "crypto"; -import { axios } from "@pipedream/platform"; +import common from "../common/base.mjs"; +import sampleEmit from "./test-event.mjs"; export default { + ...common, key: "dixa-new-conversation-created-instant", - name: "New Conversation Created", - description: "Emit new event when a conversation is created in Dixa. [See the documentation]()", - version: "0.0.{{ts}}", + name: "New Conversation Created (Instant)", + description: "Emit new event when a conversation is created in Dixa.", + version: "0.0.1", type: "source", dedupe: "unique", - props: { - dixa: { - type: "app", - app: "dixa", + methods: { + ...common.methods, + getEventType() { + return [ + "ConversationCreated", + ]; }, - http: { - type: "$.interface.http", - customResponse: true, + getSummary({ data }) { + return `New conversation created with Id: ${data.conversation.csid}`; }, - db: "$.service.db", - name: { - propDefinition: [ - "dixa", - "name", - ], - }, - }, - hooks: { - async deploy() { - const conversations = await this.dixa.listConversations(); - const latestConversations = conversations.slice(-50).reverse(); - for (const conversation of latestConversations) { - this.$emit(conversation, { - id: conversation.id, - summary: `New conversation created: ${this.name}`, - ts: new Date(conversation.created_at).getTime(), - }); - } - }, - async activate() { - const endpoint = this.http.endpoint; - const webhookData = { - event: "conversation.created", - callback_url: endpoint, - }; - const response = await this.dixa._makeRequest({ - method: "POST", - path: "/webhooks", - data: webhookData, - }); - const webhookId = response.id; - await this.db.set("webhookId", webhookId); - }, - async deactivate() { - const webhookId = await this.db.get("webhookId"); - if (webhookId) { - await this.dixa._makeRequest({ - method: "DELETE", - path: `/webhooks/${webhookId}`, - }); - await this.db.set("webhookId", null); - } - }, - }, - async run(event) { - const signature = event.headers["X-Dixa-Signature"]; - const secret = this.dixa.$auth.api_token; - const rawBody = JSON.stringify(event.body); - const computedSignature = crypto.createHmac("sha256", secret).update(rawBody) - .digest("hex"); - - if (computedSignature !== signature) { - this.http.respond({ - status: 401, - body: "Unauthorized", - }); - return; - } - - const conversation = event.body; - this.$emit(conversation, { - id: conversation.id, - summary: `New conversation created: ${this.name}`, - ts: new Date(conversation.created_at).getTime(), - }); }, + sampleEmit, }; diff --git a/components/dixa/sources/new-conversation-created-instant/test-event.mjs b/components/dixa/sources/new-conversation-created-instant/test-event.mjs new file mode 100644 index 0000000000000..c5a3311016db8 --- /dev/null +++ b/components/dixa/sources/new-conversation-created-instant/test-event.mjs @@ -0,0 +1,35 @@ +export default { + "event_fqn": "CONVERSATION_CREATED", + "event_id": "12345678-1234-1234-1234-1234567890", + "event_timestamp": "2025-01-14T22:01:05.429Z", + "event_version": "1", + "organization": { + "id": "12345678-1234-1234-1234-1234567890", + "name": "Org Name" + }, + "data": { + "conversation": { + "assignee": null, + "channel": "EMAIL", + "contact_point": "contact@email.dixa.io", + "created_at": "2025-01-13T19:48:33.178Z", + "csid": 2, + "direction": "INBOUND", + "queue": { + "id": "12345678-1234-1234-1234-1234567890", + "name": "default" + }, + "requester": { + "email": "contact@email.com", + "id": "12345678-1234-1234-1234-1234567890", + "name": "Contact Name", + "phone": null, + "roles": [], + "user_type": "Contact" + }, + "status": "OPEN", + "subject": "Subject Text", + "tags": [] + } + } +} \ No newline at end of file diff --git a/components/dixa/sources/new-customer-satisfaction-rating-instant/new-customer-satisfaction-rating-instant.mjs b/components/dixa/sources/new-customer-satisfaction-rating-instant/new-customer-satisfaction-rating-instant.mjs index 0a2ee815b12f3..704dbee0f0134 100644 --- a/components/dixa/sources/new-customer-satisfaction-rating-instant/new-customer-satisfaction-rating-instant.mjs +++ b/components/dixa/sources/new-customer-satisfaction-rating-instant/new-customer-satisfaction-rating-instant.mjs @@ -1,126 +1,24 @@ -import dixa from "../../dixa.app.mjs"; -import crypto from "crypto"; -import { axios } from "@pipedream/platform"; +import common from "../common/base.mjs"; +import sampleEmit from "./test-event.mjs"; export default { + ...common, key: "dixa-new-customer-satisfaction-rating-instant", - name: "New Customer Satisfaction Rating", - description: "Emit new event when a customer submits a satisfaction rating for a conversation. [See the documentation]()", - version: "0.0.{{ts}}", + name: "New Customer Satisfaction Rating (Instant)", + description: "Emit new event when a customer submits a satisfaction rating for a conversation.", + version: "0.0.1", type: "source", dedupe: "unique", - props: { - dixa, - http: { - type: "$.interface.http", - customResponse: true, - }, - db: "$.service.db", - name: { - type: "string", - label: "Name", - description: "Name of the event", - }, - }, methods: { - _getWebhookId() { - return this.db.get("webhookId"); - }, - _setWebhookId(id) { - this.db.set("webhookId", id); - }, - async createWebhook() { - const data = { - name: this.name, - event_type: "conversationrated", - callback_url: this.http.endpoint, - }; - const response = await this.dixa._makeRequest({ - method: "POST", - path: "/webhooks", - data, - }); - return response.id; - }, - async deleteWebhook(id) { - await this.dixa._makeRequest({ - method: "DELETE", - path: `/webhooks/${id}`, - }); - }, - async listRecentRatings() { - const ratings = await this.dixa._makeRequest({ - method: "GET", - path: "/ratings", - params: { - limit: 50, - sort: "created_at_desc", - }, - }); - return ratings; - }, - }, - hooks: { - async deploy() { - const recentRatings = await this.listRecentRatings(); - const sortedRatings = recentRatings.sort((a, b) => new Date(a.created_at) - new Date(b.created_at)); - for (const rating of sortedRatings) { - this.$emit(rating, { - id: rating.id || `${rating.created_at}`, - summary: `New satisfaction rating for conversation ${rating.conversation_id}`, - ts: Date.parse(rating.created_at) || Date.now(), - }); - } - }, - async activate() { - const webhookId = await this._getWebhookId(); - if (!webhookId) { - const id = await this.createWebhook(); - this._setWebhookId(id); - } + ...common.methods, + getEventType() { + return [ + "ConversationRated", + ]; }, - async deactivate() { - const webhookId = await this._getWebhookId(); - if (webhookId) { - await this.deleteWebhook(webhookId); - this._setWebhookId(null); - } + getSummary({ data }) { + return `New satisfaction rating for conversation ${data.conversation.csid}`; }, }, - async run(event) { - const signature = event.headers["x-dixa-signature"]; - const secret = this.dixa.$auth.webhook_secret; - const hash = crypto.createHmac("sha256", secret).update(event.body) - .digest("hex"); - - if (hash !== signature) { - this.http.respond({ - status: 401, - body: "Unauthorized", - }); - return; - } - - const eventData = JSON.parse(event.body); - if (eventData.event !== "conversationrated") { - this.http.respond({ - status: 400, - body: "Invalid event", - }); - return; - } - - const rating = eventData.data; - - this.$emit(rating, { - id: rating.id || `${rating.created_at}`, - summary: `New customer satisfaction rating received for conversation ${rating.conversation_id}`, - ts: Date.parse(rating.created_at) || Date.now(), - }); - - this.http.respond({ - status: 200, - body: "OK", - }); - }, + sampleEmit, }; diff --git a/components/dixa/sources/new-customer-satisfaction-rating-instant/test-event.mjs b/components/dixa/sources/new-customer-satisfaction-rating-instant/test-event.mjs new file mode 100644 index 0000000000000..77287d17ba3c2 --- /dev/null +++ b/components/dixa/sources/new-customer-satisfaction-rating-instant/test-event.mjs @@ -0,0 +1,53 @@ +export default { + "event_fqn": "CONVERSATION_RATED", + "event_id": "12345678-1234-1234-1234-1234567890", + "event_timestamp": "2025-01-14T22:01:05.429Z", + "event_version": "1", + "organization": { + "id": "12345678-1234-1234-1234-1234567890", + "name": "Org Name" + }, + "data": { + "agent": { + "email": "email@dixa.com", + "id": "12345678-1234-1234-1234-1234567890", + "name": "Agent Name", + "phone": "+123456789", + "roles": [], + "user_type": "Member" + }, + "comment": "Best customer service ever!", + "conversation": { + "assignee": { + "email": "email@dixa.com", + "id": "12345678-1234-1234-1234-1234567890", + "name": "Agent Name", + "phone": "+123456789", + "roles": [], + "user_type": "Member" + }, + "channel": "EMAIL", + "contact_point": "contact@email.dixa.io", + "created_at": "2025-01-13T19:48:33.178Z", + "csid": 2, + "direction": "OUTBOUND", + "queue": { + "id": "12345678-1234-1234-1234-1234567890", + "name": "default" + }, + "requester": { + "email": "contact@email.com", + "id": "12345678-1234-1234-1234-1234567890", + "name": "Contact Name", + "phone": null, + "roles": [], + "user_type": "Contact" + }, + "status": "CLOSED", + "subject": "Subject Text", + "tags": [], + "score": 4, + "type": "Csat" + } + } +} \ No newline at end of file diff --git a/components/dixa/sources/new-message-added-instant/new-message-added-instant.mjs b/components/dixa/sources/new-message-added-instant/new-message-added-instant.mjs index c1ce7924a5785..69a43a3bc0143 100644 --- a/components/dixa/sources/new-message-added-instant/new-message-added-instant.mjs +++ b/components/dixa/sources/new-message-added-instant/new-message-added-instant.mjs @@ -1,85 +1,24 @@ -import dixa from "../../dixa.app.mjs"; -import { axios } from "@pipedream/platform"; +import common from "../common/base.mjs"; +import sampleEmit from "./test-event.mjs"; export default { + ...common, key: "dixa-new-message-added-instant", - name: "New Message Added to Conversation", - description: "Emit a new event when a new message is added to a conversation. [See the documentation]()", - version: "0.0.{{ts}}", + name: "New Message Added to Conversation (Instant)", + description: "Emit new event when a new message is added to a conversation.", + version: "0.0.1", type: "source", dedupe: "unique", - props: { - name: { - type: "string", - label: "Name", - description: "Name of the event", + methods: { + ...common.methods, + getEventType() { + return [ + "ConversationMessageAdded", + ]; }, - dixa: { - type: "app", - app: "dixa", + getSummary({ data }) { + return `New message in conversation ${data.conversation.csid}`; }, - db: "$.service.db", - http: { - type: "$.interface.http", - customResponse: false, - }, - }, - hooks: { - async deploy() { - try { - const messages = await this.dixa.paginate(this.dixa.listMessages, { - limit: 50, - }); - for (const message of messages) { - this.$emit(message, { - id: message.id, - summary: `New message in conversation ${message.conversationId}`, - ts: Date.parse(message.createdAt), - }); - } - } catch (error) { - this.$emit(error, "Error deploying component: " + error.message); - } - }, - async activate() { - try { - const webhookUrl = this.http.endpoint; - const webhook = await this.dixa._makeRequest({ - method: "POST", - path: "/webhooks", - data: { - url: webhookUrl, - events: [ - "conversationmessageadded", - ], - }, - }); - await this.db.set("webhookId", webhook.id); - } catch (error) { - this.$emit(error, "Error activating webhook: " + error.message); - } - }, - async deactivate() { - try { - const webhookId = await this.db.get("webhookId"); - if (webhookId) { - await this.dixa._makeRequest({ - method: "DELETE", - path: `/webhooks/${webhookId}`, - }); - await this.db.delete("webhookId"); - } - } catch (error) { - this.$emit(error, "Error deactivating webhook: " + error.message); - } - }, - }, - async run(event) { - const message = event.body; - this.$emit(message, { - id: message.id, - summary: `New message in conversation ${message.conversationId}`, - ts: Date.parse(message.createdAt) || Date.now(), - }); }, + sampleEmit, }; diff --git a/components/dixa/sources/new-message-added-instant/test-event.mjs b/components/dixa/sources/new-message-added-instant/test-event.mjs new file mode 100644 index 0000000000000..18d41dbe56419 --- /dev/null +++ b/components/dixa/sources/new-message-added-instant/test-event.mjs @@ -0,0 +1,62 @@ +export default { + "event_fqn": "CONVERSATION_MESSAGE_ADDED", + "event_id": "12345678-1234-1234-1234-1234567890", + "event_timestamp": "2025-01-14T22:01:05.429Z", + "event_version": "1", + "organization": { + "id": "12345678-1234-1234-1234-1234567890", + "name": "Org Name" + }, + "data": { + "author": { + "email": "email@dixa.com", + "id": "12345678-1234-1234-1234-1234567890", + "name": "Agent Name", + "phone": "+123456789", + "roles": [], + "user_type": "Member" + }, + "channel": "EMAIL", + "content": { + "text":"Message Text", + "content_type":"Text", + "original_content_url":null, + "processed_content_url":null + }, + "conversation": { + "assignee": { + "email": "email@dixa.com", + "id": "12345678-1234-1234-1234-1234567890", + "name": "Agent Name", + "phone": "+123456789", + "roles": [], + "user_type": "Member" + }, + "channel": "EMAIL", + "contact_point": "contact@email.dixa.io", + "created_at": "2025-01-13T19:48:33.178Z", + "csid": 2, + "direction": "OUTBOUND", + "queue": { + "id": "12345678-1234-1234-1234-1234567890", + "name": "default" + }, + "requester": { + "email": "contact@email.com", + "id": "12345678-1234-1234-1234-1234567890", + "name": "Contact Name", + "phone": null, + "roles": [], + "user_type": "Contact" + }, + "status": "PENDING", + "subject": "Subject Text", + "tags": [] + }, + "created_at":"2025-01-14T23:08:43.187Z", + "direction":"outbound", + "external_id":"null", + "message_id":"12345678-1234-1234-1234-1234567890", + "text":"Message Text", + } +} \ No newline at end of file diff --git a/components/dixa/sources/new-tag-added-instant/new-tag-added-instant.mjs b/components/dixa/sources/new-tag-added-instant/new-tag-added-instant.mjs index b0bb6108863ca..98e42e3f18a91 100644 --- a/components/dixa/sources/new-tag-added-instant/new-tag-added-instant.mjs +++ b/components/dixa/sources/new-tag-added-instant/new-tag-added-instant.mjs @@ -1,110 +1,24 @@ -import dixa from "../../dixa.app.mjs"; -import crypto from "crypto"; -import { axios } from "@pipedream/platform"; +import common from "../common/base.mjs"; +import sampleEmit from "./test-event.mjs"; export default { + ...common, key: "dixa-new-tag-added-instant", - name: "New Tag Added in Conversation", - description: "Emit new event when a tag is added to a conversation. [See the documentation]()", - version: "0.0.{{ts}}", + name: "New Tag Added in Conversation (Instant)", + description: "Emit new event when a tag is added to a conversation.", + version: "0.0.1", type: "source", dedupe: "unique", - props: { - dixa: { - type: "app", - app: "dixa", - }, - db: "$.service.db", - http: { - type: "$.interface.http", - customResponse: true, - }, - name: { - propDefinition: [ - dixa, - "name", - ], - }, - }, methods: { - async createWebhook() { - const webhookData = { - event: "conversationtagadded", - callback_url: this.http.endpoint, - name: this.name, - }; - const response = await this.dixa._makeRequest({ - method: "POST", - path: "/webhooks", - data: webhookData, - }); - return response.id; - }, - async deleteWebhook(webhookId) { - await this.dixa._makeRequest({ - method: "DELETE", - path: `/webhooks/${webhookId}`, - }); - }, - async fetchRecentTags() { - const events = await this.dixa.paginate(this.dixa.listEvents, { - eventType: "conversationtagadded", - limit: 50, - }); - return events; - }, - }, - hooks: { - async deploy() { - const recentTags = await this.fetchRecentTags(); - recentTags.forEach((tag) => { - this.$emit(tag, { - id: tag.id || tag.timestamp, - summary: `Tag "${tag.tagName}" added to conversation ${tag.conversationId}`, - ts: tag.timestamp - ? Date.parse(tag.timestamp) - : Date.now(), - }); - }); - }, - async activate() { - const webhookId = await this.createWebhook(); - await this.db.set("webhookId", webhookId); + ...common.methods, + getEventType() { + return [ + "ConversationTagAdded", + ]; }, - async deactivate() { - const webhookId = await this.db.get("webhookId"); - if (webhookId) { - await this.deleteWebhook(webhookId); - } + getSummary({ data }) { + return `Tag "${data.tag}" added to conversation ${data.conversation.csid}`; }, }, - async run(event) { - const signature = event.headers["X-Dixa-Signature"] || event.headers["x-dixa-signature"]; - const secret = this.dixa.$auth.webhook_secret; - const computedSignature = crypto.createHmac("sha256", secret).update(event.rawBody) - .digest("hex"); - - if (computedSignature !== signature) { - await this.http.respond({ - status: 401, - body: "Unauthorized", - }); - return; - } - - const tagEvent = event.body; - - this.$emit(tagEvent, { - id: tagEvent.id || tagEvent.timestamp, - summary: `Tag "${tagEvent.tagName}" added to conversation ${tagEvent.conversationId}`, - ts: tagEvent.timestamp - ? Date.parse(tagEvent.timestamp) - : Date.now(), - }); - - await this.http.respond({ - status: 200, - body: "OK", - }); - }, + sampleEmit, }; diff --git a/components/dixa/sources/new-tag-added-instant/test-event.mjs b/components/dixa/sources/new-tag-added-instant/test-event.mjs new file mode 100644 index 0000000000000..1bc7dac26796f --- /dev/null +++ b/components/dixa/sources/new-tag-added-instant/test-event.mjs @@ -0,0 +1,57 @@ +export default { + "event_fqn": "CONVERSATION_TAG_ADDED", + "event_id": "12345678-1234-1234-1234-1234567890", + "event_timestamp": "2025-01-14T22:01:05.429Z", + "event_version": "1", + "organization": { + "id": "12345678-1234-1234-1234-1234567890", + "name": "Org Name" + }, + "data": { + "author": { + "email": "email@dixa.com", + "id": "12345678-1234-1234-1234-1234567890", + "name": "Agent Name", + "phone": "+123456789", + "roles": [], + "user_type": "Member" + }, + "tag": "Tag Name", + "conversation": { + "assignee": { + "email": "email@dixa.com", + "id": "12345678-1234-1234-1234-1234567890", + "name": "Agent Name", + "phone": "+123456789", + "roles": [], + "user_type": "Member" + }, + "channel": "EMAIL", + "contact_point": "contact@email.dixa.io", + "created_at": "2025-01-13T19:48:33.178Z", + "csid": 2, + "direction": "OUTBOUND", + "queue": { + "id": "12345678-1234-1234-1234-1234567890", + "name": "default" + }, + "requester": { + "email": "contact@email.com", + "id": "12345678-1234-1234-1234-1234567890", + "name": "Contact Name", + "phone": null, + "roles": [], + "user_type": "Contact" + }, + "status": "PENDING", + "subject": "Subject Text", + "tags": [ + { + "id":"12345678-1234-1234-1234-1234567890", + "name":"Tag Name", + "is_deactivated":false + } + ] + } + } +} \ No newline at end of file From 2622f7b00876b39a22ee7825fdf9d0ca0da042be Mon Sep 17 00:00:00 2001 From: Luan Cazarine Date: Tue, 14 Jan 2025 20:25:53 -0300 Subject: [PATCH 3/7] pnpm update --- pnpm-lock.yaml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5be7b5b9df1bd..050f62febfee8 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -2796,7 +2796,11 @@ importers: components/dispatch: {} - components/dixa: {} + components/dixa: + dependencies: + '@pipedream/platform': + specifier: ^3.0.3 + version: 3.0.3 components/dnsfilter: dependencies: From 9fccf179212b7d266d1536cd2fb9cb73a9b473f9 Mon Sep 17 00:00:00 2001 From: Luan Cazarine Date: Wed, 15 Jan 2025 16:43:51 -0300 Subject: [PATCH 4/7] some adjusts --- .../dixa/actions/add-message/add-message.mjs | 2 +- .../create-conversation.mjs | 2 +- .../set-custom-contact-attributes.mjs | 2 +- components/dixa/common/utils.mjs | 24 ------------------- components/dixa/sources/common/base.mjs | 2 +- .../conversation-status-changed-instant.mjs | 2 +- .../new-conversation-created-instant.mjs | 2 +- ...w-customer-satisfaction-rating-instant.mjs | 2 +- .../new-message-added-instant.mjs | 2 +- .../new-tag-added-instant.mjs | 2 +- 10 files changed, 9 insertions(+), 33 deletions(-) delete mode 100644 components/dixa/common/utils.mjs diff --git a/components/dixa/actions/add-message/add-message.mjs b/components/dixa/actions/add-message/add-message.mjs index ee28d0e5f1fdd..d2dd669334040 100644 --- a/components/dixa/actions/add-message/add-message.mjs +++ b/components/dixa/actions/add-message/add-message.mjs @@ -43,7 +43,7 @@ export default { }, }, async additionalProps(props) { - props.agentId.hidden = !this.direction === "Outbound"; + props.agentId.hidden = !(this.direction === "Outbound"); return {}; }, async run({ $ }) { diff --git a/components/dixa/actions/create-conversation/create-conversation.mjs b/components/dixa/actions/create-conversation/create-conversation.mjs index fd516cb4a12c9..6afa9624db276 100644 --- a/components/dixa/actions/create-conversation/create-conversation.mjs +++ b/components/dixa/actions/create-conversation/create-conversation.mjs @@ -64,7 +64,7 @@ export default { }, }, async additionalProps(props) { - props.agentId.hidden = !this.direction === "Outbound"; + props.agentId.hidden = !(this.direction === "Outbound"); props.channel.options = this.direction === "Outbound" ? [ "Email", diff --git a/components/dixa/actions/set-custom-contact-attributes/set-custom-contact-attributes.mjs b/components/dixa/actions/set-custom-contact-attributes/set-custom-contact-attributes.mjs index 215126aba9506..d4c39c1657915 100644 --- a/components/dixa/actions/set-custom-contact-attributes/set-custom-contact-attributes.mjs +++ b/components/dixa/actions/set-custom-contact-attributes/set-custom-contact-attributes.mjs @@ -3,7 +3,7 @@ import dixa from "../../dixa.app.mjs"; export default { key: "dixa-set-custom-contact-attributes", name: "Set Custom Contact Attributes", - description: "Updates custom attributes for a specified user. [See the documentation]()", + description: "Updates custom attributes for a specified user. [See the documentation](https://docs.dixa.io/openapi/dixa-api/v1/tag/Custom-Attributes/#tag/Custom-Attributes/operation/patchEndusersUseridCustom-attributes)", version: "0.0.1", type: "action", props: { diff --git a/components/dixa/common/utils.mjs b/components/dixa/common/utils.mjs deleted file mode 100644 index dcc9cc61f6f41..0000000000000 --- a/components/dixa/common/utils.mjs +++ /dev/null @@ -1,24 +0,0 @@ -export const parseObject = (obj) => { - if (!obj) return undefined; - - if (Array.isArray(obj)) { - return obj.map((item) => { - if (typeof item === "string") { - try { - return JSON.parse(item); - } catch (e) { - return item; - } - } - return item; - }); - } - if (typeof obj === "string") { - try { - return JSON.parse(obj); - } catch (e) { - return obj; - } - } - return obj; -}; diff --git a/components/dixa/sources/common/base.mjs b/components/dixa/sources/common/base.mjs index 5c9784d277e39..bbe2c47f7c7f5 100644 --- a/components/dixa/sources/common/base.mjs +++ b/components/dixa/sources/common/base.mjs @@ -39,7 +39,7 @@ export default { }, }, async run({ body }) { - const ts = Date.parse(new Date()); + const ts = Date.now(); this.$emit(body, { id: body.event_id, summary: this.getSummary(body), diff --git a/components/dixa/sources/conversation-status-changed-instant/conversation-status-changed-instant.mjs b/components/dixa/sources/conversation-status-changed-instant/conversation-status-changed-instant.mjs index 91d39915ae677..9d4fc452f49d6 100644 --- a/components/dixa/sources/conversation-status-changed-instant/conversation-status-changed-instant.mjs +++ b/components/dixa/sources/conversation-status-changed-instant/conversation-status-changed-instant.mjs @@ -5,7 +5,7 @@ export default { ...common, key: "dixa-conversation-status-changed-instant", name: "New Conversation Status Changed (Instant)", - description: "Emit new events when the status of a conversation changes (e.g., open, closed, or abandoned).", + description: "Emit new events when the status of a conversation changes (e.g., open, closed, or abandoned). [See the documentation](https://docs.dixa.io/openapi/dixa-api/v1/tag/Webhooks/).", version: "0.0.1", type: "source", dedupe: "unique", diff --git a/components/dixa/sources/new-conversation-created-instant/new-conversation-created-instant.mjs b/components/dixa/sources/new-conversation-created-instant/new-conversation-created-instant.mjs index 5621505271fd5..fa6e3bd3da5c2 100644 --- a/components/dixa/sources/new-conversation-created-instant/new-conversation-created-instant.mjs +++ b/components/dixa/sources/new-conversation-created-instant/new-conversation-created-instant.mjs @@ -5,7 +5,7 @@ export default { ...common, key: "dixa-new-conversation-created-instant", name: "New Conversation Created (Instant)", - description: "Emit new event when a conversation is created in Dixa.", + description: "Emit new event when a conversation is created in Dixa. [See the documentation](https://docs.dixa.io/openapi/dixa-api/v1/tag/Webhooks/).", version: "0.0.1", type: "source", dedupe: "unique", diff --git a/components/dixa/sources/new-customer-satisfaction-rating-instant/new-customer-satisfaction-rating-instant.mjs b/components/dixa/sources/new-customer-satisfaction-rating-instant/new-customer-satisfaction-rating-instant.mjs index 704dbee0f0134..8cc259dbc8f03 100644 --- a/components/dixa/sources/new-customer-satisfaction-rating-instant/new-customer-satisfaction-rating-instant.mjs +++ b/components/dixa/sources/new-customer-satisfaction-rating-instant/new-customer-satisfaction-rating-instant.mjs @@ -5,7 +5,7 @@ export default { ...common, key: "dixa-new-customer-satisfaction-rating-instant", name: "New Customer Satisfaction Rating (Instant)", - description: "Emit new event when a customer submits a satisfaction rating for a conversation.", + description: "Emit new event when a customer submits a satisfaction rating for a conversation. [See the documentation](https://docs.dixa.io/openapi/dixa-api/v1/tag/Webhooks/).", version: "0.0.1", type: "source", dedupe: "unique", diff --git a/components/dixa/sources/new-message-added-instant/new-message-added-instant.mjs b/components/dixa/sources/new-message-added-instant/new-message-added-instant.mjs index 69a43a3bc0143..f499673b4ab3e 100644 --- a/components/dixa/sources/new-message-added-instant/new-message-added-instant.mjs +++ b/components/dixa/sources/new-message-added-instant/new-message-added-instant.mjs @@ -5,7 +5,7 @@ export default { ...common, key: "dixa-new-message-added-instant", name: "New Message Added to Conversation (Instant)", - description: "Emit new event when a new message is added to a conversation.", + description: "Emit new event when a new message is added to a conversation. [See the documentation](https://docs.dixa.io/openapi/dixa-api/v1/tag/Webhooks/).", version: "0.0.1", type: "source", dedupe: "unique", diff --git a/components/dixa/sources/new-tag-added-instant/new-tag-added-instant.mjs b/components/dixa/sources/new-tag-added-instant/new-tag-added-instant.mjs index 98e42e3f18a91..4b2c6109c4ef9 100644 --- a/components/dixa/sources/new-tag-added-instant/new-tag-added-instant.mjs +++ b/components/dixa/sources/new-tag-added-instant/new-tag-added-instant.mjs @@ -5,7 +5,7 @@ export default { ...common, key: "dixa-new-tag-added-instant", name: "New Tag Added in Conversation (Instant)", - description: "Emit new event when a tag is added to a conversation.", + description: "Emit new event when a tag is added to a conversation. [See the documentation](https://docs.dixa.io/openapi/dixa-api/v1/tag/Webhooks/).", version: "0.0.1", type: "source", dedupe: "unique", From a7267263db3527d0dba0b960d1ec4a776e03d81a Mon Sep 17 00:00:00 2001 From: Luan Cazarine Date: Thu, 16 Jan 2025 12:32:25 -0300 Subject: [PATCH 5/7] Update components/dixa/actions/add-message/add-message.mjs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Guilherme Falcão <48412907+GTFalcao@users.noreply.github.com> --- components/dixa/actions/add-message/add-message.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/dixa/actions/add-message/add-message.mjs b/components/dixa/actions/add-message/add-message.mjs index d2dd669334040..cbfa76b7fe053 100644 --- a/components/dixa/actions/add-message/add-message.mjs +++ b/components/dixa/actions/add-message/add-message.mjs @@ -43,7 +43,7 @@ export default { }, }, async additionalProps(props) { - props.agentId.hidden = !(this.direction === "Outbound"); + props.agentId.hidden = this.direction !== "Outbound"; return {}; }, async run({ $ }) { From d321a806a5ce1d9818d4ccb3e50515a3149e5e46 Mon Sep 17 00:00:00 2001 From: Luan Cazarine Date: Thu, 16 Jan 2025 12:37:51 -0300 Subject: [PATCH 6/7] some adjusts --- components/dixa/actions/add-message/add-message.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/dixa/actions/add-message/add-message.mjs b/components/dixa/actions/add-message/add-message.mjs index cbfa76b7fe053..f912276421e47 100644 --- a/components/dixa/actions/add-message/add-message.mjs +++ b/components/dixa/actions/add-message/add-message.mjs @@ -52,7 +52,7 @@ export default { conversationId: this.conversationId, data: { content: { - agentId: this.direction === "Outbound" + agentId: this.direction !== "Outbound" ? this.agentId : undefined, value: this.content, From fac7e4920fcfbc3b0885e35973460900b5ebee55 Mon Sep 17 00:00:00 2001 From: Luan Cazarine Date: Fri, 17 Jan 2025 11:39:28 -0300 Subject: [PATCH 7/7] fix add-message data --- components/dixa/actions/add-message/add-message.mjs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/components/dixa/actions/add-message/add-message.mjs b/components/dixa/actions/add-message/add-message.mjs index f912276421e47..8e9daaff33cd3 100644 --- a/components/dixa/actions/add-message/add-message.mjs +++ b/components/dixa/actions/add-message/add-message.mjs @@ -51,10 +51,10 @@ export default { $, conversationId: this.conversationId, data: { + agentId: this.direction === "Outbound" + ? this.agentId + : undefined, content: { - agentId: this.direction !== "Outbound" - ? this.agentId - : undefined, value: this.content, _type: "Text", },