{"id":29152855,"url":"https://github.com/profiq/slack-open-poll","last_synced_at":"2026-03-15T09:44:19.750Z","repository":{"id":297225797,"uuid":"993120007","full_name":"profiq/slack-open-poll","owner":"profiq","description":"Open Source Poll App for Slack, which anyone can deploy on their own","archived":false,"fork":false,"pushed_at":"2025-06-25T16:14:26.000Z","size":387,"stargazers_count":2,"open_issues_count":2,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-06-25T17:29:25.211Z","etag":null,"topics":["open-source","poll","slack","slack-bot"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/profiq.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2025-05-30T08:49:23.000Z","updated_at":"2025-06-25T16:10:52.000Z","dependencies_parsed_at":"2025-06-04T19:43:35.503Z","dependency_job_id":"e2166c99-9eff-465d-bc88-5f8349461ddd","html_url":"https://github.com/profiq/slack-open-poll","commit_stats":null,"previous_names":["profiq/slack-open-poll"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/profiq/slack-open-poll","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/profiq%2Fslack-open-poll","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/profiq%2Fslack-open-poll/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/profiq%2Fslack-open-poll/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/profiq%2Fslack-open-poll/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/profiq","download_url":"https://codeload.github.com/profiq/slack-open-poll/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/profiq%2Fslack-open-poll/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":262876870,"owners_count":23378139,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["open-source","poll","slack","slack-bot"],"created_at":"2025-07-01T01:03:12.672Z","updated_at":"2026-03-15T09:44:19.690Z","avatar_url":"https://github.com/profiq.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# 🗳️ Open Poll – TypeScript Open Source Slack Poll App\n\n**Open Poll** is an open-source Slack-integrated app that lets your team quickly create and run polls using a `/poll` command. Inspired by [Simple Poll](https://www.simplepoll.rocks/), Open Poll is built in **TypeScript**, runs on **Firebase Cloud Functions**, and is fully customizable for self-hosting or development learning.\n\nThe idea is that you **self-host** this application in your own Firebase instance. See the installation instructions bellow.\n\n---\n\n## 🚀 Project Goals\n\n- Build a **Slack-integrated polling bot**\n- Written in **TypeScript**\n- Containerized with **Docker**\n- Encourage learning and contributions via open-source collaboration\n- Provide a lightweight, customizable alternative to commercial polling apps\n\n---\n\n## 🧩 Key Features (Planned)\n\n| Feature                         | Status     |\n| ------------------------------ | ---------- |\n| `/poll` Slack command          | ✅ Planned |\n| Anonymous \u0026 named voting       | ✅ Planned |\n| Single \u0026 multi-choice polls    | ✅ Planned |\n| Real-time vote updates         | ✅ Planned |\n| Result summaries in thread     | ✅ Planned |\n| Scheduled expiration of polls  | ⬜ Future  |\n| Web dashboard (admin optional) | ⬜ Future  |\n\n---\n\n## 🔧 Tech Stack\n\n- **TypeScript** – Strong typing for clean and scalable code\n- **Firebase Cloud Functions** – Serverless backend API\n- **Cloud Firestore** – Real-time poll \u0026 vote persistence\n- **Slack Bolt SDK** – Slack integration \u0026 command handling\n- **ngrok / localtunnel** – For local Slack command testing\n\n---\n\n## 🚀 Deploying Open Poll\n\n\u003e 🧠 First, ensure you have [Node.js](https://nodejs.org/) and [Firebase CLI](https://firebase.google.com/docs/cli) installed.\n\n\n\n### 1. Clone the repository\n\n```bash\ngit clone https://github.com/profiq/slack-open-poll.git\ncd slack-open-poll\n```\n\n\n### 3. Create a `.env` file with your configuration\n\n- Create this file in `/functions` directory\n```env\nSLACK_SIGNING_SECRET=your_signing_secret\nSLACK_BOT_TOKEN=xoxb-your-bot-token\nNODE_ENV=development\nFIRESTORE_EMULATOR_HOST=localhost:8080\n```\nYou are going to get `SLACK_SIGNING_SECRET` and `SLACK_BOT_TOKEN` in step number 3\n\n\n### 3. First Slack App Configuration\n\n- Go to [Slack Apps](https://api.slack.com/apps)\n- Create new app **from a manifest**\n- Select your workspace\n- Use this JSON:\n```JSON\n{\n    \"display_information\": {\n        \"name\": \"Open Poll\"\n    },\n    \"features\": {\n        \"bot_user\": {\n            \"display_name\": \"Open Poll\",\n            \"always_online\": false\n        }\n    },\n    \"oauth_config\": {\n        \"scopes\": {\n            \"bot\": [\n                \"channels:history\",\n                \"chat:write\",\n                \"commands\",\n                \"groups:history\"\n            ]\n        }\n    },\n    \"settings\": {\n        \"org_deploy_enabled\": false,\n        \"socket_mode_enabled\": false,\n        \"token_rotation_enabled\": false\n    }\n}\n```\n- From the page you see copy `Signing Secret` and paste it into your .env\n- Go to **Features/OAuth \u0026 Permissions/** and click `Install to your-workspace`\n- From the same page copy `Bot User OAuth Token` and paste it into your .env as `SLACK_BOT_TOKEN`\n\n\n### 4. Set up Firebase\n- Go to [Firebase Console](https://console.firebase.google.com/)\n- Create new project\n- On the left panel at the bottom upgrade pricing plan to **Pay as you go**(you will not have to pay for anything)\n- Go to Firestore Settings and create new default database, ideally in the same region you have your Functions set in .env file\n- Now in terminal:\n```bash\nnpm install -g firebase-tools\nfirebase login\ncd functions\nnpm install\nfirebase use --add\n```\n- Choose the Firebase project you just created\n- Create alias for this project\n- If you want to change the default deploy project:\n```bash\nfirebase use\nfirebase use project-you-want-to-use\n```\n- Now deploy\n```bash\nfirebase deploy --only functions\n```\n- Then it will ask you about clean up policy - just type `1` and enter\n\n\n### 5. Finish Slack App Configuration\n- In your [Firebase Console](https://console.firebase.google.com/) project open `Build/Functions`\n- You should see **slack** function and under **Trigger** and **Request** there is URL\n- Copy this and use in your [Slack App](https://api.slack.com/apps):\n  - Open **Features/App Manifest**\n  - paste this into JSON:\n  ```JSON\n  {\n    \"display_information\": {\n        \"name\": \"Open Poll\"\n    },\n    \"features\": {\n        \"bot_user\": {\n            \"display_name\": \"Open Poll\",\n            \"always_online\": false\n        },\n        \"slash_commands\": [\n            {\n                \"command\": \"/poll\",\n                \"url\": \"INSERT-URL\",\n                \"description\": \"Creates a poll!\",\n                \"usage_hint\": \"\\\"question\\\", answer, answer OR help\",\n                \"should_escape\": false\n            }\n        ]\n    },\n    \"oauth_config\": {\n        \"scopes\": {\n            \"bot\": [\n                \"channels:history\",\n                \"chat:write\",\n                \"commands\",\n                \"groups:history\"\n            ]\n        }\n    },\n    \"settings\": {\n        \"event_subscriptions\": {\n            \"request_url\": \"INSERT-URL\",\n            \"bot_events\": [\n                \"message.channels\",\n                \"message.groups\"\n            ]\n        },\n        \"interactivity\": {\n            \"is_enabled\": true,\n            \"request_url\": \"INSERT-URL\"\n        },\n        \"org_deploy_enabled\": false,\n        \"socket_mode_enabled\": false,\n        \"token_rotation_enabled\": false\n    }\n  }\n  ```\n  - Paste your URL from Firebase Console Functions into fields with `INSERT-URL`, make sure it ends with `slack/events`it should look like \n    -  `https://europe-west3-your-project-name.cloudfunctions.net/slack/events`\n  - Click **Save Changes**\n  - Now go to **Features/OAuth \u0026 Permissions** and click `Reinstal to your-workspace`\n  - And you are done!\n\n---\n\n## Architecture\n\n### Flow chart\n```mermaid\nflowchart TD\n subgraph subGraph0[\"Firebase Function\"]\n        D[\"Slack Bolt App with ExpressReceiver\"]\n        C[\"Firebase Function\"]\n        E[\"Handle Slash Command or Interaction\"]\n        F[\"Read/Write to Firestore\"]\n        G[\"Recalculate Vote Totals\"]\n        H[\"Build Updated Slack Message\"]\n        I[\"Call chat.update to refresh Slack message\"]\n  end\n    A[\"User on Slack\"] -- Slash Command or Button Click --\u003e B[\"Slack API\"]\n    B -- HTTP Request --\u003e C\n    C --\u003e D\n    D --\u003e E\n    E --\u003e F\n    F --\u003e G\n    G --\u003e H\n    H --\u003e I\n```\n\n### Architecture Diagram\n\n```mermaid\nsequenceDiagram\n  actor User as User\n  participant Slack as Slack API\n  participant FirebaseFunction as FirebaseFunction\n  participant SlackBolt as SlackBolt\n  participant Firestore as Firestore\n  User -\u003e\u003e Slack: /createpoll or clicks button\n  Slack -\u003e\u003e FirebaseFunction: Sends payload (slash command / interaction)\n  FirebaseFunction -\u003e\u003e SlackBolt: Passes request to ExpressReceiver\n  SlackBolt -\u003e\u003e SlackBolt: Parses command or interaction\n  SlackBolt -\u003e\u003e Firestore: Store poll or update vote\n  Firestore --\u003e\u003e SlackBolt: Poll data and vote results\n  SlackBolt -\u003e\u003e Slack: chat.postMessage or chat.update with new blocks\n  Slack --\u003e\u003e User: Updated poll message with current results\n```\n\n---\n\n## Slash Command Handler\n\n- Handler processes `/poll` commands from users, validates the input, creates a poll using chat.postMessage\n- Returns the poll message with options and buttons for voting\n- Added functionality for multi-choice polls\n\n#### Funcionality\n\n- Parses poll question and options from command text\n- Adds a lot of functionality by using flags\n- Creates a poll using the PollService\n- Returns a block message in the Slack channel with the question, options and buttons\n- Handles errors if the command format is invalid or if the poll creation fails\n- When errors happen, the slash command is responded by chat.postEphemeral which is shown just to the user that tried to create the poll\n\n### Flags\n\nYou can use flags before typing the question to customise the poll usage:\n- `multiple`:\n  - creates a poll that enables having multiple options\n  - ```\n    /poll multiple \"Question\"...\n    ```\n- `limit`:\n  - upgraded multiple poll that enables maximum of votes given\n  - ```\n    /poll limit 2 \"Question\"...\n    ```\n  - creates a poll that enables maximum of 2 votes\n  - when trying to add vote above limit responds with ephemeral message\n- `custom`:\n  - creates a poll that enables users to add custom option\n  - creates a button at the bottom of the poll\n  - when clicked it opens a form where you can type your new option and submit it\n  - ```\n    /poll custom \"Question\"...\n    ```\n- `anonymous`:\n  - creates a poll that does not show votes publicly (just the amount of votes)\n  - you can still see your votes in the \"Open\" form\n  - ```\n    /poll anonymous \"Question\"...\n  ```\nTip: you can combine flags\n```\n/poll custom limit 2 \"Question\"...\n```\n\n\n### Usage and Example\n\nTemplate:\n```\n/poll \"Question?\" Option 1, Option 2, Option 3\n```\nExample:\n```\n/poll \"When would be the right time for a meeting?\" 10 AM, 2 PM, 4 PM, 6 PM \n```\n\n## Vote Handler\n\nHandler processes app.action when the vote happens\n\n### Functionality\n\n- The vote is recorded in the PollService\n- After vote is recorded, the poll display is updated with the latest vote counts using user's ID\n- If user votes again for the same option, the vote is deleted\n\nIf user votes again for different option:\n- For single choice polls (default):\n  - Previous choice gets deleted\n  - New choice is added\n- For multi-choice polls:\n  - Previous choices stay intact\n  - New choice is added to list of other choices\n\n## Open Button\n\n`Open` button in every Slack poll opens a form with poll options  \nThere are two buttons:\n- `Your Votes`:\n  - Shows your own options, which you have voted for\n- `Settings`:  \n  Only the creator of the poll can access this form  \n  Opens two buttons:\n  - `Close Poll`:\n    - Closes the poll (poll.closed)\n    - Blocks another votes after the poll is closed\n    - Posts result in the poll message thread\n    - Updates original poll message with `Voting has ended for this poll`\n    - For any other vote attempt responds with ephemeral message\n  - `Delete Poll`:\n    - Soft deletes the poll in Firestore Database - just has the flag `deleted`\n    - Deletes the original poll message\n    - Displays informative form for user about deleting the poll\n\n## Usage of Logger\n\nBasic setup:\n```ts\nimport { Logger } from './utils/logger';\nconst logger = new Logger({ requestId: 'r-123' });\nlogger.info('Logger started');\n```\nUser context:\n```ts\nloger.info('User did something', {\n  userId: 'u-id',\n  workspaceId: 'w-id',\n});\n```\nUsing withContext:\n```ts\nconst userLogger = logger.withContext({\n  userId: 'u-id',\n  workspaceId: 'w-id',\n});\nuserLogger.debug('Debugging user data');\nuserLogger.info('User joined a poll', { pollIdd: 'p-id' });\n```\nUsing logger errors:\n```ts\ntry {\n  throw new Error('Error');\n} catch (err) {\n  logger.error(err);\n}\n```\n\n---\n\n## 📚 Learning Resources\n\nIf you're new to some parts of this stack, check out these:\n\n### 🧠 Slack App Development\n\n- [Slack Bolt for JS](https://slack.dev/bolt-js/)\n- [Creating Slack Commands](https://api.slack.com/interactivity/slash-commands)\n- [Slack App Manifest Templates](https://api.slack.com/reference/manifests)\n\n### 🔥 TypeScript \u0026 Node\n\n- [TypeScript Handbook](https://www.typescriptlang.org/docs/handbook/intro.html)\n- [Node.js + Express.js Crash Course](https://developer.mozilla.org/en-US/docs/Learn/Server-side/Express_Nodejs)\n\n### 🐳 Docker \u0026 DevOps\n\n- [Docker for Node.js Projects](https://nodejs.org/en/docs/guides/nodejs-docker-webapp/)\n- [Docker Compose Basics](https://docs.docker.com/compose/)\n\n---\n\n## 🤝 Contributing\n\nWe welcome contributions to Open Poll!\n\n1. Fork the repo\n2. Create a new feature branch\n3. Make your changes\n4. Submit a PR\n\nPlease follow the [Conventional Commits](https://www.conventionalcommits.org/) style for commit messages.\n\n---\n\n## 📌 Roadmap (Milestones)\n\n- ⬜ Slack command parsing \u0026 payload verification\n- ⬜ Docker dev environment\n- ⬜ Poll creation \u0026 vote storage (MongoDB)\n- ⬜ Vote interaction UI\n- ⬜ Web admin dashboard\n- ⬜ OAuth installation flow for multi-workspace support\n\n---\n\n## 📜 License\n\nMIT – free to use, modify, and distribute.\n\n---\n\nMade with ❤️ by profiq.\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fprofiq%2Fslack-open-poll","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fprofiq%2Fslack-open-poll","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fprofiq%2Fslack-open-poll/lists"}