diff --git a/components/typefully/actions/create-draft/create-draft.mjs b/components/typefully/actions/create-draft/create-draft.mjs new file mode 100644 index 0000000000000..9f0c2a3827856 --- /dev/null +++ b/components/typefully/actions/create-draft/create-draft.mjs @@ -0,0 +1,61 @@ +import typefully from "../../typefully.app.mjs"; + +export default { + key: "typefully-create-draft", + name: "Create Draft", + description: "Creates a new draft in Typefully. [See the documentation](https://support.typefully.com/en/articles/8718287-typefully-api#h_df59629cbf)", + version: "0.0.1", + type: "action", + props: { + typefully, + content: { + propDefinition: [ + typefully, + "content", + ], + }, + threadify: { + propDefinition: [ + typefully, + "threadify", + ], + optional: true, + }, + share: { + propDefinition: [ + typefully, + "share", + ], + optional: true, + }, + autoRetweetEnabled: { + propDefinition: [ + typefully, + "autoRetweetEnabled", + ], + optional: true, + }, + autoPlugEnabled: { + propDefinition: [ + typefully, + "autoPlugEnabled", + ], + optional: true, + }, + }, + async run({ $ }) { + const response = await this.typefully.createDraft({ + $, + data: { + content: this.content, + threadify: this.threadify, + share: this.share, + auto_retweet_enabled: this.autoRetweetEnabled, + auto_plug_enabled: this.autoPlugEnabled, + }, + }); + + $.export("$summary", `Created draft with ID: ${response.id}.`); + return response; + }, +}; diff --git a/components/typefully/actions/schedule-draft-next-slot/schedule-draft-next-slot.mjs b/components/typefully/actions/schedule-draft-next-slot/schedule-draft-next-slot.mjs new file mode 100644 index 0000000000000..ade6b6b3c45f1 --- /dev/null +++ b/components/typefully/actions/schedule-draft-next-slot/schedule-draft-next-slot.mjs @@ -0,0 +1,62 @@ +import typefully from "../../typefully.app.mjs"; + +export default { + key: "typefully-schedule-draft-next-slot", + name: "Schedule Draft Next Slot", + description: "Schedules an existing draft for publication in the next available time slot. [See the documentation](https://support.typefully.com/en/articles/8718287-typefully-api#h_df59629cbf)", + version: "0.0.1", + type: "action", + props: { + typefully, + content: { + propDefinition: [ + typefully, + "content", + ], + }, + threadify: { + propDefinition: [ + typefully, + "threadify", + ], + optional: true, + }, + share: { + propDefinition: [ + typefully, + "share", + ], + optional: true, + }, + autoRetweetEnabled: { + propDefinition: [ + typefully, + "autoRetweetEnabled", + ], + optional: true, + }, + autoPlugEnabled: { + propDefinition: [ + typefully, + "autoPlugEnabled", + ], + optional: true, + }, + }, + async run({ $ }) { + const response = await this.typefully.createDraft({ + $, + data: { + "content": this.content, + "threadify": this.threadify, + "share": this.share, + "schedule-date": "next-free-slot", + "auto_retweet_enabled": this.autoRetweetEnabled, + "auto_plug_enabled": this.autoPlugEnabled, + }, + }); + + $.export("$summary", `Draft scheduled successfully with ID: ${response.id}.`); + return response; + }, +}; diff --git a/components/typefully/actions/schedule-draft/schedule-draft.mjs b/components/typefully/actions/schedule-draft/schedule-draft.mjs new file mode 100644 index 0000000000000..7cfa0aafe00c3 --- /dev/null +++ b/components/typefully/actions/schedule-draft/schedule-draft.mjs @@ -0,0 +1,68 @@ +import typefully from "../../typefully.app.mjs"; + +export default { + key: "typefully-schedule-draft", + name: "Schedule Draft", + description: "Schedules a draft for publication at a specific date and time. [See the documentation](https://support.typefully.com/en/articles/8718287-typefully-api#h_df59629cbf)", + version: "0.0.1", + type: "action", + props: { + typefully, + content: { + propDefinition: [ + typefully, + "content", + ], + }, + threadify: { + propDefinition: [ + typefully, + "threadify", + ], + optional: true, + }, + share: { + propDefinition: [ + typefully, + "share", + ], + optional: true, + }, + scheduleDate: { + propDefinition: [ + typefully, + "scheduleDate", + ], + }, + autoRetweetEnabled: { + propDefinition: [ + typefully, + "autoRetweetEnabled", + ], + optional: true, + }, + autoPlugEnabled: { + propDefinition: [ + typefully, + "autoPlugEnabled", + ], + optional: true, + }, + }, + async run({ $ }) { + const response = await this.typefully.createDraft({ + $, + data: { + "content": this.content, + "threadify": this.threadify, + "share": this.share, + "schedule-date": this.scheduleDate, + "auto_retweet_enabled": this.autoRetweetEnabled, + "auto_plug_enabled": this.autoPlugEnabled, + }, + }); + + $.export("$summary", `Draft scheduled successfully with ID: ${response.id}.`); + return response; + }, +}; diff --git a/components/typefully/package.json b/components/typefully/package.json index 0d329147b4171..04f55f3dd9362 100644 --- a/components/typefully/package.json +++ b/components/typefully/package.json @@ -1,6 +1,6 @@ { "name": "@pipedream/typefully", - "version": "0.0.1", + "version": "0.1.0", "description": "Pipedream Typefully Components", "main": "typefully.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/typefully/sources/common/base.mjs b/components/typefully/sources/common/base.mjs new file mode 100644 index 0000000000000..39c8b4bb5420e --- /dev/null +++ b/components/typefully/sources/common/base.mjs @@ -0,0 +1,57 @@ +import { DEFAULT_POLLING_SOURCE_TIMER_INTERVAL } from "@pipedream/platform"; +import typefully from "../../typefully.app.mjs"; + +export default { + props: { + typefully, + 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 fn = await this.getFunction(); + const response = await fn(); + + let responseArray = []; + for await (const item of response) { + if (item.id <= lastId) break; + responseArray.push(item); + } + + if (responseArray.length) { + if (maxResults && (responseArray.length > maxResults)) { + responseArray.length = maxResults; + } + this._setLastId(responseArray[0].id); + } + + for (const item of responseArray.reverse()) { + this.$emit(item, { + id: item.id, + summary: this.getSummary(item), + ts: Date.parse(new Date()), + }); + } + }, + }, + hooks: { + async deploy() { + await this.emitEvent(25); + }, + }, + async run() { + await this.emitEvent(); + }, +}; diff --git a/components/typefully/sources/new-draft-published/new-draft-published.mjs b/components/typefully/sources/new-draft-published/new-draft-published.mjs new file mode 100644 index 0000000000000..79a6e2db5f90c --- /dev/null +++ b/components/typefully/sources/new-draft-published/new-draft-published.mjs @@ -0,0 +1,22 @@ +import common from "../common/base.mjs"; +import sampleEmit from "./test-event.mjs"; + +export default { + ...common, + key: "typefully-new-draft-published", + name: "New Draft Published", + description: "Emit new event when a draft is published to Twitter via Typefully.", + version: "0.0.1", + type: "source", + dedupe: "unique", + methods: { + ...common.methods, + getFunction() { + return this.typefully.getRecentlyPublishedDrafts; + }, + getSummary(draft) { + return `Draft Published: ${draft.id}`; + }, + }, + sampleEmit, +}; diff --git a/components/typefully/sources/new-draft-published/test-event.mjs b/components/typefully/sources/new-draft-published/test-event.mjs new file mode 100644 index 0000000000000..27370baa8303b --- /dev/null +++ b/components/typefully/sources/new-draft-published/test-event.mjs @@ -0,0 +1,15 @@ +export default { + "id": 123, + "status": "published", + "html": "content test", + "num_tweets": 1, + "last_edited": null, + "scheduled_date": null, + "published_on": "2025-01-09T10:22:11Z", + "share_url": "https://typefully.com/t/nb9Lxx13df", + "twitter_url": null, + "linkedin_url": null, + "text_first_tweet": "content", + "html_first_tweet": "content", + "text_preview_linkedin": null +} \ No newline at end of file diff --git a/components/typefully/sources/new-draft-scheduled/new-draft-scheduled.mjs b/components/typefully/sources/new-draft-scheduled/new-draft-scheduled.mjs new file mode 100644 index 0000000000000..790400310a02a --- /dev/null +++ b/components/typefully/sources/new-draft-scheduled/new-draft-scheduled.mjs @@ -0,0 +1,22 @@ +import common from "../common/base.mjs"; +import sampleEmit from "./test-event.mjs"; + +export default { + ...common, + key: "typefully-new-draft-scheduled", + name: "New Draft Scheduled", + description: "Emit new event when a draft is scheduled for publication.", + version: "0.0.1", + type: "source", + dedupe: "unique", + methods: { + ...common.methods, + getFunction() { + return this.typefully.getRecentlyScheduledDrafts; + }, + getSummary(draft) { + return `Scheduled draft: ${draft.id}`; + }, + }, + sampleEmit, +}; diff --git a/components/typefully/sources/new-draft-scheduled/test-event.mjs b/components/typefully/sources/new-draft-scheduled/test-event.mjs new file mode 100644 index 0000000000000..d9274837f569e --- /dev/null +++ b/components/typefully/sources/new-draft-scheduled/test-event.mjs @@ -0,0 +1,15 @@ +export default { + "id": 123, + "status": "scheduled", + "html": "content test", + "num_tweets": 1, + "last_edited": null, + "scheduled_date": "2025-01-09T10:22:11Z", + "published_on": null, + "share_url": "https://typefully.com/t/nb9Lxx13df", + "twitter_url": null, + "linkedin_url": null, + "text_first_tweet": "content", + "html_first_tweet": "content", + "text_preview_linkedin": null +} \ No newline at end of file diff --git a/components/typefully/typefully.app.mjs b/components/typefully/typefully.app.mjs index 3a90372e0eece..42f7acff460b9 100644 --- a/components/typefully/typefully.app.mjs +++ b/components/typefully/typefully.app.mjs @@ -1,11 +1,112 @@ +import { axios } from "@pipedream/platform"; + export default { type: "app", app: "typefully", - propDefinitions: {}, + propDefinitions: { + kind: { + type: "string", + label: "Kind", + description: "Filter notifications by kind", + async options() { + return [ + { + label: "Inbox", + value: "inbox", + }, + { + label: "Activity", + value: "activity", + }, + ]; + }, + }, + contentFilter: { + type: "string", + label: "Content Filter", + description: "Filter drafts by content type", + async options() { + return [ + { + label: "Threads", + value: "threads", + }, + { + label: "Tweets", + value: "tweets", + }, + ]; + }, + }, + content: { + type: "string", + label: "Content", + description: "You can split into multiple tweets by adding 4 consecutive newlines between tweets in the content", + }, + threadify: { + type: "boolean", + label: "Threadify", + description: "Content will be automatically split into multiple tweets", + }, + share: { + type: "boolean", + label: "Share", + description: "If true, returned payload will include a share_url", + }, + autoRetweetEnabled: { + type: "boolean", + label: "Auto Retweet Enabled", + description: "If true, the post will have an AutoRT enabled, according to the one set on Typefully for the account", + }, + autoPlugEnabled: { + type: "boolean", + label: "Auto Plug Enabled", + description: "If true, the post will have an AutoPlug enabled, according to the one set on Typefully for the account", + }, + scheduleDate: { + type: "string", + label: "Schedule Date", + description: "Date to schedule the draft (ISO format - YYYY-MM-DDTHH:MM:SSZ)", + }, + }, methods: { - // this.$auth contains connected account data - authKeys() { - console.log(Object.keys(this.$auth)); + _baseUrl() { + return "https://api.typefully.com/v1"; + }, + _headers() { + return { + "x-api-key": `${this.$auth.api_key}`, + }; + }, + _makeRequest({ + $ = this, path, ...opts + }) { + return axios($, { + url: this._baseUrl() + path, + headers: this._headers(), + ...opts, + }); + }, + createDraft(opts = {}) { + return this._makeRequest({ + method: "POST", + path: "/drafts/", + ...opts, + }); + }, + getRecentlyScheduledDrafts(opts = {}) { + return this._makeRequest({ + method: "GET", + path: "/drafts/recently-scheduled/", + ...opts, + }); + }, + getRecentlyPublishedDrafts(opts = {}) { + return this._makeRequest({ + method: "GET", + path: "/drafts/recently-published/", + ...opts, + }); }, }, }; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 53d5fb7135cf2..8190bcb5d54cb 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -11088,7 +11088,11 @@ importers: specifier: ^6.2.13 version: 6.2.13 - components/typefully: {} + components/typefully: + dependencies: + '@pipedream/platform': + specifier: ^3.0.3 + version: 3.0.3 components/typless: {} @@ -24731,22 +24735,22 @@ packages: superagent@3.8.1: resolution: {integrity: sha512-VMBFLYgFuRdfeNQSMLbxGSLfmXL/xc+OO+BZp41Za/NRDBet/BNbkRJrYzCUu0u4GU0i/ml2dtT8b9qgkw9z6Q==} engines: {node: '>= 4.0'} - deprecated: Please upgrade to v7.0.2+ of superagent. We have fixed numerous issues with streams, form-data, attach(), filesystem errors not bubbling up (ENOENT on attach()), and all tests are now passing. See the releases tab for more information at . + deprecated: Please upgrade to v9.0.0+ as we have fixed a public vulnerability with formidable dependency. Note that v9.0.0+ requires Node.js v14.18.0+. See https://github.com/ladjs/superagent/pull/1800 for insight. This project is supported and maintained by the team at Forward Email @ https://forwardemail.net superagent@4.1.0: resolution: {integrity: sha512-FT3QLMasz0YyCd4uIi5HNe+3t/onxMyEho7C3PSqmti3Twgy2rXT4fmkTz6wRL6bTF4uzPcfkUCa8u4JWHw8Ag==} engines: {node: '>= 6.0'} - deprecated: Please upgrade to v7.0.2+ of superagent. We have fixed numerous issues with streams, form-data, attach(), filesystem errors not bubbling up (ENOENT on attach()), and all tests are now passing. See the releases tab for more information at . + deprecated: Please upgrade to v9.0.0+ as we have fixed a public vulnerability with formidable dependency. Note that v9.0.0+ requires Node.js v14.18.0+. See https://github.com/ladjs/superagent/pull/1800 for insight. This project is supported and maintained by the team at Forward Email @ https://forwardemail.net superagent@5.3.1: resolution: {integrity: sha512-wjJ/MoTid2/RuGCOFtlacyGNxN9QLMgcpYLDQlWFIhhdJ93kNscFonGvrpAHSCVjRVj++DGCglocF7Aej1KHvQ==} engines: {node: '>= 7.0.0'} - deprecated: Please upgrade to v7.0.2+ of superagent. We have fixed numerous issues with streams, form-data, attach(), filesystem errors not bubbling up (ENOENT on attach()), and all tests are now passing. See the releases tab for more information at . + deprecated: Please upgrade to v9.0.0+ as we have fixed a public vulnerability with formidable dependency. Note that v9.0.0+ requires Node.js v14.18.0+. See https://github.com/ladjs/superagent/pull/1800 for insight. This project is supported and maintained by the team at Forward Email @ https://forwardemail.net superagent@7.1.6: resolution: {integrity: sha512-gZkVCQR1gy/oUXr+kxJMLDjla434KmSOKbx5iGD30Ql+AkJQ/YlPKECJy2nhqOsHLjGHzoDTXNSjhnvWhzKk7g==} engines: {node: '>=6.4.0 <13 || >=14'} - deprecated: Please downgrade to v7.1.5 if you need IE/ActiveXObject support OR upgrade to v8.0.0 as we no longer support IE and published an incorrect patch version (see https://github.com/visionmedia/superagent/issues/1731) + deprecated: Please upgrade to v9.0.0+ as we have fixed a public vulnerability with formidable dependency. Note that v9.0.0+ requires Node.js v14.18.0+. See https://github.com/ladjs/superagent/pull/1800 for insight. This project is supported and maintained by the team at Forward Email @ https://forwardemail.net supports-color@2.0.0: resolution: {integrity: sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==}