This project is a Telegram bot designed to assist students with managing their academic activities. The bot provides features such as schedule notifications, grade tracking, news updates, and attendance check-ins.
- Login and Authentication: Securely log in using student credentials.
- Daily Schedule Notifications: Receive daily updates about your schedule.
- Grade Tracking: View academic performance summaries and detailed grades.
- News Updates: Stay informed with the latest news and announcements.
- Attendance Check-in: Easily check in for classes using location and codes.
- Personal Information: View detailed student information.
- Reminders: Get notified about important reminders and tasks.
src/— Telegram bot (commands, schedules, database, middleware).src/api/huceNews.tsis a HUCE-only HTML news scraper (not part of the OneUni API client).src/api/schoolApi.ts— configures the@realldz/oneuni-apiclient from environment variables.
The API client lives in a separate public repository (realldz/oneuni-api) and is consumed as a git
dependency. It is not published to any registry.
bun run typecheck # TypeScript check for the bot
bun test # bot tests (no network calls)- Bun
- SQLite3
- Docker with the Compose plugin (optional, for containerized deployment)
- Telegram Bot Token (from BotFather)
- Environment variables configured in a
.envfile (see.env.examplefor reference)
-
Clone this repository:
git clone https://github.com/realldz/huce-bot.git cd huce-bot -
Install dependencies (the API client is fetched from its public git repository):
bun install
-
Configure the environment variables:
- Copy
.env.exampleto.env:cp .env.example .env
- Fill in the required values in the
.envfile.
- Copy
-
Initialize the database:
- The bot will automatically create the necessary SQLite tables on startup.
Start the bot in development mode with hot-reloading:
bun run dev- Start the bot:
bun run startThe repository ships a two-stage Dockerfile (Bun 1) and a docker-compose.yml that reads its
configuration straight from .env.
docker compose up -d --build # build and start in the background
docker compose logs -f # follow the bot log
docker compose down # stopImages are also built on GitHub: .github/workflows/docker-publish.yml runs on every push to main, on every v* tag, and on manual dispatch, then publishes to the GitHub Container Registry as ghcr.io/realldz/huce-bot:latest (plus main, v1.2.3 and sha-<commit> tags). The image is multi-arch — linux/amd64 and linux/arm64 — so it also runs on ARM servers, and Docker picks the right one automatically. It needs no repository secrets — it authenticates with the built-in GITHUB_TOKEN, and .env, *.db, data/ and logs/ stay out of the image because of .dockerignore.
To deploy a server from the published image instead of building from source:
docker compose pull
docker compose up -d --no-buildThe published package is publicly readable, so docker compose pull needs no login. If you ever switch it to private under Packages → huce-bot → Package settings → Change visibility, authenticate first with a classic PAT that has the read:packages scope:
echo $CR_PAT | docker login ghcr.io -u realldz --password-stdinNotes:
.envis listed in.dockerignore, so secrets are injected at runtime and never baked into the image. Compose also strips the quotes aroundDEBUG='telegraf:main'for you.- SQLite lives in
./dataon the host and is mounted at/data; the real file is./data/users.db, so the data survivesdocker compose down. Compose also setsDB_PATH=/data/users.dbin the service environment, which outranksenv_file— that way a stale.envon a server cannot silently put the database inside the container filesystem, where recreating the container would wipe it. Docker creates./dataon its own, and since a directory mount expects a directory, the old trap is gone: a missing./users.dbused to be auto-created as an empty directory, which made sqlite3 fail withSQLITE_CANTOPEN. Host runs (bun run dev) readDB_PATHfrom.env, which points at the same./data/users.db. - If SQLite over a Windows bind mount reports
database is locked, replace the./data:/datamount with a named volume (huce-bot-data:/data) and declare a top-levelvolumes:key. TZ=Asia/Ho_Chi_Minhis set in the compose file becausenode-scheduleevaluates the cron expressions in the container's local time, which is UTC by default.- Long polling is the default (
WEBHOOK_DOMAINempty), so no port is published. For webhook mode, uncommentEXPOSE 3000in theDockerfile, setWEBHOOK_DOMAINandPORTin.env, uncomment theportsblock indocker-compose.yml, and put the bot behind an HTTPS reverse proxy. - Polling survives a network drop instead of going quiet. Bun swaps
require('node-fetch')for a shim over its nativefetch, which ignores the 500 s request timeout telegraf sets and reports failures as plainErrors rather thanFetchErrors, so telegraf's own retry branch never runs and a dropped connection either hangs the request for minutes or kills polling outright.src/bot/pollingResilience.tsputs an 80 s deadline on everygetUpdatesand re-classifies network errors and409 Conflictas retryable, so polling resumes about 5 s after the network returns;src/index.tsis the last-resort supervisor that relaunches polling, with a delay, if it ever dies.
Send /help for available commands.
This project is licensed under the MIT License. See the LICENSE file for details.