diff --git a/components/smstools/actions/add-contact-opt-out/add-contact-opt-out.mjs b/components/smstools/actions/add-contact-opt-out/add-contact-opt-out.mjs new file mode 100644 index 0000000000000..a696fba6d52cd --- /dev/null +++ b/components/smstools/actions/add-contact-opt-out/add-contact-opt-out.mjs @@ -0,0 +1,34 @@ +import { ConfigurationError } from "@pipedream/platform"; +import smstools from "../../smstools.app.mjs"; + +export default { + key: "smstools-add-contact-opt-out", + name: "Add Contact to Opt-Out List", + description: "Adds a selected contact to the opt-out list, stopping further communications. [See the documentation](https://www.smstools.com/en/sms-gateway-api/add_optout)", + version: "0.0.1", + type: "action", + props: { + smstools, + contactNumber: { + propDefinition: [ + smstools, + "contactNumber", + ], + }, + }, + async run({ $ }) { + try { + const response = await this.smstools.addOptOut({ + $, + data: { + number: this.contactNumber, + }, + }); + + $.export("$summary", `Successfully added contact number ${this.contactNumber} to the opt-out list.`); + return response; + } catch (e) { + throw new ConfigurationError("The number is already opted-out or does not exist in the database."); + } + }, +}; diff --git a/components/smstools/actions/add-contact/add-contact.mjs b/components/smstools/actions/add-contact/add-contact.mjs new file mode 100644 index 0000000000000..082c219cf366a --- /dev/null +++ b/components/smstools/actions/add-contact/add-contact.mjs @@ -0,0 +1,114 @@ +import { ConfigurationError } from "@pipedream/platform"; +import smstools from "../../smstools.app.mjs"; + +export default { + key: "smstools-add-contact", + name: "Add Contact to Group", + description: "Adds a new contact to an existing contact list. [See the documentation](https://www.smstools.com/en/sms-gateway-api/add_contact)", + version: "0.0.1", + type: "action", + props: { + smstools, + phone: { + type: "string", + label: "Phone Number", + description: "The phone number of the contact.", + }, + groupid: { + propDefinition: [ + smstools, + "groupId", + ], + }, + 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, + }, + birthday: { + type: "string", + label: "Birthday", + description: "Birthday of the contact. **Format: YYYY-MM-DD**.", + optional: true, + }, + extra1: { + type: "string", + label: "Extra 1", + description: "Extra field 1 for the contact.", + optional: true, + }, + extra2: { + type: "string", + label: "Extra 2", + description: "Extra field 2 for the contact.", + optional: true, + }, + extra3: { + type: "string", + label: "Extra 3", + description: "Extra field 3 for the contact.", + optional: true, + }, + extra4: { + type: "string", + label: "Extra 4", + description: "Extra field 4 for the contact.", + optional: true, + }, + extra5: { + type: "string", + label: "Extra 5", + description: "Extra field 5 for the contact.", + optional: true, + }, + extra6: { + type: "string", + label: "Extra 6", + description: "Extra field 6 for the contact.", + optional: true, + }, + extra7: { + type: "string", + label: "Extra 7", + description: "Extra field 7 for the contact.", + optional: true, + }, + extra8: { + type: "string", + label: "Extra 8", + description: "Extra field 8 for the contact.", + optional: true, + }, + unsubscribed: { + type: "boolean", + label: "Unsubscribed", + description: "Indicates if the contact is unsubscribed.", + optional: true, + }, + }, + async run({ $ }) { + try { + const { + smstools, + ...data + } = this; + + const response = await smstools.addContact({ + $, + data, + }); + + $.export("$summary", `Successfully added contact with ID: ${response.ID}`); + return response; + } catch (e) { + throw new ConfigurationError(e.response.data.errorMsg); + } + }, +}; diff --git a/components/smstools/actions/send-sms/send-sms.mjs b/components/smstools/actions/send-sms/send-sms.mjs new file mode 100644 index 0000000000000..aff1345e0b164 --- /dev/null +++ b/components/smstools/actions/send-sms/send-sms.mjs @@ -0,0 +1,72 @@ +import smstools from "../../smstools.app.mjs"; + +export default { + key: "smstools-send-sms", + name: "Send SMS or WhatsApp Message", + description: "Sends a SMS or WhatsApp message to a specified contact. [See the documentation](https://www.smstools.com/en/sms-gateway-api/send_message)", + version: "0.0.1", + type: "action", + props: { + smstools, + message: { + type: "string", + label: "Message", + description: "The message to be sent.", + }, + to: { + propDefinition: [ + smstools, + "contactNumber", + ], + type: "string[]", + description: "The contact(s) to send the message to.", + }, + sender: { + propDefinition: [ + smstools, + "sender", + ], + }, + date: { + type: "string", + label: "Scheduled Date", + description: "The date to send the message. **Format: yyyy-MM-dd HH:mm**. If not provided, the message will be sent as soon as possible.", + optional: true, + }, + reference: { + type: "string", + label: "Reference", + description: "Reference for the message.", + optional: true, + }, + test: { + type: "boolean", + label: "Test", + description: "Test mode for the message.", + optional: true, + }, + subId: { + propDefinition: [ + smstools, + "subId", + ], + optional: true, + }, + }, + async run({ $ }) { + const response = await this.smstools.sendMessage({ + $, + data: { + message: this.message, + to: this.to, + sender: this.sender, + date: this.date, + reference: this.reference, + test: this.test, + subId: this.subId, + }, + }); + $.export("$summary", `Message sent successfully with ID: ${response.messageid}`); + return response; + }, +}; diff --git a/components/smstools/package.json b/components/smstools/package.json index 787c8d992c03b..a69571462df76 100644 --- a/components/smstools/package.json +++ b/components/smstools/package.json @@ -1,6 +1,6 @@ { "name": "@pipedream/smstools", - "version": "0.0.1", + "version": "0.1.0", "description": "Pipedream SMSTools Components", "main": "smstools.app.mjs", "keywords": [ @@ -11,5 +11,8 @@ "author": "Pipedream (https://pipedream.com/)", "publishConfig": { "access": "public" + }, + "dependencies": { + "@pipedream/platform": "^3.0.3" } -} \ No newline at end of file +} diff --git a/components/smstools/smstools.app.mjs b/components/smstools/smstools.app.mjs index 9a06528cfd4ce..5f2d0e4f35784 100644 --- a/components/smstools/smstools.app.mjs +++ b/components/smstools/smstools.app.mjs @@ -1,11 +1,165 @@ +import { axios } from "@pipedream/platform"; + export default { type: "app", app: "smstools", - propDefinitions: {}, + propDefinitions: { + groupId: { + type: "string", + label: "Group ID", + description: "The group ID where the contact should be added.", + async options({ page }) { + const groups = await this.getGroups({ + params: { + page: page + 1, + }, + }); + return groups.map(({ + ID: value, name: label, + }) => ({ + label, + value, + })); + }, + }, + contactNumber: { + type: "string", + label: "Contact Number", + description: "Select a contact number to add to the opt-out list.", + async options({ page }) { + const { contacts } = await this.getContactNumbers({ + params: { + page: page + 1, + }, + }); + return contacts.map(({ phone }) => phone); + }, + }, + sender: { + type: "string", + label: "Sender", + description: "The sender ID for the message.", + async options() { + const senders = await this.getSenderIds(); + return senders.map(({ + ID: value, name: label, + }) => ({ + label, + value, + })); + }, + }, + subId: { + type: "string", + label: "Sub ID", + description: "Subaccount ID from which the message is sent.", + async options() { + const subaccounts = await this.getSubAccounts(); + return subaccounts.map(({ + ID: value, username: label, + }) => ({ + label, + value, + })); + }, + optional: true, + }, + }, methods: { - // this.$auth contains connected account data - authKeys() { - console.log(Object.keys(this.$auth)); + _baseUrl() { + return "https://api.smsgatewayapi.com/v1"; + }, + _params(params = {}) { + return { + client_id: `${this.$auth.client_id}`, + client_secret: `${this.$auth.client_secret}`, + ...params, + }; + }, + _makeRequest({ + $ = this, path, params, ...opts + }) { + return axios($, { + url: this._baseUrl() + path, + params: this._params(params), + ...opts, + }); + }, + getInboxMessages(opts = {}) { + return this._makeRequest({ + path: "/message/inbox", + ...opts, + }); + }, + getContactNumbers(opts = {}) { + return this._makeRequest({ + path: "/contact", + ...opts, + }); + }, + getGroups(opts = {}) { + return this._makeRequest({ + path: "/groups", + ...opts, + }); + }, + getSenderIds(opts = {}) { + return this._makeRequest({ + path: "/senderids", + ...opts, + }); + }, + getSubAccounts(opts = {}) { + return this._makeRequest({ + path: "/subaccount", + ...opts, + }); + }, + addContact(opts = {}) { + return this._makeRequest({ + method: "POST", + path: "/contact", + ...opts, + }); + }, + addOptOut(opts = {}) { + return this._makeRequest({ + method: "POST", + path: "/optouts", + ...opts, + }); + }, + sendMessage(opts = {}) { + return this._makeRequest({ + method: "POST", + path: "/message/send", + ...opts, + }); + }, + async *paginate({ + fn, params = {}, maxResults = null, ...opts + }) { + let hasMore = false; + let count = 0; + let page = 0; + + do { + params.page = ++page; + const { messages } = await fn({ + params, + ...opts, + }); + for (const d of messages) { + yield d; + + if (maxResults && ++count === maxResults) { + return count; + } + } + + hasMore = messages.length; + + } while (hasMore); }, }, -}; \ No newline at end of file +}; diff --git a/components/smstools/sources/new-inbound-message/new-inbound-message.mjs b/components/smstools/sources/new-inbound-message/new-inbound-message.mjs new file mode 100644 index 0000000000000..600a8616bdde3 --- /dev/null +++ b/components/smstools/sources/new-inbound-message/new-inbound-message.mjs @@ -0,0 +1,64 @@ +import { DEFAULT_POLLING_SOURCE_TIMER_INTERVAL } from "@pipedream/platform"; +import smstools from "../../smstools.app.mjs"; +import sampleEmit from "./test-event.mjs"; + +export default { + key: "smstools-new-inbound-message", + name: "New Inbound Message", + description: "Emit new event when a new inbound message is received.", + version: "0.0.1", + type: "source", + dedupe: "unique", + props: { + smstools, + db: "$.service.db", + timer: { + type: "$.interface.timer", + default: { + intervalSeconds: DEFAULT_POLLING_SOURCE_TIMER_INTERVAL, + }, + }, + }, + methods: { + _getLastId() { + return this.db.get("lastId") || 0; + }, + _setLastId(lastId) { + this.db.set("lastId", lastId); + }, + async emitEvent(maxResults = false) { + const lastId = this._getLastId(); + const response = this.smstools.paginate({ + fn: this.smstools.getInboxMessages, + maxResults, + }); + + let responseArray = []; + for await (const item of response) { + if (item.ID <= lastId) break; + responseArray.push(item); + } + + if (responseArray.length) { + this._setLastId(responseArray[0].ID); + } + + for (const item of responseArray.reverse()) { + this.$emit(item, { + id: item.ID, + summary: `New inbound message from ${item.sender}`, + ts: Date.parse(item.date), + }); + } + }, + }, + hooks: { + async deploy() { + await this.emitEvent(25); + }, + }, + async run() { + await this.emitEvent(); + }, + sampleEmit, +}; diff --git a/components/smstools/sources/new-inbound-message/test-event.mjs b/components/smstools/sources/new-inbound-message/test-event.mjs new file mode 100644 index 0000000000000..2fad07180fa26 --- /dev/null +++ b/components/smstools/sources/new-inbound-message/test-event.mjs @@ -0,0 +1,8 @@ +export default { + "ID": "{ID}", + "message": "Hello sms", + "sender": "{nr}", + "type": "sms", + "date": "2022-01-01 12:00:00", + "receiver": "{inbox_nr}" +} \ No newline at end of file diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4c445c9e62404..23d9259654b95 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -9363,7 +9363,10 @@ importers: '@pipedream/platform': 3.0.3 components/smstools: - specifiers: {} + specifiers: + '@pipedream/platform': ^3.0.3 + dependencies: + '@pipedream/platform': 3.0.3 components/smtp2go: specifiers: @@ -13116,55 +13119,6 @@ packages: - aws-crt dev: false - /@aws-sdk/client-sso-oidc/3.600.0_tdq3komn4zwyd65w7klbptsu34: - resolution: {integrity: sha512-7+I8RWURGfzvChyNQSyj5/tKrqRbzRl7H+BnTOf/4Vsw1nFOi5ROhlhD4X/Y0QCTacxnaoNcIrqnY7uGGvVRzw==} - engines: {node: '>=16.0.0'} - dependencies: - '@aws-crypto/sha256-browser': 5.2.0 - '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/client-sts': 3.600.0 - '@aws-sdk/core': 3.598.0 - '@aws-sdk/credential-provider-node': 3.600.0_f7n47caigsrjd2lr2szmwfuee4 - '@aws-sdk/middleware-host-header': 3.598.0 - '@aws-sdk/middleware-logger': 3.598.0 - '@aws-sdk/middleware-recursion-detection': 3.598.0 - '@aws-sdk/middleware-user-agent': 3.598.0 - '@aws-sdk/region-config-resolver': 3.598.0 - '@aws-sdk/types': 3.598.0 - '@aws-sdk/util-endpoints': 3.598.0 - '@aws-sdk/util-user-agent-browser': 3.598.0 - '@aws-sdk/util-user-agent-node': 3.598.0 - '@smithy/config-resolver': 3.0.3 - '@smithy/core': 2.2.3 - '@smithy/fetch-http-handler': 3.2.1 - '@smithy/hash-node': 3.0.2 - '@smithy/invalid-dependency': 3.0.2 - '@smithy/middleware-content-length': 3.0.2 - '@smithy/middleware-endpoint': 3.0.4 - '@smithy/middleware-retry': 3.0.6 - '@smithy/middleware-serde': 3.0.3 - '@smithy/middleware-stack': 3.0.3 - '@smithy/node-config-provider': 3.1.3 - '@smithy/node-http-handler': 3.1.2 - '@smithy/protocol-http': 4.0.3 - '@smithy/smithy-client': 3.1.6 - '@smithy/types': 3.3.0 - '@smithy/url-parser': 3.0.3 - '@smithy/util-base64': 3.0.0 - '@smithy/util-body-length-browser': 3.0.0 - '@smithy/util-body-length-node': 3.0.0 - '@smithy/util-defaults-mode-browser': 3.0.6 - '@smithy/util-defaults-mode-node': 3.0.6 - '@smithy/util-endpoints': 2.0.3 - '@smithy/util-middleware': 3.0.3 - '@smithy/util-retry': 3.0.2 - '@smithy/util-utf8': 3.0.0 - tslib: 2.6.3 - transitivePeerDependencies: - - '@aws-sdk/client-sts' - - aws-crt - dev: false - /@aws-sdk/client-sso/3.423.0: resolution: {integrity: sha512-znIufHkwhCIePgaYciIs3x/+BpzR57CZzbCKHR9+oOvGyufEPPpUT5bFLvbwTgfiVkTjuk6sG/ES3U5Bc+xtrA==} engines: {node: '>=14.0.0'} @@ -13400,7 +13354,7 @@ packages: dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/client-sso-oidc': 3.600.0_tdq3komn4zwyd65w7klbptsu34 + '@aws-sdk/client-sso-oidc': 3.600.0 '@aws-sdk/core': 3.598.0 '@aws-sdk/credential-provider-node': 3.600.0_f7n47caigsrjd2lr2szmwfuee4 '@aws-sdk/middleware-host-header': 3.598.0 @@ -13442,6 +13396,55 @@ packages: - aws-crt dev: false + /@aws-sdk/client-sts/3.600.0_dseaa2p5u2yk67qiepewcq3hkq: + resolution: {integrity: sha512-KQG97B7LvTtTiGmjlrG1LRAY8wUvCQzrmZVV5bjrJ/1oXAU7DITYwVbSJeX9NWg6hDuSk0VE3MFwIXS2SvfLIA==} + engines: {node: '>=16.0.0'} + dependencies: + '@aws-crypto/sha256-browser': 5.2.0 + '@aws-crypto/sha256-js': 5.2.0 + '@aws-sdk/client-sso-oidc': 3.600.0 + '@aws-sdk/core': 3.598.0 + '@aws-sdk/credential-provider-node': 3.600.0_f7n47caigsrjd2lr2szmwfuee4 + '@aws-sdk/middleware-host-header': 3.598.0 + '@aws-sdk/middleware-logger': 3.598.0 + '@aws-sdk/middleware-recursion-detection': 3.598.0 + '@aws-sdk/middleware-user-agent': 3.598.0 + '@aws-sdk/region-config-resolver': 3.598.0 + '@aws-sdk/types': 3.598.0 + '@aws-sdk/util-endpoints': 3.598.0 + '@aws-sdk/util-user-agent-browser': 3.598.0 + '@aws-sdk/util-user-agent-node': 3.598.0 + '@smithy/config-resolver': 3.0.3 + '@smithy/core': 2.2.3 + '@smithy/fetch-http-handler': 3.2.1 + '@smithy/hash-node': 3.0.2 + '@smithy/invalid-dependency': 3.0.2 + '@smithy/middleware-content-length': 3.0.2 + '@smithy/middleware-endpoint': 3.0.4 + '@smithy/middleware-retry': 3.0.6 + '@smithy/middleware-serde': 3.0.3 + '@smithy/middleware-stack': 3.0.3 + '@smithy/node-config-provider': 3.1.3 + '@smithy/node-http-handler': 3.1.2 + '@smithy/protocol-http': 4.0.3 + '@smithy/smithy-client': 3.1.6 + '@smithy/types': 3.3.0 + '@smithy/url-parser': 3.0.3 + '@smithy/util-base64': 3.0.0 + '@smithy/util-body-length-browser': 3.0.0 + '@smithy/util-body-length-node': 3.0.0 + '@smithy/util-defaults-mode-browser': 3.0.6 + '@smithy/util-defaults-mode-node': 3.0.6 + '@smithy/util-endpoints': 2.0.3 + '@smithy/util-middleware': 3.0.3 + '@smithy/util-retry': 3.0.2 + '@smithy/util-utf8': 3.0.0 + tslib: 2.6.3 + transitivePeerDependencies: + - '@aws-sdk/client-sso-oidc' + - aws-crt + dev: false + /@aws-sdk/core/3.556.0: resolution: {integrity: sha512-vJaSaHw2kPQlo11j/Rzuz0gk1tEaKdz+2ser0f0qZ5vwFlANjt08m/frU17ctnVKC1s58bxpctO/1P894fHLrA==} engines: {node: '>=14.0.0'} @@ -16170,12 +16173,12 @@ packages: kuler: 2.0.0 dev: false - /@definitelytyped/header-parser/0.2.12: - resolution: {integrity: sha512-UYtSXiLMhzRFKh7xHMkgiWsscgHxIndmjetaptZMMS0EOvfhUTuEM68GpjiCtz5shXw22Vacs1vDTAkKGDhNmg==} + /@definitelytyped/header-parser/0.2.13: + resolution: {integrity: sha512-m7YEtGhwAjmQyJQFQ7q8+hTGTiC/WrdRATvw8fyTwgW+RiWUt8MAeehuFj4txnCYXDcLO0ozuW5gNrLoYR4Ubg==} engines: {node: '>=18.18.0'} dependencies: '@definitelytyped/typescript-versions': 0.1.4 - '@definitelytyped/utils': 0.1.7 + '@definitelytyped/utils': 0.1.8 semver: 7.6.3 dev: true @@ -16184,8 +16187,8 @@ packages: engines: {node: '>=18.18.0'} dev: true - /@definitelytyped/utils/0.1.7: - resolution: {integrity: sha512-t58AeNg6+mvyMnBHyPC6JQqWMW0Iwyb+vlpBz4V0d0iDY9H8gGCnLFg9vtN1nC+JXfTXBlf9efu9unMUeaPCiA==} + /@definitelytyped/utils/0.1.8: + resolution: {integrity: sha512-4JINx4Rttha29f50PBsJo48xZXx/He5yaIWJRwVarhYAN947+S84YciHl+AIhQNRPAFkg8+5qFngEGtKxQDWXA==} engines: {node: '>=18.18.0'} dependencies: '@qiwi/npm-registry-client': 8.9.1 @@ -17734,7 +17737,7 @@ packages: '@aws-sdk/client-sns': 3.423.0 '@aws-sdk/client-sqs': 3.423.0 '@aws-sdk/client-ssm': 3.423.0 - '@aws-sdk/client-sts': 3.600.0 + '@aws-sdk/client-sts': 3.600.0_dseaa2p5u2yk67qiepewcq3hkq '@aws-sdk/s3-request-presigner': 3.609.0 '@pipedream/helper_functions': 0.3.12 '@pipedream/platform': 1.6.6 @@ -24547,7 +24550,7 @@ packages: peerDependencies: typescript: '*' dependencies: - '@definitelytyped/header-parser': 0.2.12 + '@definitelytyped/header-parser': 0.2.13 command-exists: 1.2.9 rimraf: 3.0.2 semver: 6.3.1 @@ -24562,7 +24565,7 @@ packages: peerDependencies: typescript: '*' dependencies: - '@definitelytyped/header-parser': 0.2.12 + '@definitelytyped/header-parser': 0.2.13 command-exists: 1.2.9 rimraf: 3.0.2 semver: 6.3.1 @@ -24578,9 +24581,9 @@ packages: peerDependencies: typescript: '>= 3.0.0-dev || >= 3.1.0-dev || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.7.0-dev || >= 3.8.0-dev || >= 3.9.0-dev || >= 4.0.0-dev' dependencies: - '@definitelytyped/header-parser': 0.2.12 + '@definitelytyped/header-parser': 0.2.13 '@definitelytyped/typescript-versions': 0.1.4 - '@definitelytyped/utils': 0.1.7 + '@definitelytyped/utils': 0.1.8 dts-critic: 3.3.11_typescript@5.2.2 fs-extra: 6.0.1 json-stable-stringify: 1.0.2 @@ -24598,9 +24601,9 @@ packages: peerDependencies: typescript: '>= 3.0.0-dev || >= 3.1.0-dev || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.7.0-dev || >= 3.8.0-dev || >= 3.9.0-dev || >= 4.0.0-dev' dependencies: - '@definitelytyped/header-parser': 0.2.12 + '@definitelytyped/header-parser': 0.2.13 '@definitelytyped/typescript-versions': 0.1.4 - '@definitelytyped/utils': 0.1.7 + '@definitelytyped/utils': 0.1.8 dts-critic: 3.3.11_typescript@5.5.4 fs-extra: 6.0.1 json-stable-stringify: 1.0.2