{ "kind": "open-flow-workflow", "version": 1, "name": "GitHub Star Alerts in Slack", "content": { "modelVersion": 14, "document": { "bindings": {}, "graph": { "nodes": { "n_dbd93ced1a4e96fa": { "name": "Receive repository star event", "kind": "integration", "config": { "owner": { "kind": "value", "value": "your-github-owner" }, "repo": { "kind": "value", "value": "your-github-repo" }, "events": { "kind": "value", "value": [ "star" ] }, "insecureSsl": { "kind": "value", "value": false } }, "definition": { "configInputs": [ { "jsonSchema": { "type": "array", "items": { "type": "string", "enum": [ "*", "branch_protection_configuration", "branch_protection_rule", "check_run", "check_suite", "code_scanning_alert", "commit_comment", "create", "custom_property_values", "delete", "dependabot_alert", "deploy_key", "deployment", "deployment_status", "discussion", "discussion_comment", "fork", "gollum", "issue_comment", "issue_dependencies", "issues", "label", "member", "meta", "milestone", "package", "page_build", "project", "project_card", "project_column", "public", "pull_request", "pull_request_review", "pull_request_review_comment", "pull_request_review_thread", "push", "registry_package", "release", "repository", "repository_advisory", "repository_import", "repository_ruleset", "repository_vulnerability_alert", "secret_scanning_alert", "secret_scanning_alert_location", "secret_scanning_scan", "security_and_analysis", "star", "status", "sub_issues", "team_add", "watch", "workflow_job", "workflow_run" ] }, "minItems": 1, "uniqueItems": true }, "nullable": false, "handle": "events" }, { "jsonSchema": { "type": "boolean" }, "nullable": false, "handle": "insecureSsl", "value": false }, { "jsonSchema": { "type": "string", "pattern": "^[a-zA-Z0-9](?:[a-zA-Z0-9]|-(?=[a-zA-Z0-9])){0,38}$" }, "nullable": false, "handle": "owner" }, { "jsonSchema": { "type": "string", "pattern": "^(?!\\.{1,2}$)[a-zA-Z0-9._-]{1,100}$" }, "nullable": false, "handle": "repo" } ], "definitionVersion": 2, "description": "Triggers when selected GitHub webhook events occur in a repository.", "displayName": "Repository Event", "key": "github.on_repo_event", "name": "on_repo_event", "outputs": [ { "jsonSchema": { "type": "object" }, "nullable": false, "handle": "body" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "deliveryId" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "event" } ], "provider": "github", "type": "integration", "endpoint": { "body": { "allowArray": false, "allowEmpty": false, "formats": [ "json" ] }, "methods": [ "POST" ], "successStatus": 202 } } }, "n_df47d3bdbd824375": { "inputs": { "body": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_dbd93ced1a4e96fa", "output": "body" } ] } }, "name": "Format added or removed star", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "object" }, "nullable": false, "handle": "body" } ], "outputs": [ { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "text" }, { "jsonSchema": { "type": "array", "items": { "type": "object", "properties": {}, "additionalProperties": true, "description": "A Slack legacy attachment object. Prefer blocks for new messages when possible." }, "minItems": 1, "description": "Slack legacy attachments to include in the message." }, "nullable": false, "handle": "attachments" }, { "jsonSchema": { "type": "string", "enum": [ "created", "deleted" ] }, "nullable": false, "handle": "action" } ], "name": "Format added or removed star", "moduleId": "n_0c807d376aa291c3", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [] } ] } }, "n_7f93bdc488d39166": { "inputs": {}, "name": "Star was added", "kind": "condition", "matchMode": "first", "cases": [ { "output": "added", "groups": [ { "expressions": [ { "left": { "kind": "source", "source": { "kind": "node", "nodeId": "n_df47d3bdbd824375", "output": "action" } }, "operator": "==", "right": { "kind": "value", "value": "created" } } ] } ] } ] }, "n_4029754a6733afb8": { "inputs": { "channelId": { "kind": "value", "value": "YOUR_SLACK_CHANNEL_ID" }, "text": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_df47d3bdbd824375", "output": "text" } ] }, "attachments": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_df47d3bdbd824375", "output": "attachments" } ] } }, "name": "Post added star to Slack", "kind": "task", "task": { "inputs": [ { "description": "The Slack conversation or channel ID.", "jsonSchema": { "type": "string", "minLength": 1, "description": "The Slack conversation or channel ID." }, "nullable": false, "handle": "channelId" }, { "description": "Plain text message content. When blocks are provided, Slack uses this as notification and accessibility fallback text.", "jsonSchema": { "type": "string", "description": "Plain text message content. When blocks are provided, Slack uses this as notification and accessibility fallback text." }, "nullable": true, "handle": "text", "value": null }, { "description": "Slack Block Kit blocks to render in the message.", "jsonSchema": { "type": "array", "items": { "type": "object", "properties": {}, "additionalProperties": true, "description": "A Slack Block Kit block object. Pass the block exactly as Slack documents it." }, "minItems": 1, "description": "Slack Block Kit blocks to render in the message." }, "nullable": true, "handle": "blocks", "value": null }, { "description": "Slack legacy attachments to include in the message.", "jsonSchema": { "type": "array", "items": { "type": "object", "properties": {}, "additionalProperties": true, "description": "A Slack legacy attachment object. Prefer blocks for new messages when possible." }, "minItems": 1, "description": "Slack legacy attachments to include in the message." }, "nullable": true, "handle": "attachments", "value": null }, { "description": "Whether Slack should unfurl links in the message.", "jsonSchema": { "type": "boolean", "description": "Whether Slack should unfurl links in the message." }, "nullable": true, "handle": "unfurlLinks", "value": null }, { "description": "Whether Slack should unfurl media in the message.", "jsonSchema": { "type": "boolean", "description": "Whether Slack should unfurl media in the message." }, "nullable": true, "handle": "unfurlMedia", "value": null }, { "description": "Slack message metadata to attach to the message.", "jsonSchema": { "type": "object", "properties": {}, "additionalProperties": true, "description": "Slack message metadata to attach to the message." }, "nullable": true, "handle": "metadata", "value": null } ], "outputs": [ { "description": "The timestamp identifier of the posted message.", "jsonSchema": { "type": "string", "description": "The timestamp identifier of the posted message." }, "nullable": false, "handle": "ts" }, { "description": "The channel ID where the message was posted.", "jsonSchema": { "type": "string", "description": "The channel ID where the message was posted." }, "nullable": false, "handle": "channelId" } ], "name": "Post added star to Slack", "executor": { "kind": "connector", "action": "slack.post_message" } } }, "n_2399e0079c4a4b01": { "inputs": { "channelId": { "kind": "value", "value": "YOUR_SLACK_CHANNEL_ID" }, "text": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_df47d3bdbd824375", "output": "text" } ] }, "attachments": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_df47d3bdbd824375", "output": "attachments" } ] } }, "name": "Post removed star to Slack", "kind": "task", "task": { "inputs": [ { "description": "The Slack conversation or channel ID.", "jsonSchema": { "type": "string", "minLength": 1, "description": "The Slack conversation or channel ID." }, "nullable": false, "handle": "channelId" }, { "description": "Plain text message content. When blocks are provided, Slack uses this as notification and accessibility fallback text.", "jsonSchema": { "type": "string", "description": "Plain text message content. When blocks are provided, Slack uses this as notification and accessibility fallback text." }, "nullable": true, "handle": "text", "value": null }, { "description": "Slack Block Kit blocks to render in the message.", "jsonSchema": { "type": "array", "items": { "type": "object", "properties": {}, "additionalProperties": true, "description": "A Slack Block Kit block object. Pass the block exactly as Slack documents it." }, "minItems": 1, "description": "Slack Block Kit blocks to render in the message." }, "nullable": true, "handle": "blocks", "value": null }, { "description": "Slack legacy attachments to include in the message.", "jsonSchema": { "type": "array", "items": { "type": "object", "properties": {}, "additionalProperties": true, "description": "A Slack legacy attachment object. Prefer blocks for new messages when possible." }, "minItems": 1, "description": "Slack legacy attachments to include in the message." }, "nullable": true, "handle": "attachments", "value": null }, { "description": "Whether Slack should unfurl links in the message.", "jsonSchema": { "type": "boolean", "description": "Whether Slack should unfurl links in the message." }, "nullable": true, "handle": "unfurlLinks", "value": null }, { "description": "Whether Slack should unfurl media in the message.", "jsonSchema": { "type": "boolean", "description": "Whether Slack should unfurl media in the message." }, "nullable": true, "handle": "unfurlMedia", "value": null }, { "description": "Slack message metadata to attach to the message.", "jsonSchema": { "type": "object", "properties": {}, "additionalProperties": true, "description": "Slack message metadata to attach to the message." }, "nullable": true, "handle": "metadata", "value": null } ], "outputs": [ { "description": "The timestamp identifier of the posted message.", "jsonSchema": { "type": "string", "description": "The timestamp identifier of the posted message." }, "nullable": false, "handle": "ts" }, { "description": "The channel ID where the message was posted.", "jsonSchema": { "type": "string", "description": "The channel ID where the message was posted." }, "nullable": false, "handle": "channelId" } ], "name": "Post removed star to Slack", "executor": { "kind": "connector", "action": "slack.post_message" } } } }, "edges": [ { "source": "n_dbd93ced1a4e96fa", "target": "n_df47d3bdbd824375" }, { "source": "n_df47d3bdbd824375", "target": "n_7f93bdc488d39166" }, { "source": "n_7f93bdc488d39166", "target": "n_4029754a6733afb8", "sourceHandle": "added" }, { "source": "n_7f93bdc488d39166", "target": "n_2399e0079c4a4b01", "sourceHandle": "otherwise" } ] } }, "modules": { "n_0c807d376aa291c3": { "name": "Format added or removed star", "imports": [], "source": "export default function starMessage({ body }) {\n const isHttpLink = (value) => {\n const parts = typeof value === 'string' && /^https?:\\/\\/(\\[[0-9a-f:.]+\\]|[^\\s/\\\\?#@:%\\[\\]<>\"`]+)(?::(\\d{1,5}))?(?:[/?#][^\\s\\\\]*)?$/i.exec(value);\n return Boolean(parts && !/[\\u0000-\\u0020\\u007f]/.test(value) && (!parts[2] || Number(parts[2]) <= 65535));\n };\n if (!['created', 'deleted'].includes(body.action)) throw new Error('Expected a GitHub star created or deleted action');\n const total = body.repository?.stargazers_count, sender = body.sender;\n if (!Number.isSafeInteger(total) || total < 0 || !sender || typeof sender.login !== 'string' || !sender.login) throw new Error('GitHub star event is missing its count or sender');\n for (const field of ['avatar_url', 'html_url']) {\n if (typeof sender[field] !== 'string') throw new Error('GitHub sender is missing ' + field);\n if (!isHttpLink(sender[field])) throw new Error('GitHub sender URLs must use HTTP(S) without credentials');\n }\n const escape = (text) => text.replace(/&/g, '&').replace(//g, '>');\n const added = body.action === 'created';\n const title = (added ? 'Got new star from: ' : 'Star got removed by: ') + escape(sender.login);\n const message = 'The project has now: ' + total + ' Stars';\n return { action: body.action, text: title + '\\n' + message, attachments: [{ color: added ? '#88FF00' : '#ff0000', fallback: title + '. ' + message, title, title_link: sender.html_url, image_url: sender.avatar_url, text: message }] };\n}\n" } } }, "presentation": { "designer": { "version": 1, "flow": { "direction": "vertical" } } } }