diff --git a/components/message_bird/actions/create-contact/create-contact.mjs b/components/message_bird/actions/create-contact/create-contact.mjs new file mode 100644 index 0000000000000..99c5fb190099d --- /dev/null +++ b/components/message_bird/actions/create-contact/create-contact.mjs @@ -0,0 +1,41 @@ +import messagebird from "../../message_bird.app.mjs"; + +export default { + key: "message_bird-create-contact", + name: "Create Contact", + description: "Creates a new contact. [See the documentation](https://developers.messagebird.com/api/contacts/#create-a-contact)", + version: "0.0.1", + type: "action", + props: { + messagebird, + phone: { + type: "string", + label: "Phone", + description: "The phone number of the contact. Example: `31612345678`", + }, + firstName: { + type: "string", + label: "First Name", + description: "First name of the contact", + optional: true, + }, + lastName: { + type: "string", + label: "Last Name", + description: "Last name of the contact", + optional: true, + }, + }, + async run({ $ }) { + const response = await this.messagebird.createContact({ + $, + data: { + msisdn: this.phone, + firstName: this.firstName, + lastName: this.lastName, + }, + }); + $.export("$summary", `Successfully created contact with ID ${response.id}`); + return response; + }, +}; diff --git a/components/message_bird/actions/send-sms/send-sms.mjs b/components/message_bird/actions/send-sms/send-sms.mjs new file mode 100644 index 0000000000000..72cd578d3268e --- /dev/null +++ b/components/message_bird/actions/send-sms/send-sms.mjs @@ -0,0 +1,45 @@ +import messagebird from "../../message_bird.app.mjs"; + +export default { + key: "message_bird-send-sms", + name: "Send SMS", + description: "Sends an SMS message. [See the documentation](https://developers.messagebird.com/api/sms-messaging/#send-outbound-sms)", + version: "0.0.1", + type: "action", + props: { + messagebird, + originator: { + propDefinition: [ + messagebird, + "originator", + ], + }, + body: { + propDefinition: [ + messagebird, + "body", + ], + }, + recipients: { + propDefinition: [ + messagebird, + "recipients", + ], + }, + }, + async run({ $ }) { + const response = await this.messagebird.sendSMS({ + $, + data: { + originator: this.originator, + body: this.body, + recipients: typeof this.recipients === "string" + ? JSON.parse(this.recipients) + : this.recipients, + type: "sms", + }, + }); + $.export("$summary", `Successfully sent SMS with ID ${response.id}`); + return response; + }, +}; diff --git a/components/message_bird/actions/send-voice-message/send-voice-message.mjs b/components/message_bird/actions/send-voice-message/send-voice-message.mjs new file mode 100644 index 0000000000000..e43c9ab314baf --- /dev/null +++ b/components/message_bird/actions/send-voice-message/send-voice-message.mjs @@ -0,0 +1,45 @@ +import messagebird from "../../message_bird.app.mjs"; + +export default { + key: "message_bird-send-voice-message", + name: "Send Voice Message", + description: "Sends a voice message. [See the documentation](https://developers.messagebird.com/api/voice-messaging/#send-a-voice-message)", + version: "0.0.1", + type: "action", + props: { + messagebird, + body: { + propDefinition: [ + messagebird, + "body", + ], + }, + recipients: { + propDefinition: [ + messagebird, + "recipients", + ], + }, + originator: { + propDefinition: [ + messagebird, + "originator", + ], + optional: true, + }, + }, + async run({ $ }) { + const response = await this.messagebird.sendVoiceMessage({ + $, + data: { + body: this.body, + recipients: typeof this.recipients === "string" + ? JSON.parse(this.recipients) + : this.recipients, + originator: this.originator, + }, + }); + $.export("$summary", `Successfully sent voice message with ID ${response.id}`); + return response; + }, +}; diff --git a/components/message_bird/message_bird.app.mjs b/components/message_bird/message_bird.app.mjs index 16d8c68358749..10b2d1036cad2 100644 --- a/components/message_bird/message_bird.app.mjs +++ b/components/message_bird/message_bird.app.mjs @@ -1,11 +1,68 @@ +import { axios } from "@pipedream/platform"; + export default { type: "app", app: "message_bird", - propDefinitions: {}, + propDefinitions: { + originator: { + type: "string", + label: "Originator", + description: "The sender of the message. This can be a telephone number (including country code) or an alphanumeric string. In case of an alphanumeric string, the maximum length is 11 characters.", + }, + body: { + type: "string", + label: "Body", + description: "The body of the message", + }, + recipients: { + type: "string[]", + label: "Recipients", + description: "An array of recipients msisdns (phone numbers)", + }, + }, methods: { - // this.$auth contains connected account data - authKeys() { - console.log(Object.keys(this.$auth)); + _baseUrl() { + return "https://rest.messagebird.com"; + }, + _makeRequest({ + $ = this, + path, + ...opts + }) { + return axios($, { + url: `${this._baseUrl()}${path}`, + headers: { + "Authorization": `AccessKey ${this.$auth.access_key}`, + }, + ...opts, + }); + }, + listSMSMessages(opts = {}) { + return this._makeRequest({ + path: "/messages", + ...opts, + }); + }, + createContact(opts = {}) { + return this._makeRequest({ + method: "POST", + path: "/contacts", + ...opts, + }); + }, + sendSMS(opts = {}) { + return this._makeRequest({ + method: "POST", + path: "/messages", + ...opts, + }); + }, + sendVoiceMessage(opts = {}) { + return this._makeRequest({ + method: "POST", + path: "/voicemessages", + ...opts, + }); }, }, }; diff --git a/components/message_bird/package.json b/components/message_bird/package.json new file mode 100644 index 0000000000000..65d5b369395af --- /dev/null +++ b/components/message_bird/package.json @@ -0,0 +1,18 @@ +{ + "name": "@pipedream/message_bird", + "version": "0.0.1", + "description": "Pipedream MessageBird Components", + "main": "message_bird.app.mjs", + "keywords": [ + "pipedream", + "message_bird" + ], + "homepage": "https://pipedream.com/apps/message_bird", + "author": "Pipedream (https://pipedream.com/)", + "publishConfig": { + "access": "public" + }, + "dependencies": { + "@pipedream/platform": "^3.0.3" + } +} diff --git a/components/message_bird/sources/new-sms-message-received/new-sms-message-received.mjs b/components/message_bird/sources/new-sms-message-received/new-sms-message-received.mjs new file mode 100644 index 0000000000000..24c4ced16e7c7 --- /dev/null +++ b/components/message_bird/sources/new-sms-message-received/new-sms-message-received.mjs @@ -0,0 +1,62 @@ +import messagebird from "../../message_bird.app.mjs"; +import { DEFAULT_POLLING_SOURCE_TIMER_INTERVAL } from "@pipedream/platform"; + +export default { + key: "message_bird-new-sms-message-received", + name: "New SMS Message Received", + description: "Emit new event when a new SMS message is received. [See the documentation](https://developers.messagebird.com/api/sms-messaging/#list-messages)", + version: "0.0.1", + dedupe: "unique", + type: "source", + props: { + messagebird, + db: "$.service.db", + timer: { + type: "$.interface.timer", + default: { + intervalSeconds: DEFAULT_POLLING_SOURCE_TIMER_INTERVAL, + }, + }, + }, + methods: { + _getLastTs() { + return this.db.get("lastTs"); + }, + _setLastTs(lastTs) { + this.db.set("lastTs", lastTs); + }, + generateMeta(message) { + return { + id: message.id, + summary: `New Message ID: ${message.id}`, + ts: Date.parse(message.createdDatetime), + }; + }, + }, + async run() { + let lastTs = this._getLastTs(); + + const { items } = await this.messagebird.listSMSMessages({ + params: { + direction: "mo", // mt = sent, mo = received + type: "sms", + from: lastTs, + }, + }); + + if (!items?.length) { + return; + } + + for (const message of items) { + const meta = this.generateMeta(message); + this.$emit(message, meta); + + if (!lastTs || Date.parse(message.createdDatetime) > Date.parse(lastTs)) { + lastTs = message.createdDatetime; + } + } + + this._setLastTs(lastTs); + }, +}; diff --git a/components/messagebird/.gitignore b/components/messagebird/.gitignore deleted file mode 100644 index ec761ccab7595..0000000000000 --- a/components/messagebird/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -*.js -*.mjs -dist \ No newline at end of file diff --git a/components/messagebird/actions/create-contact/create-contact.mjs b/components/messagebird/actions/create-contact/create-contact.mjs new file mode 100644 index 0000000000000..603a503d22f73 --- /dev/null +++ b/components/messagebird/actions/create-contact/create-contact.mjs @@ -0,0 +1,56 @@ +import messagebird from "../../messagebird.app.mjs"; + +export default { + key: "messagebird-create-contact", + name: "Create Contact", + description: "Creates a new contact. [See the documentation](https://docs.bird.com/api/contacts-api/api-reference/manage-workspace-contacts/create-a-contact)", + version: "0.0.1", + type: "action", + props: { + messagebird, + workspaceId: { + propDefinition: [ + messagebird, + "workspaceId", + ], + }, + displayName: { + type: "string", + label: "Display Name", + description: "The display name for the contact", + }, + email: { + type: "string", + label: "Email", + description: "The email address of the contact", + optional: true, + }, + listIds: { + propDefinition: [ + messagebird, + "listIds", + (c) => ({ + workspaceId: c.workspaceId, + }), + ], + }, + }, + async run({ $ }) { + const response = await this.messagebird.createContact({ + $, + workspaceId: this.workspaceId, + data: { + displayName: this.displayName, + identifiers: this.email && [ + { + key: "emailaddress", + value: this.email, + }, + ], + listIds: this.listIds, + }, + }); + $.export("$summary", `Successfully created contact with ID: ${response.id}`); + return response; + }, +}; diff --git a/components/messagebird/actions/send-sms/send-sms.mjs b/components/messagebird/actions/send-sms/send-sms.mjs new file mode 100644 index 0000000000000..87c83a0c81adc --- /dev/null +++ b/components/messagebird/actions/send-sms/send-sms.mjs @@ -0,0 +1,65 @@ +import messagebird from "../../messagebird.app.mjs"; + +export default { + key: "messagebird-send-sms", + name: "Send SMS", + description: "Sends an SMS message. [See the documentation](https://docs.bird.com/api/channels-api/supported-channels/programmable-sms/sending-sms-messages)", + version: "0.0.1", + type: "action", + props: { + messagebird, + workspaceId: { + propDefinition: [ + messagebird, + "workspaceId", + ], + }, + channelId: { + propDefinition: [ + messagebird, + "channelId", + (c) => ({ + workspaceId: c.workspaceId, + }), + ], + }, + contactId: { + propDefinition: [ + messagebird, + "contactId", + (c) => ({ + workspaceId: c.workspaceId, + }), + ], + }, + message: { + type: "string", + label: "Message", + description: "The message text to send", + }, + }, + async run({ $ }) { + const response = await this.messagebird.sendSmsMessage({ + $, + workspaceId: this.workspaceId, + channelId: this.channelId, + data: { + receiver: { + contacts: [ + { + id: this.contactId, + }, + ], + }, + body: { + type: "text", + text: { + text: this.message, + }, + }, + }, + }); + $.export("$summary", `Successfully sent SMS message with ID: ${response.id}`); + return response; + }, +}; diff --git a/components/messagebird/actions/send-voice-message/send-voice-message.mjs b/components/messagebird/actions/send-voice-message/send-voice-message.mjs new file mode 100644 index 0000000000000..d01ee564985a9 --- /dev/null +++ b/components/messagebird/actions/send-voice-message/send-voice-message.mjs @@ -0,0 +1,60 @@ +import messagebird from "../../messagebird.app.mjs"; + +export default { + key: "messagebird-send-voice-message", + name: "Send Voice Message", + description: "Send a voice message to any phone number globally. [See the documentation](https://docs.bird.com/api/voice-api/voice-calls-api/initiate-an-outbound-call)", + version: "0.0.1", + type: "action", + props: { + messagebird, + workspaceId: { + propDefinition: [ + messagebird, + "workspaceId", + ], + }, + channelId: { + propDefinition: [ + messagebird, + "channelId", + (c) => ({ + workspaceId: c.workspaceId, + }), + ], + }, + recipientNumber: { + type: "string", + label: "Recipient Number", + description: "The phone number to send the message to. Example: `+351000000000`", + }, + message: { + type: "string", + label: "Message", + description: "The message to send as a voice message", + }, + }, + async run({ $ }) { + const response = await this.messagebird.sendVoiceMessage({ + $, + workspaceId: this.workspaceId, + channelId: this.channelId, + data: { + to: this.recipientNumber, + callFlow: [ + { + options: { + text: this.message, + }, + command: "say", + }, + { + command: "hangup", + }, + ], + }, + }); + $.export("$summary", `Successfully sent voice message with ID: ${response.id}`); + return response; + }, +}; diff --git a/components/messagebird/app/messagebird.app.ts b/components/messagebird/app/messagebird.app.ts deleted file mode 100644 index 5d564cb3bc9a5..0000000000000 --- a/components/messagebird/app/messagebird.app.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { defineApp } from "@pipedream/types"; - -export default defineApp({ - type: "app", - app: "messagebird", - propDefinitions: {}, - methods: { - // this.$auth contains connected account data - authKeys() { - console.log(Object.keys(this.$auth)); - }, - }, -}); \ No newline at end of file diff --git a/components/messagebird/messagebird.app.mjs b/components/messagebird/messagebird.app.mjs new file mode 100644 index 0000000000000..6b4f3e23c7039 --- /dev/null +++ b/components/messagebird/messagebird.app.mjs @@ -0,0 +1,221 @@ +import { axios } from "@pipedream/platform"; + +export default { + type: "app", + app: "messagebird", + propDefinitions: { + workspaceId: { + type: "string", + label: "Workspace ID", + description: "The unique identifier of a workspace", + async options({ prevContext }) { + const { next: pageToken } = prevContext; + const { + results, nextPageToken, + } = await this.listWorkspaces({ + organizationId: this._organizationId(), + params: pageToken + ? { + pageToken, + } + : {}, + }); + return { + options: results?.map(({ + id: value, name: label, + }) => ({ + value, + label, + })) || [], + context: { + next: nextPageToken, + }, + }; + }, + }, + channelId: { + type: "string", + label: "Channel ID", + description: "The unique identifier of a channel", + async options({ + workspaceId, prevContext, + }) { + if (!workspaceId) { + return []; + } + const { next: pageToken } = prevContext; + const { + results, nextPageToken, + } = await this.listChannels({ + workspaceId, + params: pageToken + ? { + pageToken, + } + : {}, + }); + return { + options: results?.map(({ + id: value, name: label, + }) => ({ + value, + label, + })) || [], + context: { + next: nextPageToken, + }, + }; + }, + }, + contactId: { + type: "string", + label: "Contact ID", + description: "The contact to send the message to", + async options({ + workspaceId, prevContext, + }) { + if (!workspaceId) { + return []; + } + const { next: pageToken } = prevContext; + const { + results, nextPageToken, + } = await this.listContacts({ + workspaceId, + params: pageToken + ? { + pageToken, + } + : {}, + }); + return { + options: results?.map(({ + id: value, computedDisplayName: label, + }) => ({ + value, + label, + })) || [], + context: { + next: nextPageToken, + }, + }; + }, + }, + listIds: { + type: "string[]", + label: "List IDs", + description: "An array of unique list identifiers to add the contact to", + optional: true, + async options({ workspaceId }) { + if (!workspaceId) { + return []; + } + const { results } = await this.listLists({ + workspaceId, + }); + return results?.map(({ + id: value, name: label, + }) => ({ + value, + label, + })) || []; + }, + }, + }, + methods: { + _baseUrl() { + return "https://api.bird.com"; + }, + _organizationId() { + return this.$auth.organization_id; + }, + _makeRequest({ + $ = this, + path, + ...otherOpts + }) { + return axios($, { + url: `${this._baseUrl()}${path}`, + headers: { + "Authorization": `AccessKey ${this.$auth.api_key}`, + }, + ...otherOpts, + }); + }, + createWebhook({ + workspaceId, ...opts + }) { + return this._makeRequest({ + method: "POST", + path: `/organizations/${this._organizationId()}/workspaces/${workspaceId}/webhook-subscriptions`, + ...opts, + }); + }, + deleteWebhook({ + workspaceId, hookId, ...opts + }) { + return this._makeRequest({ + method: "DELETE", + path: `/organizations/${this._organizationId()}/workspaces/${workspaceId}/webhook-subscriptions/${hookId}`, + ...opts, + }); + }, + listWorkspaces(opts = {}) { + return this._makeRequest({ + path: `/organizations/${this._organizationId()}/workspaces`, + ...opts, + }); + }, + listChannels({ + workspaceId, ...opts + }) { + return this._makeRequest({ + path: `/workspaces/${workspaceId}/channels`, + ...opts, + }); + }, + listLists({ + workspaceId, ...opts + }) { + return this._makeRequest({ + path: `/workspaces/${workspaceId}/lists`, + ...opts, + }); + }, + listContacts({ + workspaceId, ...opts + }) { + return this._makeRequest({ + path: `/workspaces/${workspaceId}/contacts`, + ...opts, + }); + }, + createContact({ + workspaceId, ...opts + }) { + return this._makeRequest({ + method: "POST", + path: `/workspaces/${workspaceId}/contacts`, + ...opts, + }); + }, + sendVoiceMessage({ + workspaceId, channelId, ...opts + }) { + return this._makeRequest({ + method: "POST", + path: `/workspaces/${workspaceId}/channels/${channelId}/calls`, + ...opts, + }); + }, + sendSmsMessage({ + workspaceId, channelId, ...opts + }) { + return this._makeRequest({ + method: "POST", + path: `/workspaces/${workspaceId}/channels/${channelId}/messages`, + ...opts, + }); + }, + }, +}; diff --git a/components/messagebird/package.json b/components/messagebird/package.json index 0d01bddd480fb..2c188834d5cd3 100644 --- a/components/messagebird/package.json +++ b/components/messagebird/package.json @@ -1,16 +1,18 @@ { "name": "@pipedream/messagebird", - "version": "0.0.2", + "version": "0.1.0", "description": "Pipedream MessageBird Components", - "main": "dist/app/messagebird.app.mjs", + "main": "messagebird.app.mjs", "keywords": [ "pipedream", "messagebird" ], - "files": ["dist"], "homepage": "https://pipedream.com/apps/messagebird", "author": "Pipedream (https://pipedream.com/)", "publishConfig": { "access": "public" + }, + "dependencies": { + "@pipedream/platform": "^3.0.3" } } diff --git a/components/messagebird/sources/new-message-received/new-message-received.mjs b/components/messagebird/sources/new-message-received/new-message-received.mjs new file mode 100644 index 0000000000000..bed0f49addaa7 --- /dev/null +++ b/components/messagebird/sources/new-message-received/new-message-received.mjs @@ -0,0 +1,85 @@ +import messagebird from "../../messagebird.app.mjs"; +import sampleEmit from "./test-event.mjs"; + +export default { + key: "messagebird-new-message-received", + name: "New Message Received (Instant)", + description: "Emit new event for each new message received. [See the documentation](https://docs.bird.com/api/notifications-api/api-reference/webhook-subscriptions/create-a-webhook-subscription)", + version: "0.0.1", + dedupe: "unique", + type: "source", + props: { + messagebird, + http: "$.interface.http", + db: "$.service.db", + workspaceId: { + propDefinition: [ + messagebird, + "workspaceId", + ], + }, + platform: { + type: "string", + label: "Platform", + description: "The type of inbound message to watch for", + options: [ + "sms", + "whatsapp", + "email", + "line", + "instagram", + "facebook", + "viber", + "linkedin", + "tiktok", + "telegram", + ], + }, + }, + hooks: { + async activate() { + const { id } = await this.messagebird.createWebhook({ + workspaceId: this.workspaceId, + data: { + service: "channels", + event: `${this.platform}.inbound`, + url: this.http.endpoint, + }, + }); + this._setHookId(id); + }, + async deactivate() { + const hookId = this._getHookId(); + if (hookId) { + await this.messagebird.deleteWebhook({ + workspaceId: this.workspaceId, + hookId, + }); + } + }, + }, + methods: { + _getHookId() { + return this.db.get("hookId"); + }, + _setHookId(hookId) { + this.db.set("hookId", hookId); + }, + generateMeta(event) { + return { + id: event.payload.id, + summary: `New Message ID: ${event.payload.id}`, + ts: Date.parse(event.createdAt), + }; + }, + }, + async run(event) { + const { body } = event; + if (!body) { + return; + } + const meta = this.generateMeta(body); + this.$emit(body, meta); + }, + sampleEmit, +}; diff --git a/components/messagebird/sources/new-message-received/test-event.mjs b/components/messagebird/sources/new-message-received/test-event.mjs new file mode 100644 index 0000000000000..c86ec5b5ee366 --- /dev/null +++ b/components/messagebird/sources/new-message-received/test-event.mjs @@ -0,0 +1,44 @@ +export default { + "service": "channels", + "event": "whatsapp.inbound", + "payload": { + "id": "404544c5-9920-45f1-8990-0855634ab7ac", + "channelId": "52ad151f-f7b4-46f9-a5c6-d3318e650c84", + "sender": { + "contact": { + "id": "d4e8935a-5f48-45a5-95a5-ee7ba1e2c5b4", + "identifierKey": "phonenumber", + "identifierValue": "+3511111111" + } + }, + "receiver": { + "connector": { + "id": "60bf59d6-fc6f-4511-89c4-75d9127d7a7c", + "identifierValue": "" + } + }, + "body": { + "type": "text", + "text": { + "text": "Test" + } + }, + "meta": { + "extraInformation": { + "timestamp": "1702496037" + } + }, + "reference": "", + "parts": [ + { + "platformReference": "wamid.HBgMMzUxOTE0MjYyNTM1FQIAEhggQUMzODQyNUY1MDlDQkU1QjJGNTM3RDlENjg0OTJGMDgA" + } + ], + "status": "delivered", + "reason": "", + "direction": "incoming", + "lastStatusAt": "2023-12-13T19:34:01.858Z", + "createdAt": "2023-12-13T19:34:01.858Z", + "updatedAt": "2023-12-13T19:34:02.063Z" + } +} \ No newline at end of file diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1369dec8beb36..d9e629cbb96da 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -6382,7 +6382,17 @@ importers: specifier: ^3.0.0 version: 3.0.3 - components/messagebird: {} + components/message_bird: + dependencies: + '@pipedream/platform': + specifier: ^3.0.3 + version: 3.0.3 + + components/messagebird: + dependencies: + '@pipedream/platform': + specifier: ^3.0.3 + version: 3.0.3 components/metabase: {}