{"id":21489595,"url":"https://github.com/kooznitsa/utility_bot","last_synced_at":"2026-04-13T00:41:47.345Z","repository":{"id":198716753,"uuid":"701380886","full_name":"kooznitsa/utility_bot","owner":"kooznitsa","description":"Telegram bot notifying about water and electricity outages","archived":false,"fork":false,"pushed_at":"2025-02-06T07:42:40.000Z","size":453,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-05-27T00:11:21.836Z","etag":null,"topics":["aiogram","celery","fastapi","postgresql","python","redis"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/kooznitsa.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"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}},"created_at":"2023-10-06T14:14:43.000Z","updated_at":"2025-02-06T07:42:44.000Z","dependencies_parsed_at":"2023-10-10T16:01:04.282Z","dependency_job_id":"12e152e7-cc26-4da5-85a7-3187e373a5cd","html_url":"https://github.com/kooznitsa/utility_bot","commit_stats":null,"previous_names":["kooznitsa/utility_bot"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/kooznitsa/utility_bot","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kooznitsa%2Futility_bot","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kooznitsa%2Futility_bot/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kooznitsa%2Futility_bot/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kooznitsa%2Futility_bot/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kooznitsa","download_url":"https://codeload.github.com/kooznitsa/utility_bot/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kooznitsa%2Futility_bot/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31735541,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-12T22:19:12.206Z","status":"ssl_error","status_checked_at":"2026-04-12T22:18:33.088Z","response_time":58,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"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":["aiogram","celery","fastapi","postgresql","python","redis"],"created_at":"2024-11-23T14:22:12.575Z","updated_at":"2026-04-13T00:41:47.324Z","avatar_url":"https://github.com/kooznitsa.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Utility bot\r\n\r\n![Static Badge](https://img.shields.io/badge/production-finished-blue)\r\n\r\nA Telegram bot that sends updates on water and electricity disruptions/outage in Novi Sad and its vicinity (Serbia).\r\n\r\n\u003c!---- Telegram address: @ns_utility_bot. --\u003e\r\n\r\n## Tech stack\r\n\r\n\u003cimg src=\"https://img.shields.io/badge/FastAPI-fc884d?style=for-the-badge\u0026logo=fastapi\u0026logoColor=black\"/\u003e \u003cimg src=\"https://img.shields.io/badge/Redis-fc884d?style=for-the-badge\u0026logo=Redis\u0026logoColor=black\"/\u003e \u003cimg src=\"https://img.shields.io/badge/Celery-fc884d?style=for-the-badge\"/\u003e \u003cimg src=\"https://img.shields.io/badge/PostgreSQL-f5df66?style=for-the-badge\u0026logo=PostgreSQL\u0026logoColor=black\"/\u003e \u003cimg src=\"https://img.shields.io/badge/AsyncIO-65a362?style=for-the-badge\u0026logo=AsyncIO\u0026logoColor=black\"/\u003e \u003cimg src=\"https://img.shields.io/badge/aiogram-65a362?style=for-the-badge\u0026logo=aiogram\u0026logoColor=black\"/\u003e\r\n\r\n## App structure\r\n\r\n- API (FastAPI): \r\n  - Scrapes latest articles from https://gradskeinfo.rs/kategorija/servisne-info/\r\n  - Sends data to the database and endpoints\r\n- Bot (aiogram):\r\n  - Sends a new user to the API endpoint /users\r\n  - Sends districts chosen by the user to the API endpoint /users/{user_id}/districts\r\n  - Receives user articles from the API endpoint /users/{user_id}/articles\r\n  - Sends messages to users on schedule\r\n\r\n## Database structure\r\n\r\n![Database structure](https://raw.githubusercontent.com/kooznitsa/utility_bot/main/api/database/db_diagram.png)\r\n\r\n## API endpoints\r\n\r\n| Method      | Endpoint                     | Description                 |\r\n|-------------|------------------------------|-----------------------------|\r\n| ---GENERAL  | /\t                           |                             |\r\n| GET\t        | /\t                           | Root                        |\r\n| GET\t        | /docs\t                       | Documentation               |\r\n| ---ARTICLES | /api/articles                | Articles                    | \r\n| GET         | /                            | Get all articles            |\r\n| GET         | ?district={d1}\u0026district={d2} | Get articles with districts |\r\n| POST        | /                            | Create an article           |\r\n| DELETE      | /{article_id}                | Delete an article           |\r\n| ---USERS\t   | /api/users                   | Users                       |\r\n| GET\t        | /                            | Get all users               |\r\n| POST\t       | / \t                          | Create a user               |\r\n| GET\t        | /{user_id}\t                  | Get a user                  |\r\n| DELETE\t     | /{user_id}\t                  | Delete a user               |\r\n| POST\t       | /{user_id}/districts\t        | Create a user district      |\r\n| POST\t       | /{user_id}districts/delete\t  | Delete all user districts   |\r\n| GET\t        | /{user_id}/articles\t         | Get user articles           |\r\n\r\n## Commands\r\n\r\n### With Docker\r\n\r\n1. Create network: ```docker network create my-net```\r\n\r\n2. Build Docker containers: ```docker-compose up -d --build```\r\n\r\n3. Run Alembic migrations:\r\n  - Initialize Alembic: ```docker exec -it fastapi_service poetry run alembic init -t async migrations```\r\n  - Generate a migration file: ```docker exec -it fastapi_service poetry run alembic revision --autogenerate -m \"init\"```\r\n  - Apply the migration: ```docker exec -it fastapi_service poetry run alembic upgrade head```\r\n\r\nAPI will be available at http://127.0.0.1:8000/docs.\r\n\r\n4. Other useful commands:\r\n  - Display a table: ```docker exec -it db_postgres psql -U postgres utility_db -c \"SELECT * FROM public.articles\"```\r\n  - Remove Docker containers: ```docker-compose down``` or bring down the existing containers and volumes: ```docker-compose down -v```\r\n\r\n### Without Docker\r\n\r\n**1. Edit .env**\r\n```\r\n| With Docker                                            | Without Docker                               |\r\n|--------------------------------------------------------|----------------------------------------------|\r\n| POSTGRES_SERVER=db_postgres                            | POSTGRES_SERVER=localhost                    |\r\n| CELERY_BROKER_URL=redis://redis:6379                   | CELERY_BROKER_URL=redis://127.0.0.1:6379     |\r\n| CELERY_RESULT_BACKEND=redis://redis:6379               | CELERY_RESULT_BACKEND=redis://127.0.0.1:6379 |\r\n| REDIS_URL=redis://redis:6379                           | REDIS_URL=redis://127.0.0.1:6379             |\r\n| GW_ROOT_URL=fastapi_service://fastapi_service:8000/api | GW_ROOT_URL=http://127.0.0.1:8000/api        |\r\n```\r\n\r\nNOTE: For production with Docker use ```GW_ROOT_URL=http://fastapi_service:8000/api```.\r\n\r\n**2. Create utility_db database (PostgreSQL 16)**\r\n\r\n**3. Start Redis server in Ubuntu terminal**\r\n```\r\n# Once:\r\nsudo add-apt-repository universe\r\nsudo apt install redis\r\nsudo service redis-service restart\r\n\r\n# Always:\r\nsudo service redis-server start\r\nsudo service redis-server status\r\n# Status: \"Ready to accept connections\"\r\nredis-cli\r\n```\r\n\r\n**4. Start uvicorn server (API)**\r\n```\r\ncd api\r\n\r\npy -m venv venv\r\nvenv\\Scripts\\activate\r\n# If venv\\Scripts\\Activate.ps1 cannot be loaded because running scripts is disabled on this system, run:\r\n# Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy Unrestricted\r\n\r\npy -m pip install poetry\r\npy -m poetry install\r\n\r\npy -m uvicorn main:app --host 127.0.0.1 --port 8000 --reload\r\n```\r\n\r\nAPI will be available at http://127.0.0.1:8000/docs.\r\n\r\n**5. Run Alembic migrations**\r\n- Initialize Alembic: ```python -m alembic init -t async migrations```\r\n- Generate a migration file: ```python -m alembic revision --autogenerate -m \"init\"```\r\n- Or, with existing migrations: ```python -m alembic stamp head```\r\n- Apply the migrations: ```python -m alembic upgrade head```\r\n\r\n**6. Launch bot (separate terminal tab)**\r\n```\r\ncd bot\r\n\r\npy -m venv venv\r\nvenv\\Scripts\\activate\r\n\r\npy -m pip install poetry\r\npy -m poetry install\r\n\r\npy main.py\r\n```\r\n\r\n**7. Launch Celery (separate terminal tabs)**\r\n```\r\n# Tab1: \r\ncd api\r\nvenv\\Scripts\\activate\r\npy -m celery -A parser.tasks worker --pool=solo -l info -Q main-queue -c 1\r\n\r\n# Tab2:\r\ncd api\r\nvenv\\Scripts\\activate\r\npy -m celery -A parser.tasks beat --loglevel=info\r\n```\r\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkooznitsa%2Futility_bot","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkooznitsa%2Futility_bot","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkooznitsa%2Futility_bot/lists"}