# liebenholz/ohaasa-daily-discord architecture

> Architecture diagram of the GitHub repository liebenholz/ohaasa-daily-discord, generated by GitDiagram from its file tree, README and a sample of source files (last updated 2026-11-12). It is an AI-made overview: check details against the source.

- Interactive diagram: https://gitdiagram.com/liebenholz/ohaasa-daily-discord
- Repository: https://github.com/liebenholz/ohaasa-daily-discord

## Overview

Ohaasa is a Korean-language Discord horoscope bot. Its daily workflow collects and parses the Japanese ranking, translates and stores horoscope data, then sends ranked embeds to configured server channels. Discord slash-command requests return sign-specific details, while separate commands manage notification channels. The product also provides scheduled source-health checks and monthly/annual ranking reports. The graph follows README-described flows and sampled imports/calls; some feature details are documented but their implementation was not sampled.

Main entry points: [`notifier.py`](https://github.com/liebenholz/ohaasa-daily-discord/blob/main/notifier.py) (Bot Notifier), [`main.py`](https://github.com/liebenholz/ohaasa-daily-discord/blob/main/main.py) (Collect and Parse), [`api/index.py`](https://github.com/liebenholz/ohaasa-daily-discord/blob/main/api/index.py) (Interaction Handler), [`data`](https://github.com/liebenholz/ohaasa-daily-discord/tree/main/data) (Horoscope Files)

## Diagram (Mermaid)

```mermaid
flowchart TD

subgraph group_daily["Daily Horoscope"]
  node_crawler["Collect and Parse<br/>[main.py]"]
  node_translator["Translate Content<br/>[main.py]"]
end

subgraph group_discord["Discord Interactions"]
  node_notifier["Bot Notifier<br/>[notifier.py]"]
  node_interactions["Interaction Handler<br/>[index.py]"]
end

subgraph group_ops["Health Monitoring"]
  node_health_entry["Health Request<br/>[health_entry.py]"]
  node_signature["QStash Verifier<br/>[qstash_verify.py]"]
  node_health_check["Source Health Check<br/>[source_health.py]"]
  node_relay["Weekend Relay<br/>[source_relay.py]"]
end

subgraph group_reports["Ranking Reports"]
  node_ranking_stats["Ranking Aggregates<br/>[ranking_stats.py]"]
  node_monthly_report["Monthly Report<br/>[monthly_stats.py]"]
  node_annual_report["Annual Report<br/>[annual_stats.py]"]
end

subgraph group_state["Shared State"]
  node_horoscope_data[("Horoscope Files")]
  node_channels_store[("Channel Registry<br/>[channels.py]")]
end

node_scheduler(("Daily Scheduler"))
node_discord_app(("Discord App"))
node_user(("Discord User"))
node_qstash(("QStash Scheduler"))
node_source_sites["Horoscope Sites"]
node_deepl["DeepL API"]

node_scheduler -->|"starts crawl"| node_crawler
node_crawler -->|"fetches pages"| node_source_sites
node_crawler -->|"requests weekend HTML"| node_relay
node_crawler -->|"passes parsed text"| node_translator
node_translator -->|"batch translates"| node_deepl
node_translator -->|"writes JSON"| node_horoscope_data
node_crawler -->|"stores rankings"| node_horoscope_data
node_crawler -->|"requests delivery"| node_notifier
node_notifier -->|"reads channels"| node_channels_store
node_notifier -->|"sends embeds"| node_discord_app
node_user -->|"submits command"| node_discord_app
node_discord_app -->|"posts interaction"| node_interactions
node_interactions -->|"fetches latest"| node_horoscope_data
node_interactions -->|"manages channels"| node_channels_store
node_interactions -->|"sends test message"| node_notifier
node_interactions -->|"returns embed"| node_discord_app
node_qstash -->|"calls health endpoint"| node_health_entry
node_health_entry -->|"verifies request"| node_signature
node_health_entry -->|"runs check"| node_health_check
node_health_check -->|"checks availability"| node_source_sites
node_health_check -->|"reads health state"| node_channels_store
node_health_check -->|"sends alerts"| node_notifier
node_relay -->|"fetches weekend page"| node_source_sites
node_monthly_report -->|"loads date files"| node_horoscope_data
node_annual_report -->|"loads date files"| node_horoscope_data
node_monthly_report -->|"uses aggregates"| node_ranking_stats
node_annual_report -->|"uses aggregates"| node_ranking_stats
node_monthly_report -->|"lists destinations"| node_channels_store
node_annual_report -->|"lists destinations"| node_channels_store
node_monthly_report -->|"sends report"| node_notifier
node_annual_report -->|"sends report"| node_notifier

click node_crawler "https://github.com/liebenholz/ohaasa-daily-discord/blob/main/main.py"
click node_translator "https://github.com/liebenholz/ohaasa-daily-discord/blob/main/main.py"
click node_horoscope_data "https://github.com/liebenholz/ohaasa-daily-discord/tree/main/data"
click node_channels_store "https://github.com/liebenholz/ohaasa-daily-discord/blob/main/channels.py"
click node_notifier "https://github.com/liebenholz/ohaasa-daily-discord/blob/main/notifier.py"
click node_interactions "https://github.com/liebenholz/ohaasa-daily-discord/blob/main/api/index.py"
click node_health_entry "https://github.com/liebenholz/ohaasa-daily-discord/blob/main/health_entry.py"
click node_signature "https://github.com/liebenholz/ohaasa-daily-discord/blob/main/qstash_verify.py"
click node_health_check "https://github.com/liebenholz/ohaasa-daily-discord/blob/main/source_health.py"
click node_relay "https://github.com/liebenholz/ohaasa-daily-discord/blob/main/source_relay.py"
click node_ranking_stats "https://github.com/liebenholz/ohaasa-daily-discord/blob/main/ranking_stats.py"
click node_monthly_report "https://github.com/liebenholz/ohaasa-daily-discord/blob/main/monthly_stats.py"
click node_annual_report "https://github.com/liebenholz/ohaasa-daily-discord/blob/main/annual_stats.py"

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_crawler,node_translator,node_user toneBlue
class node_notifier,node_interactions toneAmber
class node_health_entry,node_signature,node_health_check,node_relay,node_deepl toneMint
class node_ranking_stats,node_monthly_report,node_annual_report,node_scheduler,node_qstash toneRose
class node_horoscope_data,node_channels_store,node_discord_app,node_source_sites toneIndigo
```

## Components

### Daily Horoscope

- [Collect and Parse](https://github.com/liebenholz/ohaasa-daily-discord/blob/main/main.py): `main.py`
- [Translate Content](https://github.com/liebenholz/ohaasa-daily-discord/blob/main/main.py): `main.py`

### Discord Interactions

- [Bot Notifier](https://github.com/liebenholz/ohaasa-daily-discord/blob/main/notifier.py): `notifier.py`
- [Interaction Handler](https://github.com/liebenholz/ohaasa-daily-discord/blob/main/api/index.py): `api/index.py`

### Health Monitoring

- [Health Request](https://github.com/liebenholz/ohaasa-daily-discord/blob/main/health_entry.py): `health_entry.py`
- [QStash Verifier](https://github.com/liebenholz/ohaasa-daily-discord/blob/main/qstash_verify.py): `qstash_verify.py`
- [Source Health Check](https://github.com/liebenholz/ohaasa-daily-discord/blob/main/source_health.py): `source_health.py`
- [Weekend Relay](https://github.com/liebenholz/ohaasa-daily-discord/blob/main/source_relay.py): `source_relay.py`

### Ranking Reports

- [Ranking Aggregates](https://github.com/liebenholz/ohaasa-daily-discord/blob/main/ranking_stats.py): `ranking_stats.py`
- [Monthly Report](https://github.com/liebenholz/ohaasa-daily-discord/blob/main/monthly_stats.py): `monthly_stats.py`
- [Annual Report](https://github.com/liebenholz/ohaasa-daily-discord/blob/main/annual_stats.py): `annual_stats.py`

### Shared State

- [Horoscope Files](https://github.com/liebenholz/ohaasa-daily-discord/tree/main/data): `data`
- [Channel Registry](https://github.com/liebenholz/ohaasa-daily-discord/blob/main/channels.py): `channels.py`

### Other components

- **Daily Scheduler**
- **Discord App**
- **Discord User**
- **QStash Scheduler**
- **Horoscope Sites**
- **DeepL API**

## Connections

- Daily Scheduler → Collect and Parse: starts crawl (evidence: `README.md`)
- Collect and Parse → Horoscope Sites: fetches pages (evidence: `README.md`)
- Collect and Parse → Weekend Relay: requests weekend HTML (evidence: `README.md`)
- Collect and Parse → Translate Content: passes parsed text (evidence: `README.md`)
- Translate Content → DeepL API: batch translates (evidence: `README.md`)
- Translate Content → Horoscope Files: writes JSON (evidence: `README.md`)
- Collect and Parse → Horoscope Files: stores rankings (evidence: `README.md`)
- Collect and Parse → Bot Notifier: requests delivery (evidence: `main.py`)
- Bot Notifier → Channel Registry: reads channels (evidence: `notifier.py`)
- Bot Notifier → Discord App: sends embeds (evidence: `README.md`)
- Discord User → Discord App: submits command (evidence: `README.md`)
- Discord App → Interaction Handler: posts interaction (evidence: `README.md`)
- Interaction Handler → Horoscope Files: fetches latest (evidence: `README.md`)
- Interaction Handler → Channel Registry: manages channels (evidence: `api/index.py`)
- Interaction Handler → Bot Notifier: sends test message (evidence: `api/index.py`)
- Interaction Handler → Discord App: returns embed (evidence: `README.md`)
- QStash Scheduler → Health Request: calls health endpoint (evidence: `README.md`)
- Health Request → QStash Verifier: verifies request (evidence: `health_entry.py`)
- Health Request → Source Health Check: runs check (evidence: `health_entry.py`)
- Source Health Check → Horoscope Sites: checks availability (evidence: `source_health.py`)
- Source Health Check → Channel Registry: reads health state (evidence: `source_health.py`)
- Source Health Check → Bot Notifier: sends alerts (evidence: `source_health.py`)
- Weekend Relay → Horoscope Sites: fetches weekend page (evidence: `source_relay.py`)
- Monthly Report → Horoscope Files: loads date files (evidence: `README.md`)
- Annual Report → Horoscope Files: loads date files (evidence: `README.md`)
- Monthly Report → Ranking Aggregates: uses aggregates (evidence: `monthly_stats.py`)
- Annual Report → Ranking Aggregates: uses aggregates (evidence: `annual_stats.py`)
- Monthly Report → Channel Registry: lists destinations (evidence: `monthly_stats.py`)
- Annual Report → Channel Registry: lists destinations (evidence: `annual_stats.py`)
- Monthly Report → Bot Notifier: sends report (evidence: `monthly_stats.py`)
- Annual Report → Bot Notifier: sends report (evidence: `annual_stats.py`)
