# Ofek-bot Ofek-bot is a Python bot that logs in to the **Ofek** learning website by CET, used in Israeli schools ([myofek.cet.ac.il](https://myofek.cet.ac.il/he)), on behalf of each of your kids. It signs in through the Ministry of Education's SSO (edu.gov.il), reads their task counters, and sends you a notification through [Apprise](https://github.com/caronc/apprise) when a kid has tasks to complete or fix. It runs on a daily schedule inside a Docker container with a headless Chrome browser driven by Selenium. > [!IMPORTANT] > **Unofficial project.** Ofek-bot is not affiliated with, endorsed by, or supported by Ofek, > CET, the Israeli Ministry of Education, or any school. It automates a normal browser login > with credentials you provide. You are responsible for making sure your use complies with the > website's terms of use and your school's policies. ## Table of contents - [Features](#features) - [How it works](#how-it-works) - [Requirements](#requirements) - [Installation](#installation) - [Configuration](#configuration) - [Notifications](#notifications) - [Logging](#logging) - [Troubleshooting](#troubleshooting) - [Security and privacy](#security-and-privacy) - [Known issues and limitations](#known-issues-and-limitations) - [Development](#development) - [Contributing](#contributing) - [License](#license) ## Features - Monitors the Ofek task status of any number of kids, each with their own login. - Logs in through the edu.gov.il SSO using the username/password option. - Reads four counters from the Ofek dashboard: tasks to do, tasks to fix, checked tasks, and tasks waiting for review. - Notifies only when there is something to do (the "to do" or "to fix" counter is above zero). - Runs on one or more daily schedules (default: 16:00). - Supports many notification channels (Telegram, Discord, Slack, email, Pushover, Home Assistant, and more), thanks to [Apprise](https://github.com/caronc/apprise). ### Components and frameworks - [Loguru](https://pypi.org/project/loguru/) for logging. - [Schedule](https://pypi.org/project/schedule/) for the run schedule. - [Apprise](https://github.com/caronc/apprise) for notifications. - [Selenium](https://selenium-python.readthedocs.io/) and [webdriver-manager](https://pypi.org/project/webdriver-manager/) for browser automation. - [PyYAML](https://pypi.org/project/PyYAML/) for the configuration file. ## How it works ```mermaid flowchart TD A[Start: load config/config.yaml
and NOTIFIERS / SCHEDULES] --> B{Scheduled time?} B -- no --> B B -- yes --> C[For each kid in config] C --> V{name, username and
password all set?} V -- no --> X[Log warning and
stop this run] V -- yes --> D[Start headless Chrome] D --> E[Open myofek.cet.ac.il/he
and click Login] E --> F[Choose edu.gov.il SSO,
switch to username/password login] F --> G[Enter the kid's username and password] G --> H[Read the four task counters
from the dashboard] H --> I{To do > 0 or
to fix > 0?} I -- yes --> J[Send Apprise notification] I -- no --> K[Log: no tasks] J --> L[Close browser] K --> L L --> C ``` The diagram shows the normal path. If a scrape fails, the error is logged and the previous counter values are kept. If no counter has been read since the bot started, the counter check raises an error that ends the run for all remaining kids (see [Known issues and limitations](#known-issues-and-limitations)). 1. On startup the bot loads the kids list from `config/config.yaml` (relative to the working directory, `/app` in the container). If the file does not exist, it copies the empty template `config.yaml` there. The file is read **once**, so restart the bot after you change it. 2. It registers every Apprise URL from `NOTIFIERS` and one daily job for every time in `SCHEDULES`. 3. At each scheduled time, for every kid, it starts a fresh headless Chrome session (incognito, `--no-sandbox`, `--disable-dev-shm-usage`), logs in to Ofek through the edu.gov.il SSO, and reads the text of the four counters on the dashboard. 4. It takes the first number in the "to do" and "to fix" counters. If either is greater than zero, it sends a notification; otherwise it logs that there are no tasks. There is no stored state or de-duplication: as long as a kid has open tasks, you get a notification at **every** scheduled run. ## Requirements - Docker (recommended), **or** Python 3 with Google Chrome installed locally. - Outbound internet access to `myofek.cet.ac.il`, the edu.gov.il login pages, and your notification services. `webdriver-manager` also downloads a matching ChromeDriver at run time, so it needs internet access too. - An Ofek (edu.gov.il) **username and password** for each kid. The bot uses the username/password login, not the SMS login. - At least one [Apprise URL](https://github.com/caronc/apprise/wiki) for notifications. ## Installation ### Docker Compose (recommended) ```yaml services: ofek: image: techblog/ofek-bot:latest container_name: ofek restart: always environment: - SCHEDULES=16:00 - NOTIFIERS=tgram:/// volumes: - ./ofek-bot/config:/app/config ``` 1. Create the config folder and the `config.yaml` file described in [Configuration](#configuration): ```bash mkdir -p ofek-bot/config nano ofek-bot/config/config.yaml ``` 2. Start the container: ```bash docker compose up -d docker compose logs -f ofek ``` ### Docker ```bash docker run -d --name ofek --restart always \ -e SCHEDULES="07:30,16:00" \ -e NOTIFIERS="tgram:///" \ -v "$(pwd)/ofek-bot/config:/app/config" \ techblog/ofek-bot:latest ``` ### Published images Images are published to Docker Hub as [`techblog/ofek-bot`](https://hub.docker.com/r/techblog/ofek-bot) for `linux/amd64` only. The tags are `latest` and the version from the [`VERSION`](VERSION) file. There are no GitHub Releases. > [!NOTE] > At the time of writing, the newest tag on Docker Hub is `3.2.1` (September 2024), which is > also `latest`. The current `VERSION` file says `3.3.1`, and the Dockerfile on `main` (based > on `selenium/standalone-chrome`) has not been published yet. Until a new image is built, > `latest` is built from an older Dockerfile. ### From source ```bash git clone https://github.com/t0mer/ofek-bot.git cd ofek-bot pip3 install -r requirements.txt cd app # the bot reads config/config.yaml relative to this folder mkdir -p config cp config.yaml config/config.yaml nano config/config.yaml # add your kids export NOTIFIERS="tgram:///" # must be set, even to "" export SCHEDULES="16:00" python3 app.py ``` Google Chrome must be installed; `webdriver-manager` downloads the matching ChromeDriver. ## Configuration Configuration comes from two environment variables and one YAML file. There are no command line flags. ### Environment variables | Variable | Required | Default | Description | | -------- | -------- | ------- | ----------- | | `NOTIFIERS` | Yes, to get notifications | `""` in the Docker image; unset outside Docker | One or more [Apprise URLs](#notifications), separated by **spaces**. If empty, the bot still checks Ofek but sends nothing. Outside Docker it must be set (even to an empty string), or the bot exits on startup. | | `SCHEDULES` | No | `16:00` | Daily run times in 24-hour `HH:MM` format, separated by commas **without spaces**, for example `07:30,16:00`. Times use the container's local time zone. | | `TZ` | No | `UTC` (from the `selenium/standalone-chrome` base image) | Time zone for `SCHEDULES`, for example `Asia/Jerusalem`. The `selenium/standalone-chrome` base image supports it; the bot itself does not read it. | ### `config/config.yaml` The kids list is stored in `/app/config/config.yaml` inside the container. Mount a host folder to `/app/config` so the file survives container updates. | Key | Required | Description | | --- | -------- | ----------- | | `kids` | Yes | List of kids to check. | | `kids[].name` | Yes | Display name. Used in the notification title and the logs only. | | `kids[].username` | Yes | The kid's edu.gov.il username (usually the ID number). Numbers are converted to strings. | | `kids[].password` | Yes | The kid's edu.gov.il password. | Example: ```yaml kids: - name: Kid1 username: "000000000" password: "" - name: Kid2 username: "000000000" password: "" ``` Quote usernames with leading zeros (`"012345678"`), or YAML may misread them as numbers (including octal) and change the value. All three keys are required for every entry. When the bot reaches an entry with an empty `name`, `username`, or `password`, it logs `Kids list is empty or not configured` and **stops processing the rest of the list** for that run. Remove unused entries instead of leaving them blank. ## Notifications Set `NOTIFIERS` to one or more Apprise URLs separated by spaces: ```bash NOTIFIERS="tgram:/// pover://@" ``` A few common examples (see the [Apprise wiki](https://github.com/caronc/apprise/wiki) for the full, current list of services and URL formats): | Service | Example URL | | ------- | ----------- | | [Telegram](https://github.com/caronc/apprise/wiki/Notify_telegram) | `tgram://bottoken/ChatID` | | [Discord](https://github.com/caronc/apprise/wiki/Notify_discord) | `discord://webhook_id/webhook_token` | | [Slack](https://github.com/caronc/apprise/wiki/Notify_slack) | `slack://TokenA/TokenB/TokenC/Channel` | | [Pushover](https://github.com/caronc/apprise/wiki/Notify_pushover) | `pover://user@token` | | [Gotify](https://github.com/caronc/apprise/wiki/Notify_gotify) | `gotifys://hostname/token` | | [ntfy](https://github.com/caronc/apprise/wiki/Notify_ntfy) | `ntfys://hostname/topic` | | [Home Assistant](https://github.com/caronc/apprise/wiki/Notify_homeassistant) | `hassio://hostname/accesstoken` | | [Microsoft Teams](https://github.com/caronc/apprise/wiki/Notify_msteams) | `msteams://TokenA/TokenB/TokenC/` | | [MQTT](https://github.com/caronc/apprise/wiki/Notify_mqtt) | `mqtt://user:pass@hostname/topic` | | [Email](https://github.com/caronc/apprise/wiki/Notify_email) | `mailtos://user:password@gmail.com` | | [Apprise API](https://github.com/caronc/apprise/wiki/Notify_apprise_api) | `apprises://hostname/Token` | ### Message format A notification is sent per kid, only when the "to do" or "to fix" counter is above zero. - **Title:** `מצב משימות אופק של ` ("Ofek task status of <name>"). - **Body:** the text of the four dashboard counters exactly as Ofek shows them (in Hebrew), one per line, in this order: to do, to fix, checked, waiting. ## Logging The bot logs to standard error with Loguru's default format at `DEBUG` level. The level is not configurable. View the logs with `docker logs -f ofek`. Useful messages: `Loading kids list`, `Getting tasks for: `, `Scrapping...`, `No tasks tbd for `, `Setting default schedule to 16:00` (when `SCHEDULES` is empty), and `Setting schedule to everyday at