Metadata-Version: 2.4
Name: cv-check
Version: 0.1.0
Summary: Checks KIET CyberVidya attendance daily and alerts when marked absent
Author: Vansh Sahay
License-Expression: MIT
Keywords: attendance,kiet,cybervidya,college,notifications,otp
Classifier: Environment :: Console
Classifier: Intended Audience :: End Users/Desktop
Classifier: Operating System :: MacOS
Classifier: Operating System :: POSIX :: Linux
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Utilities
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: requests>=2.31
Requires-Dist: pycryptodome>=3.19
Provides-Extra: dev
Requires-Dist: pytest>=8; extra == "dev"
Dynamic: license-file

# CyberVidya Attendance Checker (KIET)

> **Unofficial tool — not affiliated with KIET or CyberVidya.** It uses the same
> endpoints as the web app you log into yourself; use at your own discretion.

Logs into [kiet.cybervidya.net](https://kiet.cybervidya.net) using the OTP emailed to your
student account, fetches today's per-subject attendance, and notifies you when you've been
marked absent — via macOS notification, Telegram, and/or email.

Everything runs locally. Your credentials live only in `config.json` (mode 600) and are sent
only to the services they belong to (CyberVidya API, your Gmail, your chosen notification
channels).

## Install

```bash
pipx install cv-check          # or: pip install --user cv-check
```

Then create a config (see `config.example.json` in the repo for all options):

```bash
cv-check setup-gmail           # Gmail API OAuth for reading the OTP email (recommended)
# or set imap_app_password in config.json (app password)
```

Config is looked up in `./config.json` or `~/.config/cybervidya/config.json`.
`api_base` defaults to the KIET endpoint and can be overridden for forks.

## How the login works (reverse-engineered from the site's Angular bundle)

1. `POST /api/auth/encrypt/login` — roll number + password encrypted with AES-128-CBC.
   The key/iv are hardcoded in the site's JS (`cipherkey`/`cipheriv`), so no server-side
   tricks are needed. (Verified: output is byte-identical to the site's own requests.)
2. The API emails a 6-digit OTP to your `@kiet.edu` inbox — the tool reads it via IMAP
   (app password) or the Gmail API, and completes `POST /api/auth/verify/otp`.
3. Session: `Authorization: GlobalEducation <JWT>` + `UID: <id>` headers. The JWT lives
   ~5h, so it's cached in `session_cache.json` (mode 600) and reused.

Attendance comes from `POST /api/attendance/schedule/student/course/attendance/percentage`
per course component — it returns `lectureList[]` with `planLecDate`, `timeSlot` and
`attendance: PRESENT/ABSENT` per lecture. The student calendar
(`GET /api/student/schedule/class?weekStartDate=..&weekEndDate=..`) provides today's
schedule so lectures that are scheduled but not yet marked are reported separately.

Notes learned the hard way:
- **One active session per user**: logging in anywhere (the web UI *or* this tool) revokes
  the previous session token. The tool re-logins automatically on 401.
- `student_id` is NOT in any API response — it's the numeric id shown in the attendance
  percentage request payload in your browser devtools (config.json).
- The portal **rate-limits OTP emails** (~1/hour after several logins). The error is
  reported clearly; the cached session usually avoids the need to log in again.

## Setup

```bash
./scripts/setup.sh                 # venv + install the package + create config.json
```

Email access — pick whichever works for your account:

**A. Gmail API (OAuth) — recommended; works even where app passwords are blocked:**
```bash
# one-time Google Cloud setup (about 5 minutes):
#   1. console.cloud.google.com → create a project
#   2. APIs & Services → Library → enable "Gmail API"
#   3. OAuth consent screen → External → add your @kiet.edu as a test user
#   4. Credentials → Create credentials → OAuth client ID → "Desktop app"
#   5. Download the JSON → save as gmail_client_secret.json in this folder
cv-check setup-gmail     # opens browser; one consent click
```

**B. IMAP app password** (only if your account allows it):
```bash
# edit config.json: imap_user = your @kiet.edu, imap_app_password = App Password
# (myaccount.google.com/apppasswords — requires 2-Step Verification, and many
#  Workspace admins / Google itself now disable these)
```

Then:
```bash
cv-check verify-login --verbose   # test login once
```

## Usage

```bash
cv-check check              # login (if needed) + check today + notify
cv-check check --no-notify  # dry run
cv-check check --dump       # save raw API responses (debugging)
cv-check check --otp 123456 # supply OTP manually
cv-check today              # cached session only
cv-check watch              # background watcher (see below)
cv-check --version
```

Exit codes (useful for scripting): `0` all present / no classes · `1` error · `2` absent today.

## Background watcher (macOS, runs at login)

The recommended setup: a launchd agent that starts at login and keeps running,
polling every 10 minutes all day. It notifies **within minutes of an absence
being marked** (instead of waiting for a fixed evening time), and each absence
triggers exactly one notification per day.

```bash
./scripts/install_watch_launchd.sh
```

Installs agent `com.user.cybervidya-attendance-watch` (RunAtLoad + KeepAlive —
restarts if it crashes). Logs:
`~/Library/Logs/com.user.cybervidya-attendance-watch.{stdout,stderr}.log`

```bash
tail -f ~/Library/Logs/com.user.cybervidya-attendance-watch.stderr.log
# uninstall:
launchctl unload ~/Library/LaunchAgents/com.user.cybervidya-attendance-watch.plist && rm ~/Library/LaunchAgents/com.user.cybervidya-attendance-watch.plist
```

How OTP usage works in watch mode: polling reuses the cached session (no OTPs).
A new login — and therefore a new OTP email — happens only when the ~5h JWT
expires or a web-UI login revokes it: roughly 2–4 OTP emails per day, well
under the portal's per-hour rate limit. If the limit is ever hit, the watcher
logs it and retries next cycle without crashing.

Prefer the old one-shot daily run instead? `./scripts/install_launchd.sh [HH:MM]`
(daily Mon–Sat at 17:30 by default) still works.

## Development

```bash
.venv/bin/pip install -e '.[dev]'   # pytest
.venv/bin/python -m pytest          # 25 offline tests (crypto vectors, OTP parsing, fixtures)
```

Structure:

| module | responsibility |
|---|---|
| `cybervidya_checker/cli.py` | commands, exit codes, logging |
| `cybervidya_checker/config.py` | config loading + validation |
| `cybervidya_checker/crypto.py` | AES-128-CBC (site-compatible) |
| `cybervidya_checker/api.py` | HTTP client + typed errors (`ApiError`, `UnauthorizedError`) |
| `cybervidya_checker/auth.py` | login, OTP verify, session cache |
| `cybervidya_checker/email_reader.py` | IMAP + Gmail API OTP extraction, OAuth setup |
| `cybervidya_checker/attendance.py` | attendance endpoints, schedule cross-check |
| `cybervidya_checker/notifier.py` | macOS / Telegram / email channels |
| `tests/` | offline tests with real API fixtures (`tests/fixtures/`) |

## Files

| file | purpose |
|---|---|
| `config.json` | credentials + notification prefs (gitignore me!) |
| `session_cache.json` | cached JWT between runs (auto-created, mode 600) |
| `config.example.json` | template for config.json |
| `scripts/setup.sh`, `scripts/install_launchd.sh` | one-time setup + scheduler |

## Security notes

- Never commit `config.json` / `session_cache.json` / `gmail_*.json` — all hold secrets
  (`.gitignore` covers them).
- The portal password is stored plaintext in `config.json` (like a `.netrc`); the file is
  chmod 600. If this is shared publicly, prefer an env var or keychain integration later.
- The OTP email reader only searches for CyberVidya OTP mails (subject/from filter) — it
  does not read your general inbox.
- If the portal password or app password leaks (e.g. pasted into a chat), rotate them:
  portal password at the KIET helpdesk, app passwords at myaccount.google.com/apppasswords.
