{"id":20879018,"url":"https://github.com/hacksoftware/europython-2021-training","last_synced_at":"2025-08-12T10:13:31.460Z","repository":{"id":48454359,"uuid":"389108267","full_name":"HackSoftware/EuroPython-2021-Training","owner":"HackSoftware","description":"Repository to hold materials for the \"Building a practical Slack bot with Python \u0026 FastAPI\" training.","archived":false,"fork":false,"pushed_at":"2021-07-26T07:20:26.000Z","size":26,"stargazers_count":10,"open_issues_count":1,"forks_count":7,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-08-10T05:37:01.171Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Python","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/HackSoftware.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2021-07-24T13:42:04.000Z","updated_at":"2023-01-14T16:02:27.000Z","dependencies_parsed_at":"2022-08-24T05:30:37.496Z","dependency_job_id":null,"html_url":"https://github.com/HackSoftware/EuroPython-2021-Training","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/HackSoftware/EuroPython-2021-Training","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/HackSoftware%2FEuroPython-2021-Training","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/HackSoftware%2FEuroPython-2021-Training/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/HackSoftware%2FEuroPython-2021-Training/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/HackSoftware%2FEuroPython-2021-Training/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/HackSoftware","download_url":"https://codeload.github.com/HackSoftware/EuroPython-2021-Training/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/HackSoftware%2FEuroPython-2021-Training/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":270041536,"owners_count":24516868,"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","status":"online","status_checked_at":"2025-08-12T02:00:09.011Z","response_time":80,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":[],"created_at":"2024-11-18T07:14:56.249Z","updated_at":"2025-08-12T10:13:31.436Z","avatar_url":"https://github.com/HackSoftware.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# EuroPython 2021 Training\n\nThis is the repository with materials for the [Building a practical Slack bot with Python \u0026 FastAPI\n](https://ep2021.europython.eu/talks/9xzPSHe-building-a-practical-slack-bot-with-python-fastapi-training/) training.\n\n[Slides can be found here](https://docs.google.com/presentation/d/1h3mHULQQrtyvmiTPuneqhpEH3l9yzpLIgL-r_bUCHSg/edit?usp=sharing)\n\n## Overview\n\n![Slack Chat Bot@2x](https://user-images.githubusercontent.com/387867/126901049-4a965bad-41b4-4168-8ba7-05cc4322dee3.png)\n\n## Step 0 - OS setup\n\nThis training is using the following OS tools:\n\n1. `curl` - for making HTTP calls.\n1. `jq` - for pretty printing JSON results.\n\nBut in case you don't have them, it's not a problem and we don't need them for the final solution.\n\n## Step 1 - Python setup\n\nTo run everything from here, we recommend the following Python setup:\n\n1. Use the latest Python version (`3.9.6` by the time of writing).\n1. Create a fresh virtual environment for the training.\n1. Create a fresh directory to contain everything for this training.\n\nTo achieve the points above, use the tools that you use everyday.\n\nWe recommend using:\n\n1. [pyenv](https://github.com/pyenv/pyenv)\n1. [pyenv-virtualenv](https://github.com/pyenv/pyenv-virtualenv)\n\n## Step 2 - Slack setup\n\nWe are going to need a test Slack workspace for this training.\n\nThe best approach here is to create a brand new one, but feel free to reuse already existing one.\n\n## Step 3 - FastAPI setup\n\nNow, we are going to create one small FastAPI app and expose an endpoint to test with.\n\nFirst, we need to install the dependencies:\n\n```\npip install fastapi\npip install uvicorn[standard] # or uvicorn\\[standard\\] depending on your shell\n```\n\nThen, in the working directory, create a file called `main.py` with the following contents:\n\n```python\nfrom fastapi import Body, FastAPI, Request, Response\n\napp = FastAPI()\n\n\n@app.post(\"/echo\")\nasync def echo(request: Request, response: Response, data=Body(...)):\n    raw_body = await request.body()\n    body = raw_body.decode(\"utf-8\")\n\n    print(data)\n\n    return {\n        \"data\": data,\n        \"raw_body\": body,\n        \"headers\": request.headers\n    }\n```\n\nNow, lets run our server:\n\n```\nuvicorn main:app --reload\n```\n\nAnd test it by issuing a POST request via `curl` (or any other HTTP client that you find suitable):\n\n```bash\ncurl -s -X POST -H \"Content-Type: application/json\" -d '{\"key\": \"value\"}' http://localhost:8000/echo\n```\n\nFew things to note:\n\n1. We are using `-s` for a \"silent\" curl output, skipping the progress bar.\n1. We are sending the `Content-Type` header, set to `application/json`, because this triggers FastAPI to parse the JSON into the `body` argument.\n\nAn example response would look like that:\n\n```json\n{\n  \"data\": {\n    \"key\": \"value\"\n  },\n  \"raw_body\": \"{\\\"key\\\": \\\"value\\\"}\",\n  \"headers\": {\n    \"host\": \"localhost:8000\",\n    \"user-agent\": \"curl/7.58.0\",\n    \"accept\": \"*/*\",\n    \"content-type\": \"application/json\",\n    \"content-length\": \"16\"\n  }\n}\n```\n\n## Step 4 - Slack app setup\n\nNow, we are going to setup an app within our new Slack workspace, so we can start communicating with our server.\n\n1. Navigate to \u003chttps://api.slack.com/\u003e\n1. Click on the `Create an app` button\n1. Click on the `Create New App` button\n1. Select `From scratch`\n1. Type `EuroPython Bot` in the `App Name` field\n1. Select your newly created workspace\n\nAfter following those steps, you should end up with a screen that looks something like that:\n\n![image](https://user-images.githubusercontent.com/387867/126795187-59ed6d31-e0a2-4ebe-9713-727a525b4762.png)\n\nBefore we continue further, we'll need 1 more thing.\n\n## Step 5 - ngrok setup\n\nSince we are going to listen for events from Slack \u0026 those events are going to be HTTP POST requests, we need a way to tell Slack how to find our `localhost:8000` server.\n\nOne way of doing this is using [`ngrok`](https://ngrok.com/) - a piece of software that's going to create a tunnel to our localhost \u0026 give us a public url, that we'll post in Slack.\n\nNavigate to the [download page](https://ngrok.com/download) and download ngrok for you OS \u0026 platform.\n\nOnce extracted, in a new shell, while our FastAPI server is running, type:\n\n```\n./ngrok http 8000\n```\n\nThis will run `ngrok` and present us with the public url. Copy that url.\n\n## Step 6 - Testing if we have integrated correctly\n\nNow, back to Slack setup:\n\n1. Go to the `Event Subscriptions` option.\n1. Turn it on.\n1. Paste your `ngrok` url, pointing to the `/echo` endpoint. In my case, that's `https://c153a68641fd.ngrok.io/echo`\n1. If our server \u0026 `ngrok` are running, you'll see a green `Verified` in Slack. Check the `ngrok` shell - you should see a request there.\n1. Now click on `Subscribe to bot events`, click `Add Bot User Event` and select `app_mention`.\n1. Hit `Save Changes`.\n1. Leave the page open, since we'll come back here to add additional things.\n\nNow, lets wire everything together, so we can start developing:\n\n1. In the app settings, navigate to `Settings` -\u003e `Basic Information`\n1. Click the `Install to Workspace` button.\n1. Click `Allow`.\n1. Every time we change the permissions or scopes, we'll have to redo this entire process.\n\nNow, open up the Slack workspace:\n\n1. Add the bot (type `/add` in the channel \u0026 click on the action item) to a random channel.\n1. `@` the bot and write something.\n1. Check your FastAPI console.\n\nThe event payload that we received should look something like that:\n\n```json\n{\n    \"token\": \"C2lhacRaergBLDrNglaNtVYQ\",\n    \"team_id\": \"T028KF61U7R\",\n    \"api_app_id\": \"A0290D2N9B4\",\n    \"event\": {\n        \"client_msg_id\": \"7f025cc5-2f1c-41c9-9ad9-f78d86c13b49\",\n        \"type\": \"app_mention\",\n        \"text\": \"\u003c@U0290LLS803\u003e hello :wave:\",\n        \"user\": \"U028TEER6KY\",\n        \"ts\": \"1627050528.001000\",\n        \"team\": \"T028KF61U7R\",\n        \"blocks\": [\n            {\n                \"type\": \"rich_text\",\n                \"block_id\": \"UBr7f\",\n                \"elements\": [\n                    {\n                        \"type\": \"rich_text_section\",\n                        \"elements\": [\n                            {\n                                \"type\": \"user\",\n                                \"user_id\": \"U0290LLS803\"\n                            },\n                            {\n                                \"type\": \"text\",\n                                \"text\": \" hello \"\n                            },\n                            {\n                                \"type\": \"emoji\",\n                                \"name\": \"wave\"\n                            }\n                        ]\n                    }\n                ]\n            }\n        ],\n        \"channel\": \"C028TEHTWKG\",\n        \"event_ts\": \"1627050528.001000\"\n    },\n    \"type\": \"event_callback\",\n    \"event_id\": \"Ev029Q5P36BA\",\n    \"event_time\": 1627050528,\n    \"authorizations\": [\n        {\n            \"enterprise_id\": null,\n            \"team_id\": \"T028KF61U7R\",\n            \"user_id\": \"U0290LLS803\",\n            \"is_bot\": true,\n            \"is_enterprise_install\": false\n        }\n    ],\n    \"is_ext_shared_channel\": false,\n    \"event_context\": \"3-app_mention-T028KF61U7R-A0290D2N9B4-C028TEHTWKG\"\n}\n```\n\nThis means we are now ready with our setup and can start adding funcitonality back.\n\n## Step 7 - Implementing bot behavior (TASKS START HERE)\n\nNow, it's time for our tasks.\n\n**We want to implement the following general behavior:**\n\n1. Whenever someone mentions our bot, we want to reply in the channel with a message.\n1. Whenever someone mentions our bot **in a thread**, we want to reply in the same thread with a message.\n1. **[BONUS TASK]** Whenever someone mentions our bot, start a new thread by replying to that user.\n\nIn order to do that, we want to be making HTTP calls to the Slack API via `requests`, so we need to do:\n\n```\npip install requests\n```\n\n### Documentation links to help\n\nExtracted documentation links to help you navigate to the proper stuff to look at:\n\n1. [What is a Slack app?](https://api.slack.com/authentication/basics#start)\n1. [Listening to the `app_mention` event](https://api.slack.com/events/app_mention)\n1. [Sending messages](https://api.slack.com/messaging/sending)\n1. [Retrieving individual messages](https://api.slack.com/messaging/retrieving#individual_messages)\n\n\n### Hints\n\nMaking a call to Slack requires obtaining `Bot User OAuth Token` from the settings page:\n\n![europython](https://user-images.githubusercontent.com/387867/126901159-808b77db-04f2-4b3d-85d0-e6a851155959.png)\n\nSince this is a secret, in order to manage secrets for our app, we recommend using `.env` file and \u003chttps://github.com/theskumar/python-dotenv\u003e for parsing it:\n\n```python\nfrom dotenv import dotenv_values\n\nconfig = dotenv_values(\".env\")  # config = {\"USER\": \"foo\", \"EMAIL\": \"foo@example.org\"}\n```\n\n## Step 8 - Validate Slack requests\n\nEverything is great, but our API is public, meaning anyone can call it and start sending messages to our Slack.\n\n**We want to prevent this \u0026 your next task is to implement request verification!**\n\n### Documentation links to help\n\n1. [Verifying requests from Slack](https://api.slack.com/authentication/verifying-requests-from-slack)\n\n\n## Step 9 - Wrapping it up and further references\n\nThat's about it.\n\nMaterials for further references:\n\n1. Since we are using FastAPI with `async`, you can go \u0026 replace `requests` with something that's async, like \u003chttps://docs.aiohttp.org/en/stable/\u003e\n1. There's an offical [Python Slack SDK](https://github.com/slackapi/python-slack-sdk) that you can use.\n1. You can also use [`bolt-python`](https://github.com/slackapi/bolt-python) which is a framework for building Slack apps.\n1. [The official documentation, of course](https://api.slack.com/)\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhacksoftware%2Feuropython-2021-training","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhacksoftware%2Feuropython-2021-training","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhacksoftware%2Feuropython-2021-training/lists"}