Ofek-bot is a Python bot that logs in to the Ofek learning website by CET, used in Israeli schools (myofek.cet.ac.il), 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 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.
- Features
- How it works
- Requirements
- Installation
- Configuration
- Notifications
- Logging
- Troubleshooting
- Security and privacy
- Known issues and limitations
- Development
- Contributing
- License
- 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.
- Loguru for logging.
- Schedule for the run schedule.
- Apprise for notifications.
- Selenium and webdriver-manager for browser automation.
- PyYAML for the configuration file.
flowchart TD
A[Start: load config/config.yaml<br/>and NOTIFIERS / SCHEDULES] --> B{Scheduled time?}
B -- no --> B
B -- yes --> C[For each kid in config]
C --> V{name, username and<br/>password all set?}
V -- no --> X[Log warning and<br/>stop this run]
V -- yes --> D[Start headless Chrome]
D --> E[Open myofek.cet.ac.il/he<br/>and click Login]
E --> F[Choose edu.gov.il SSO,<br/>switch to username/password login]
F --> G[Enter the kid's username and password]
G --> H[Read the four task counters<br/>from the dashboard]
H --> I{To do > 0 or<br/>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).
- On startup the bot loads the kids list from
config/config.yaml(relative to the working directory,/appin the container). If the file does not exist, it copies the empty templateconfig.yamlthere. The file is read once, so restart the bot after you change it. - It registers every Apprise URL from
NOTIFIERSand one daily job for every time inSCHEDULES. - 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. - 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.
- 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-manageralso 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 for notifications.
services:
ofek:
image: techblog/ofek-bot:latest
container_name: ofek
restart: always
environment:
- SCHEDULES=16:00
- NOTIFIERS=tgram://<bot_token>/<chat_id>
volumes:
- ./ofek-bot/config:/app/config- Create the config folder and the
config.yamlfile described in Configuration:mkdir -p ofek-bot/config nano ofek-bot/config/config.yaml
- Start the container:
docker compose up -d docker compose logs -f ofek
docker run -d --name ofek --restart always \
-e SCHEDULES="07:30,16:00" \
-e NOTIFIERS="tgram://<bot_token>/<chat_id>" \
-v "$(pwd)/ofek-bot/config:/app/config" \
techblog/ofek-bot:latestImages are published to Docker Hub as
techblog/ofek-bot for linux/amd64 only.
The tags are latest and the version from the 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.
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://<bot_token>/<chat_id>" # must be set, even to ""
export SCHEDULES="16:00"
python3 app.pyGoogle Chrome must be installed; webdriver-manager downloads the matching ChromeDriver.
Configuration comes from two environment variables and one YAML file. There are no command line flags.
| Variable | Required | Default | Description |
|---|---|---|---|
NOTIFIERS |
Yes, to get notifications | "" in the Docker image; unset outside Docker |
One or more Apprise URLs, 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. |
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:
kids:
- name: Kid1
username: "000000000"
password: "<password>"
- name: Kid2
username: "000000000"
password: "<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.
Set NOTIFIERS to one or more Apprise URLs separated by spaces:
NOTIFIERS="tgram://<bot_token>/<chat_id> pover://<user_key>@<app_token>"A few common examples (see the Apprise wiki for the full, current list of services and URL formats):
| Service | Example URL |
|---|---|
| Telegram | tgram://bottoken/ChatID |
| Discord | discord://webhook_id/webhook_token |
| Slack | slack://TokenA/TokenB/TokenC/Channel |
| Pushover | pover://user@token |
| Gotify | gotifys://hostname/token |
| ntfy | ntfys://hostname/topic |
| Home Assistant | hassio://hostname/accesstoken |
| Microsoft Teams | msteams://TokenA/TokenB/TokenC/ |
| MQTT | mqtt://user:pass@hostname/topic |
mailtos://user:password@gmail.com |
|
| Apprise API | apprises://hostname/Token |
A notification is sent per kid, only when the "to do" or "to fix" counter is above zero.
- Title:
מצב משימות אופק של <name>("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.
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: <name>, Scrapping...,
No tasks tbd for <name>, Setting default schedule to 16:00 (when SCHEDULES is empty),
and Setting schedule to everyday at <time>.
- Nothing happens after startup. The bot waits for the next scheduled time. Check the
Setting default schedule to 16:00orSetting schedule to everyday at <time>lines in the log and the container's time zone (seeTZ). - The container exits at startup with a schedule error. Each
SCHEDULESentry must be a validHH:MMtime, separated by commas without spaces (07:30,16:00, not07:30, 16:00). - No notifications arrive. Check that
NOTIFIERSis set, that multiple URLs are separated by spaces (not commas), and that the log shows anAdding: ...line for each URL. Test each URL with theappriseCLI. Kids list is empty or not configured. An entry inconfig.yamlhas an emptyname,username, orpassword. The bot stops at the first such entry.config.yamlis not created in the mounted folder. The container runs as the non-rootseluser. Createconfig/config.yamlyourself, or make the host folder writable by that user.- Timeout or "no such element" errors while logging in or scraping. The bot uses fixed XPaths and a 5-second wait for each step. If Ofek or the edu.gov.il login page changes its layout, these break and the code needs an update. Also check that the credentials work with the username/password login in a normal browser.
- ChromeDriver download errors.
webdriver-managerchecks for and, when not cached, downloads ChromeDriver on each run (the cache is lost when the container is recreated), so it needs internet access.
config.yamlholds your kids' Ministry of Education credentials in plain text. Keep it out of version control, restrict its permissions (for examplechmod 600), and store it only on a host you trust.- Apprise URLs usually contain API tokens. Treat
NOTIFIERSas a secret: don't commit your compose file with real values; use an.envfile or your orchestrator's secret store. - At the default
DEBUGlog level the bot logs every Apprise URL it registers (Adding: <url>) and the kids' names. Protect the container logs accordingly. - Notifications include your kid's name and task counts. Choose a private notification channel.
- The container runs Chrome as the non-root
seluserwith--no-sandbox. Don't expose it more than needed; the bot does not listen on any port.
- Repeated alerts: there is no state or de-duplication, so you get a notification at every scheduled run for as long as tasks are open.
- Fragile scraping: the scraper relies on absolute XPaths, fixed element ids and a
hard-coded SSO URL rewrite (
EduCombinedAuthSmstoEduCombinedAuthUidPwd), so any change to the Ofek website or the edu.gov.il login can break the bot. - An empty entry stops the run: an incomplete kid entry ends the run for the entries that follow it.
- Stale counters after a failed scrape: the counter values are not reset between kids. If scraping fails for a kid, the previous kid's values are kept and reported under the current kid's name.
- A scrape failure can end the run: if no counter has been read since the bot started, an
IndexErrorends the run for all remaining kids and leaves Chrome running, becausebrowser.quit()is skipped. - Configuration is read once at startup; restart the container after editing
config.yaml. - Images are built for
linux/amd64only.
Project layout:
app/
app.py # the bot: scheduler, Selenium crawler, Apprise notifications
config.yaml # empty kids template, copied to config/config.yaml on first run
Dockerfile # based on selenium/standalone-chrome; runs as seluser
requirements.txt # Python dependencies
VERSION # image version tag used by the Docker workflow
.github/workflows/docker-image.yml
Run locally as described in From source. To build the image yourself:
docker build -t ofek-bot:dev .
docker run --rm -e NOTIFIERS="" -e SCHEDULES="16:00" \
-v "$(pwd)/config:/app/config" ofek-bot:devThe Dockerfile uses selenium/standalone-chrome as a base only to get Chrome; the entrypoint
is replaced with python3 /app/app.py, so no Selenium Grid server runs in the container.
The docker GitHub Actions workflow is started manually (workflow_dispatch). It builds for
linux/amd64 and pushes techblog/ofek-bot:latest and techblog/ofek-bot:<VERSION>. Bump
VERSION before running it.
There are no automated tests.
Issues and pull requests are welcome. Since the bot depends on the Ofek website's layout, reports about login or scraping failures (with the relevant log lines, without credentials or personal data) are especially helpful.
This project is licensed under the Apache License 2.0.