diff --git a/components/beekeeper/actions/create-post/create-post.mjs b/components/beekeeper/actions/create-post/create-post.mjs new file mode 100644 index 0000000000000..22bfb8d879cf3 --- /dev/null +++ b/components/beekeeper/actions/create-post/create-post.mjs @@ -0,0 +1,101 @@ +import beekeeper from "../../beekeeper.app.mjs"; +import { parseObject } from "../../common/utils.mjs"; + +export default { + key: "beekeeper-create-post", + name: "Create Post", + description: "Create a new text or multimedia post in a defined stream. [See the documentation](https://beekeeper.stoplight.io/docs/beekeeper-api/18408b41927b9-creates-a-new-post)", + version: "0.0.1", + type: "action", + props: { + beekeeper, + streamId: { + propDefinition: [ + beekeeper, + "streamId", + ], + }, + text: { + type: "string", + label: "Text", + description: "The text content of the post", + }, + files: { + type: "string[]", + label: "Files", + description: "List of file objects to be attached. E.g. [{\"name\": \"fair_play_rules.pdf\", \"url\": \"https://mytenant.beekeeper.io/file/665987/original/fair_play_rules.pdf\", \"userid\": \"5cb9v45d-8i78-4v65-b5fd-81cgfac3ef17\", \"height\": 619, \"width\": 700, \"key\": \"f4fdaab0-d198-49b4-b1cc-dd85572d72f1\", \"media_type\": \"image/png\", \"usage_type\": \"attachment_image\" }]. [See the documentation](https://beekeeper.stoplight.io/docs/beekeeper-api/18408b41927b9-creates-a-new-post) for further details.", + optional: true, + }, + locked: { + type: "boolean", + label: "Locked", + description: "Disables adding comments on the post", + optional: true, + }, + title: { + type: "string", + label: "Title", + description: "The title of the post", + optional: true, + }, + media: { + type: "string[]", + label: "Media", + description: "List of Photo or Video objects. E.g. [{\"name\": \"fair_play_rules.pdf\", \"url\": \"https://mytenant.beekeeper.io/file/665987/original/fair_play_rules.pdf\", \"userid\": \"5cb9v45d-8i78-4v65-b5fd-81cgfac3ef17\", \"height\": 619, \"width\": 700, \"key\": \"f4fdaab0-d198-49b4-b1cc-dd85572d72f1\", \"media_type\": \"image/png\", \"usage_type\": \"attachment_image\" }]. [See the documentation](https://beekeeper.stoplight.io/docs/beekeeper-api/18408b41927b9-creates-a-new-post) for further details.", + optional: true, + }, + labels: { + type: "string[]", + label: "Labels", + description: "List of labels attached to the post", + optional: true, + }, + sticky: { + type: "boolean", + label: "Sticky", + description: "Flag that pins a post to the top of the stream", + optional: true, + }, + reactionsDisabled: { + type: "boolean", + label: "Reactions Disabled", + description: "Flag that disables the ability to add reaction to the post and to see reactions that have been added", + optional: true, + }, + options: { + type: "string[]", + label: "Options", + description: "List of poll options in a post. E.g. [\"This Friday\", \"Monday next week\"]", + optional: true, + }, + scheduledAt: { + type: "string", + label: "Scheduled At", + description: "Date and time when the post is scheduled to be published (UTC timezone, yyyy-mm-ddTHH:MM:SS format)", + optional: true, + }, + }, + async run({ $ }) { + const response = await this.beekeeper.createPost({ + $, + data: { + streamid: this.streamId, + files: parseObject(this.files), + locked: this.locked, + title: this.title, + media: parseObject(this.media), + labels: parseObject(this.labels), + sticky: this.sticky, + reactions_disabled: this.reactionsDisabled, + text: this.text, + options: parseObject(this.options)?.map((item) => ({ + text: item, + })), + scheduled_at: this.scheduledAt, + }, + }); + + $.export("$summary", `Successfully created post with UUID: ${response.uuid}`); + return response; + }, +}; diff --git a/components/beekeeper/actions/get-profile/get-profile.mjs b/components/beekeeper/actions/get-profile/get-profile.mjs new file mode 100644 index 0000000000000..1e6c22c76f9c0 --- /dev/null +++ b/components/beekeeper/actions/get-profile/get-profile.mjs @@ -0,0 +1,43 @@ +import beekeeper from "../../beekeeper.app.mjs"; + +export default { + key: "beekeeper-get-profile", + name: "Get User Profile", + description: "Retrieve the profile details of a specific user. [See the documentation](https://beekeeper.stoplight.io/docs/beekeeper-api/05bcd13b38a67-get-profile-of-the-given-user)", + version: "0.0.1", + type: "action", + props: { + beekeeper, + userId: { + propDefinition: [ + beekeeper, + "userId", + ], + }, + includeActivities: { + type: "boolean", + label: "Include Activities", + description: "Whether to include the user's activities.", + default: true, + }, + includeTotals: { + type: "boolean", + label: "Include Totals", + description: "Whether to include the user's total number of posts, comments, and likes received.", + default: true, + }, + }, + async run({ $ }) { + const response = await this.beekeeper.getUserProfile({ + $, + userId: this.userId, + params: { + include_activities: this.includeActivities, + include_totals: this.includeTotals, + }, + }); + + $.export("$summary", `Successfully retrieved profile for user ID ${this.userId}`); + return response; + }, +}; diff --git a/components/beekeeper/actions/send-message-group-chat/send-message-group-chat.mjs b/components/beekeeper/actions/send-message-group-chat/send-message-group-chat.mjs new file mode 100644 index 0000000000000..b440b02f6ce51 --- /dev/null +++ b/components/beekeeper/actions/send-message-group-chat/send-message-group-chat.mjs @@ -0,0 +1,86 @@ +import { ConfigurationError } from "@pipedream/platform"; +import beekeeper from "../../beekeeper.app.mjs"; +import { parseObject } from "../../common/utils.mjs"; + +export default { + key: "beekeeper-send-message-group-chat", + name: "Send Message to Group Chat", + description: "Send a precomposed message to a defined group chat. [See the documentation](https://beekeeper.stoplight.io/docs/beekeeper-api/9075b32d36db4-send-a-message-to-a-group-chat)", + version: "0.0.1", + type: "action", + props: { + beekeeper, + chatId: { + propDefinition: [ + beekeeper, + "chatId", + ], + }, + body: { + type: "string", + label: "Body", + description: "The message body. Supports rich text formatting.", + optional: true, + }, + attachment: { + type: "object", + label: "Attachment", + description: "JSON object representing attachment. A valid attachment object can be created and fetched using the POST /files/{usage_type}/upload endpoint.", + optional: true, + }, + eventType: { + type: "string", + label: "Event Type", + description: "Type of event that the message corresponds to.", + optional: true, + }, + chatStateAddons: { + type: "string[]", + label: "Chat State Addons", + description: "Array of objects containing type and data fields for chat state addons.", + optional: true, + }, + messageAddons: { + type: "string[]", + label: "Message Addons", + description: "Array of objects containing type and data fields for message addons.", + optional: true, + }, + mentions: { + propDefinition: [ + beekeeper, + "mentions", + ({ chatId }) => ({ + chatId, + }), + ], + optional: true, + }, + }, + async run({ $ }) { + if (!this.body && !this.eventType && !this.attachment && !this.chatStateAddons) { + throw new ConfigurationError("You must provide at least **Body**, **Event**, **Attachment** or **Chat State Addons**"); + } + const response = await this.beekeeper.sendMessage({ + $, + chatId: this.chatId, + data: { + body: this.body, + attachment: parseObject(this.attachment), + ...(this.eventType + ? { + event: { + type: this.eventType, + }, + } + : {}), + chat_state_addons: parseObject(this.chatStateAddons), + message_addons: parseObject(this.messageAddons), + mentions: parseObject(this.mentions), + }, + }); + + $.export("$summary", `Message sent successfully to chat ID: ${this.chatId}`); + return response; + }, +}; diff --git a/components/beekeeper/beekeeper.app.mjs b/components/beekeeper/beekeeper.app.mjs index a83983245740c..4ce78002e7da6 100644 --- a/components/beekeeper/beekeeper.app.mjs +++ b/components/beekeeper/beekeeper.app.mjs @@ -1,11 +1,172 @@ +import { axios } from "@pipedream/platform"; +import { LIMIT } from "./common/constants.mjs"; + export default { type: "app", app: "beekeeper", - propDefinitions: {}, + propDefinitions: { + chatId: { + type: "string", + label: "Chat ID", + description: "The ID of the chat to send a message", + async options() { + const { chats } = await this.listChats(); + return chats.map(({ + id: value, title: label, + }) => ({ + label, + value, + })); + }, + }, + streamId: { + type: "string", + label: "Stream ID", + description: "The ID of the stream to create a post", + async options() { + const streams = await this.listStreams(); + return streams.map(({ + id: value, name: label, + }) => ({ + label, + value, + })); + }, + }, + userId: { + type: "string", + label: "User ID", + description: "The user ID for profile details", + async options({ page }) { + const users = await this.listUsers({ + params: { + limit: LIMIT, + offset: LIMIT * page, + }, + }); + return users.map(({ + id: value, display_name: label, + }) => ({ + label, + value, + })); + }, + }, + mentions: { + type: "string[]", + label: "Mentions", + description: "IDs of the users mentioned in the message body. Can also have value of 'all', which means all the chat members were mentioned.", + async options({ chatId }) { + const members = await this.listChatMembers({ + chatId, + }); + return [ + { + label: "All Members", + value: "all", + }, + ...members.map(({ + id: value, display_name: label, + }) => ({ + label, + value, + })), + ]; + }, + }, + }, methods: { - // this.$auth contains connected account data - authKeys() { - console.log(Object.keys(this.$auth)); + _baseUrl() { + return `https://${this.$auth.subdomain}.beekeeper.io/api/2`; + }, + _headers() { + return { + Authorization: `Token ${this.$auth.access_token}`, + }; + }, + _makeRequest({ + $ = this, path, ...opts + }) { + return axios($, { + url: this._baseUrl() + path, + headers: this._headers(), + ...opts, + }); + }, + listChats(opts = {}) { + return this._makeRequest({ + path: "/chats/groups", + ...opts, + }); + }, + listStreams(opts = {}) { + return this._makeRequest({ + path: "/streams", + ...opts, + }); + }, + listUsers(opts = {}) { + return this._makeRequest({ + path: "/users", + ...opts, + }); + }, + async listChatMembers({ + chatId, ...opts + }) { + const { members } = await this._makeRequest({ + path: `/chats/groups/${chatId}/members`, + ...opts, + }); + + return await this.listUserProfiles({ + params: { + user_ids: members.map(({ user_id }) => user_id), + }, + }); + }, + createPost(opts = {}) { + return this._makeRequest({ + method: "POST", + path: "/posts", + ...opts, + }); + }, + sendMessage({ + chatId, ...opts + }) { + return this._makeRequest({ + method: "POST", + path: `/chats/groups/${chatId}/messages`, + ...opts, + }); + }, + listUserProfiles(opts = {}) { + return this._makeRequest({ + path: "/profiles", + ...opts, + }); + }, + getUserProfile({ + userId, ...opts + }) { + return this._makeRequest({ + path: `/profiles/${userId}`, + ...opts, + }); + }, + createWebhook(opts = {}) { + return this._makeRequest({ + method: "POST", + path: "/webhooks", + ...opts, + }); + }, + deleteWebhook(webhookId) { + return this._makeRequest({ + method: "DELETE", + path: `/webhooks/${webhookId}`, + }); }, }, }; diff --git a/components/beekeeper/common/constants.mjs b/components/beekeeper/common/constants.mjs new file mode 100644 index 0000000000000..ea830c15a04cb --- /dev/null +++ b/components/beekeeper/common/constants.mjs @@ -0,0 +1 @@ +export const LIMIT = 100; diff --git a/components/beekeeper/common/utils.mjs b/components/beekeeper/common/utils.mjs new file mode 100644 index 0000000000000..dcc9cc61f6f41 --- /dev/null +++ b/components/beekeeper/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/beekeeper/package.json b/components/beekeeper/package.json index 00c5d06a9a1c4..3de34f8b23586 100644 --- a/components/beekeeper/package.json +++ b/components/beekeeper/package.json @@ -1,6 +1,6 @@ { "name": "@pipedream/beekeeper", - "version": "0.6.0", + "version": "0.7.0", "description": "Pipedream beekeeper Components", "main": "beekeeper.app.mjs", "keywords": [ @@ -13,6 +13,6 @@ "access": "public" }, "dependencies": { - "@pipedream/platform": "^3.0.0" + "@pipedream/platform": "^3.0.3" } } diff --git a/components/beekeeper/sources/common/base.mjs b/components/beekeeper/sources/common/base.mjs new file mode 100644 index 0000000000000..05bf22394214a --- /dev/null +++ b/components/beekeeper/sources/common/base.mjs @@ -0,0 +1,42 @@ +import beekeeper from "../../beekeeper.app.mjs"; + +export default { + props: { + beekeeper, + http: { + type: "$.interface.http", + customResponse: true, + }, + db: "$.service.db", + }, + methods: { + _getHookId() { + return this.db.get("hookId"); + }, + _setHookId(hookId) { + this.db.set("hookId", hookId); + }, + }, + hooks: { + async activate() { + const response = await this.beekeeper.createWebhook({ + data: { + callback_url: this.http.endpoint, + event_type: this.getEventType(), + }, + }); + this._setHookId(response.id); + }, + async deactivate() { + const webhookId = this._getHookId(); + await this.beekeeper.deleteWebhook(webhookId); + }, + }, + async run({ body }) { + this.$emit(body, { + id: body.notification_id, + summary: this.getSummary(body), + ts: Date.now(), + }); + }, +}; diff --git a/components/beekeeper/sources/new-chat-message-instant/new-chat-message-instant.mjs b/components/beekeeper/sources/new-chat-message-instant/new-chat-message-instant.mjs new file mode 100644 index 0000000000000..0cb0e5da59350 --- /dev/null +++ b/components/beekeeper/sources/new-chat-message-instant/new-chat-message-instant.mjs @@ -0,0 +1,22 @@ +import common from "../common/base.mjs"; +import sampleEmit from "./test-event.mjs"; + +export default { + ...common, + key: "beekeeper-new-chat-message-instant", + name: "New Chat Message (Instant)", + description: "Emit new event instantly when a new chat message is created in any chat the user is a member of. [See the documentation](https://beekeeper.stoplight.io/docs/beekeeper-api/1ba495ce70084-register-a-new-webhook)", + version: "0.0.1", + type: "source", + dedupe: "unique", + methods: { + ...common.methods, + getEventType() { + return "CHATS_V2.MESSAGE.CREATED"; + }, + getSummary(body) { + return `New message in chat ${body.payload.message.id}`; + }, + }, + sampleEmit, +}; diff --git a/components/beekeeper/sources/new-chat-message-instant/test-event.mjs b/components/beekeeper/sources/new-chat-message-instant/test-event.mjs new file mode 100644 index 0000000000000..7668a3accd1f2 --- /dev/null +++ b/components/beekeeper/sources/new-chat-message-instant/test-event.mjs @@ -0,0 +1,37 @@ +export default { + "payload": { + "message": { + "files": [ + null + ], + "profile": "peter_smith", + "user_id": "5cb9v45d-8i78-4v65-b5fd-81cgfac3ef17", + "name": "Hello", + "links": [ + {} + ], + "created": "2016-10-07T12:48:27", + "text": "Hi guys, when would you like to meet?", + "is_event": true, + "photos": [ + null + ], + "message_type": "regular", + "avatar": "https://dz343oy86h947.cloudfront.net/business/neutral/normal/05.png", + "media": [ + null + ], + "conversation_id": 853613, + "addons": [ + {} + ], + "sent_by_user": true, + "id": 0, + "uuid": "162c1c0c-2e30-4faf-8f0d-deb0a77aef5a" + } + }, + "notification_id": "5469063f-b63c-4541-a796-40d536907ec9", + "event_type": "CHATS.MESSAGE.CREATED", + "tenant_fqdn": "happytenant.beekeper.io", + "webhook_id": "5469063f-b63c-4541-a796-40d536907ec9" +} \ No newline at end of file diff --git a/components/beekeeper/sources/new-comment-created-instant/new-comment-created-instant.mjs b/components/beekeeper/sources/new-comment-created-instant/new-comment-created-instant.mjs new file mode 100644 index 0000000000000..f03e2e7f8fc6f --- /dev/null +++ b/components/beekeeper/sources/new-comment-created-instant/new-comment-created-instant.mjs @@ -0,0 +1,22 @@ +import common from "../common/base.mjs"; +import sampleEmit from "./test-event.mjs"; + +export default { + ...common, + key: "beekeeper-new-comment-created-instant", + name: "New Comment Created (Instant)", + description: "Emit new event when a new comment is created. [See the documentation](https://beekeeper.stoplight.io/docs/beekeeper-api/1ba495ce70084-register-a-new-webhook)", + version: "0.0.1", + type: "source", + dedupe: "unique", + methods: { + ...common.methods, + getEventType() { + return "POSTS.COMMENT.CREATED"; + }, + getSummary(body) { + return `New comment on post ${body.payload.comment.postid}`; + }, + }, + sampleEmit, +}; diff --git a/components/beekeeper/sources/new-comment-created-instant/test-event.mjs b/components/beekeeper/sources/new-comment-created-instant/test-event.mjs new file mode 100644 index 0000000000000..d7f5f065950ff --- /dev/null +++ b/components/beekeeper/sources/new-comment-created-instant/test-event.mjs @@ -0,0 +1,16 @@ +export default { + "payload": { + "comment": { + "id": 0, + "postid": 0, + "stream_id": 0, + "text": "string", + "user_id": "string", + "display_name": "string" + } + }, + "notification_id": "5469063f-b63c-4541-a796-40d536907ec9", + "event_type": "POSTS.COMMENT.CREATED", + "tenant_fqdn": "happytenant.beekeper.io", + "webhook_id": "5469063f-b63c-4541-a796-40d536907ec9" +} \ No newline at end of file diff --git a/components/beekeeper/sources/new-user-created-instant/new-user-created-instant.mjs b/components/beekeeper/sources/new-user-created-instant/new-user-created-instant.mjs new file mode 100644 index 0000000000000..a43f6db5f8c15 --- /dev/null +++ b/components/beekeeper/sources/new-user-created-instant/new-user-created-instant.mjs @@ -0,0 +1,22 @@ +import common from "../common/base.mjs"; +import sampleEmit from "./test-event.mjs"; + +export default { + ...common, + key: "beekeeper-new-user-created-instant", + name: "New User Created (Instant)", + description: "Emit new event when a new user is created. [See the documentation](https://beekeeper.stoplight.io/docs/beekeeper-api/1ba495ce70084-register-a-new-webhook)", + version: "0.0.1", + type: "source", + dedupe: "unique", + methods: { + ...common.methods, + getEventType() { + return "USER.CREATED"; + }, + getSummary(body) { + return `New user created: ${body.payload.user_id}`; + }, + }, + sampleEmit, +}; diff --git a/components/beekeeper/sources/new-user-created-instant/test-event.mjs b/components/beekeeper/sources/new-user-created-instant/test-event.mjs new file mode 100644 index 0000000000000..599863fe174f1 --- /dev/null +++ b/components/beekeeper/sources/new-user-created-instant/test-event.mjs @@ -0,0 +1,9 @@ +export default { + "payload": { + "user_id": "string" + }, + "notification_id": "5469063f-b63c-4541-a796-40d536907ec9", + "event_type": "USER.CREATED", + "tenant_fqdn": "happytenant.beekeper.io", + "webhook_id": "5469063f-b63c-4541-a796-40d536907ec9" +} \ No newline at end of file diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6b8f3ed1afc0b..ed2f46d408029 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -367,8 +367,7 @@ importers: specifier: ^1.2.1 version: 1.6.6 - components/adtraction: - specifiers: {} + components/adtraction: {} components/adversus: dependencies: @@ -1399,7 +1398,7 @@ importers: components/beekeeper: dependencies: '@pipedream/platform': - specifier: ^3.0.0 + specifier: ^3.0.3 version: 3.0.3 components/benchmark_email: @@ -3739,8 +3738,7 @@ importers: components/dokan: {} - components/dolibarr: - specifiers: {} + components/dolibarr: {} components/domain_group: dependencies: @@ -7373,8 +7371,7 @@ importers: specifier: ^3.0.0 version: 3.0.3 - components/limoexpress: - specifiers: {} + components/limoexpress: {} components/line: dependencies: @@ -9884,8 +9881,7 @@ importers: specifier: ^1.5.1 version: 1.6.6 - components/pinghome: - specifiers: {} + components/pinghome: {} components/pingone: {} @@ -10380,8 +10376,7 @@ importers: components/procore_sandbox: {} - components/prodatakey: - specifiers: {} + components/prodatakey: {} components/prodpad: dependencies: @@ -15050,8 +15045,7 @@ importers: specifier: ^3.0.3 version: 3.0.3 - components/zenedu: - specifiers: {} + components/zenedu: {} components/zenkit: dependencies: