# itshenrywu/mcut-menu-bot architecture

> Architecture diagram of the GitHub repository itshenrywu/mcut-menu-bot, generated by GitDiagram from its file tree, README and a sample of source files (last updated 2026-06-13). It is an AI-made overview: check details against the source.

- Interactive diagram: https://gitdiagram.com/itshenrywu/mcut-menu-bot
- Repository: https://github.com/itshenrywu/mcut-menu-bot

## Overview

1. This repository is a LINE-based menu bot for National Chung Hsing University? Wait, actually 明志科技大學（MCUT）學餐菜單機器人. Its core purpose is to answer menu queries for campus dining, with a public API and a chat bot as the main user-facing entry points.

2. The main runtime entry point is `bot.js`. That is the interactive layer that likely handles LINE webhook events, user preferences, and menu lookup responses. The README’s LINE add-friend link indicates the bot is exposed through a LINE Official Account rather than a web UI.

3. Menu data is not authored in this repo; it is ingested from the school’s external site `http://elder.mcut.edu.tw/website1/`. The `crawler/` directory contains the domain-specific scrapers:
   - `menu.js` for regular meal menus
   - `snack.js` for snack/light meal menus
   - `news.js` for announcements/news
   - `working-day.js` for workday/calendar logic

4. A key architectural boundary is between “live interaction” and “data collection.” The bot consumes already-prepared menu data, while the crawler scripts periodically refresh that data from the upstream school site and calendar source. This keeps chat responses fast and decouples the bot from scraping latency.

5. The repository also exposes a public menu API, documented in the README as:
   `https://mcut-menu-api.henrywu.tw/{YYYY}/{MM}/{DD}/{MEAL_ID}.json`
   That API is a read-only surface for menu retrieval by date and meal type (`1` breakfast, `2` lunch, `3` dinner). The response shape is restaurant-based (`menu_1`, `menu_2`).

6. The “next/previous day” navigation is business-rule driven, not just date arithmetic. The README says those buttons move to the previous/next working day using `ruyut/TaiwanCalendar`, which implies the bot must skip weekends and holidays when navigating menus.

7. The `public/`, `public/image/`, and `public/multi_size_image/` directories suggest generated assets for presenting menus visually, likely for LINE rich messages or shareable images. `fonts/` supports that rendering pipeline, indicating the system probably converts scraped text into branded image cards.

8. The `.github/workflows/` directory shows this is automation-heavy and likely GitHub Actions–driven:
   - `get-menu.yml`
   - `get-snack-menu.yml`
   - `get-working-day.yml`
   - `healthcheck.yml`
   These workflows probably run on schedules to refresh menu/calendar data and verify service availability.

9. `healthcheck.js` is a separate operational component, used by the workflow to confirm the deployed bot/API is reachable. This suggests the repo includes both application logic and lightweight observability/uptime checking.

10. Configuration appears environment-based. The presence of `.env.sample` implies secrets and deployment-specific values are injected at runtime, likely for LINE credentials and any hosting/API settings.

11. The overall data flow is:
   external MCUT website and calendar source -> crawler scripts -> generated menu/calendar assets and JSON -> bot/API consumers -> LINE users.
   In other words, the repo acts as a scraping-and-serving pipeline rather than a traditional database-backed application.

12. There is no obvious database, queue, or separate backend service in the tree. That means the architecture is probably stateless or file-driven, with GitHub Actions and published files/API responses serving as the persistence and distribution mechanism.

13. For an architecture diagram, the important boxes are:
   external school website/calendar source, crawler jobs, generated static assets/API, LINE bot runtime, and GitHub Actions automation. The key arrows are “scrape/update,” “render/generate,” and “serve/respond,” with the working-day calendar logic influencing menu navigation.

Main entry points: [`bot.js`](https://github.com/itshenrywu/mcut-menu-bot/blob/main/bot.js) (Bot runtime), [`.github/workflows`](https://github.com/itshenrywu/mcut-menu-bot/blob/main/.github/workflows) (GitHub Actions), [`crawler/menu.js`](https://github.com/itshenrywu/mcut-menu-bot/blob/main/crawler/menu.js) (Menu crawler), [`crawler/snack.js`](https://github.com/itshenrywu/mcut-menu-bot/blob/main/crawler/snack.js) (Snack crawler)

## Diagram (Mermaid)

```mermaid
flowchart TD

subgraph group_g1["User-facing"]
  node_n1(("LINE users<br/>external audience"))
  node_n2["Bot runtime<br/>LINE webhook bot<br/>[bot.js]"]
  node_n3[("Public API<br/>read-only menu API")]
end

subgraph group_g2["Data collection"]
  node_n4(("School site<br/>external source"))
  node_n5["Menu crawler<br/>scraper<br/>[menu.js]"]
  node_n6["Snack crawler<br/>scraper<br/>[snack.js]"]
  node_n7["News crawler<br/>scraper<br/>[news.js]"]
  node_n8["Workday logic<br/>calendar rules<br/>[working-day.js]"]
end

subgraph group_g3["Generated outputs"]
  node_n9["Menu assets<br/>generated images"]
  node_n10["Multi-size assets<br/>generated images"]
  node_n11["Fonts<br/>render assets"]
  node_n12[("API files<br/>generated json")]
end

subgraph group_g4["Automation"]
  node_n13{{"GitHub Actions<br/>CI/CD workflows<br/>[workflows]"}}
  node_n14["Menu workflow<br/>scheduled job<br/>[get-menu.yml]"]
  node_n15["Snack workflow<br/>scheduled job<br/>[get-snack-menu.yml]"]
  node_n16["Workday workflow<br/>scheduled job"]
  node_n17["Healthcheck<br/>uptime check<br/>[healthcheck.js]"]
end

node_n1 -->|"ask menus"| node_n2
node_n2 -->|"read api"| node_n3
node_n2 -->|"nav rules"| node_n8
node_n2 -->|"render reply"| node_n9
node_n2 -->|"render reply"| node_n10
node_n5 -->|"scrape"| node_n4
node_n6 -->|"scrape"| node_n4
node_n7 -->|"scrape"| node_n4
node_n8 -->|"calendar source"| node_n4
node_n5 -->|"write json"| node_n12
node_n6 -->|"write json"| node_n12
node_n5 -->|"generate images"| node_n9
node_n6 -->|"generate images"| node_n10
node_n11 -->|"style"| node_n9
node_n11 -->|"style"| node_n10
node_n13 -->|"run"| node_n14
node_n13 -->|"run"| node_n15
node_n13 -->|"run"| node_n16
node_n13 -->|"run"| node_n17
node_n14 -->|"execute"| node_n5
node_n15 -->|"execute"| node_n6
node_n16 -->|"execute"| node_n8
node_n17 -->|"check"| node_n2
node_n17 -->|"check"| node_n3

click node_n2 "https://github.com/itshenrywu/mcut-menu-bot/blob/main/bot.js"
click node_n3 "https://github.com/itshenrywu/mcut-menu-bot/tree/main/public"
click node_n5 "https://github.com/itshenrywu/mcut-menu-bot/blob/main/crawler/menu.js"
click node_n6 "https://github.com/itshenrywu/mcut-menu-bot/blob/main/crawler/snack.js"
click node_n7 "https://github.com/itshenrywu/mcut-menu-bot/blob/main/crawler/news.js"
click node_n8 "https://github.com/itshenrywu/mcut-menu-bot/blob/main/crawler/working-day.js"
click node_n9 "https://github.com/itshenrywu/mcut-menu-bot/tree/main/public/image"
click node_n10 "https://github.com/itshenrywu/mcut-menu-bot/tree/main/public/multi_size_image"
click node_n11 "https://github.com/itshenrywu/mcut-menu-bot/tree/main/fonts"
click node_n12 "https://github.com/itshenrywu/mcut-menu-bot/tree/main/public"
click node_n13 "https://github.com/itshenrywu/mcut-menu-bot/blob/main/.github/workflows"
click node_n14 "https://github.com/itshenrywu/mcut-menu-bot/blob/main/.github/workflows/get-menu.yml"
click node_n15 "https://github.com/itshenrywu/mcut-menu-bot/blob/main/.github/workflows/get-snack-menu.yml"
click node_n16 "https://github.com/itshenrywu/mcut-menu-bot/blob/main/.github/workflows/get-working-day.yml"
click node_n17 "https://github.com/itshenrywu/mcut-menu-bot/blob/main/healthcheck.js"

classDef toneNeutral fill:#f8fafc,stroke:#334155,stroke-width:1.5px,color:#0f172a
classDef toneBlue fill:#dbeafe,stroke:#2563eb,stroke-width:1.5px,color:#172554
classDef toneAmber fill:#fef3c7,stroke:#d97706,stroke-width:1.5px,color:#78350f
classDef toneMint fill:#dcfce7,stroke:#16a34a,stroke-width:1.5px,color:#14532d
classDef toneRose fill:#ffe4e6,stroke:#e11d48,stroke-width:1.5px,color:#881337
classDef toneIndigo fill:#e0e7ff,stroke:#4f46e5,stroke-width:1.5px,color:#312e81
classDef toneTeal fill:#ccfbf1,stroke:#0f766e,stroke-width:1.5px,color:#134e4a
class node_n1,node_n2,node_n3 toneBlue
class node_n4,node_n5,node_n6,node_n7,node_n8 toneAmber
class node_n9,node_n10,node_n11,node_n12 toneMint
class node_n13,node_n14,node_n15,node_n16,node_n17 toneRose
```

## Components

### User-facing

Chat bot and public menu access

- **LINE users**: external audience · People querying campus menus through LINE
- [Bot runtime](https://github.com/itshenrywu/mcut-menu-bot/blob/main/bot.js): LINE webhook bot · Interactive entry point for menu lookup and replies · `bot.js`
- [Public API](https://github.com/itshenrywu/mcut-menu-bot/tree/main/public): read-only menu API · Static JSON menu surface for external consumers · `public`

### Data collection

Scrapers and calendar logic

- **School site**: external source · Upstream MCUT website that publishes menu and news content
- [Menu crawler](https://github.com/itshenrywu/mcut-menu-bot/blob/main/crawler/menu.js): scraper · Fetches regular meal menus from the upstream site · `crawler/menu.js`
- [Snack crawler](https://github.com/itshenrywu/mcut-menu-bot/blob/main/crawler/snack.js): scraper · Fetches snack and light meal menus · `crawler/snack.js`
- [News crawler](https://github.com/itshenrywu/mcut-menu-bot/blob/main/crawler/news.js): scraper · Fetches announcements and related updates · `crawler/news.js`
- [Workday logic](https://github.com/itshenrywu/mcut-menu-bot/blob/main/crawler/working-day.js): calendar rules · Determines working days for menu navigation · `crawler/working-day.js`

### Generated outputs

Static menu assets and API data

- [Menu assets](https://github.com/itshenrywu/mcut-menu-bot/tree/main/public/image): generated images · Rendered menu images for presentation and sharing · `public/image`
- [Multi-size assets](https://github.com/itshenrywu/mcut-menu-bot/tree/main/public/multi_size_image): generated images · Alternative image sizes for rich message delivery · `public/multi_size_image`
- [Fonts](https://github.com/itshenrywu/mcut-menu-bot/tree/main/fonts): render assets · Typography used when composing menu visuals · `fonts`
- [API files](https://github.com/itshenrywu/mcut-menu-bot/tree/main/public): generated json · Published menu JSON consumed by the public API · `public`

### Automation

Scheduled workflows and checks

- [GitHub Actions](https://github.com/itshenrywu/mcut-menu-bot/blob/main/.github/workflows): CI/CD workflows · Scheduled refresh and operational automation · `.github/workflows`
- [Menu workflow](https://github.com/itshenrywu/mcut-menu-bot/blob/main/.github/workflows/get-menu.yml): scheduled job · Runs menu refresh automation · `.github/workflows/get-menu.yml`
- [Snack workflow](https://github.com/itshenrywu/mcut-menu-bot/blob/main/.github/workflows/get-snack-menu.yml): scheduled job · Runs snack menu refresh automation · `.github/workflows/get-snack-menu.yml`
- [Workday workflow](https://github.com/itshenrywu/mcut-menu-bot/blob/main/.github/workflows/get-working-day.yml): scheduled job · Refreshes calendar and working-day data · `.github/workflows/get-working-day.yml`
- [Healthcheck](https://github.com/itshenrywu/mcut-menu-bot/blob/main/healthcheck.js): uptime check · Verifies the deployed bot or API is reachable · `healthcheck.js`

## Connections

- LINE users → Bot runtime: ask menus · Users send menu queries to the bot
- Bot runtime → Public API: read api · Bot can consume published menu JSON
- Bot runtime → Workday logic: nav rules · Bot uses working-day logic for previous/next navigation
- Bot runtime → Menu assets: render reply · Bot replies with generated menu images
- Bot runtime → Multi-size assets: render reply · Bot can use alternate-sized images for rich responses
- Menu crawler → School site: scrape · Regular menu crawler pulls upstream content
- Snack crawler → School site: scrape · Snack crawler pulls upstream content
- News crawler → School site: scrape · News crawler pulls upstream content
- Workday logic → School site: calendar source · Working-day logic depends on external calendar rules/source
- Menu crawler → API files: write json · Menu crawler produces JSON data for serving
- Snack crawler → API files: write json · Snack crawler produces JSON data for serving
- Menu crawler → Menu assets: generate images · Menu crawler feeds visual menu rendering
- Snack crawler → Multi-size assets: generate images · Snack crawler feeds alternative image rendering
- Fonts → Menu assets: style · Fonts support image composition
- Fonts → Multi-size assets: style · Fonts support image composition
- GitHub Actions → Menu workflow: run · Workflow orchestrates menu refresh job
- GitHub Actions → Snack workflow: run · Workflow orchestrates snack refresh job
- GitHub Actions → Workday workflow: run · Workflow orchestrates working-day refresh job
- GitHub Actions → Healthcheck: run · Workflow executes deployment health check
- Menu workflow → Menu crawler: execute · Menu workflow triggers the menu crawler
- Snack workflow → Snack crawler: execute · Snack workflow triggers the snack crawler
- Workday workflow → Workday logic: execute · Workday workflow refreshes calendar logic/data
- Healthcheck → Bot runtime: check · Healthcheck validates bot reachability
- Healthcheck → Public API: check · Healthcheck can validate the public API surface
