Skip to content

Commit 566c1dc

Browse files
authored
Add Telegram rich report rendering (#184)
* Add Telegram rich report rendering * Address Telegram rich report review comments
1 parent d142513 commit 566c1dc

11 files changed

Lines changed: 1065 additions & 44 deletions

File tree

AGENTS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,7 @@ The gate has a **second pass** for sources that emit `sleep_unspecified` only (n
264264
| `REPORT_EVENING_WEEKEND` | `21` | Evening report hour on weekends |
265265
| `REPORT_MORNING_CAP` | `morningHour+4` (floor 11) | Smart-retry deadline for morning report — past this hour the report force-sends with a stale-data banner regardless of `SleepSettled`. Server prefers an adaptive cap from `GetTypicalWakeTime(14) + 60min` when ≥7 days of per-segment sleep data are available; falls back to this env value otherwise. Override via env or `report_morning_cap` setting. **Floor:** `MorningCapTime` pushes the cap to `now + MinPromptWindow` (60min) whenever the computed cap is already in the past at call time — adaptive cap can land before `morning_hour` for users whose schedule puts the morning report well after typical wake, and without the floor the smart-retry loop enters past cap on first tick and silently skips `MorningActionPrompt`, disabling subjective check-in entirely. Pinned by `TestMorningCapTime_FloorsPastCapsToPromptWindow` |
266266
| `REPORT_TZ` | system local | Timezone for report scheduling (e.g. `Europe/Belgrade`) |
267+
| `TELEGRAM_RICH_MESSAGES` | `false` | Opt-in Telegram Bot API Rich Messages for morning/evening reports. DB setting `telegram_rich_messages` uses the same boolean semantics. Fallback stays on legacy `sendMessage` HTML when rich send fails. |
267268
| `GEMINI_API_KEY` || Gemini API key; enables AI morning briefing |
268269
| `GEMINI_MODEL` | `gemini-2.5-flash` | Gemini model; overridable in Admin UI |
269270
| `GEMINI_MAX_TOKENS` | `5000` | Max output tokens for AI briefing; overridable in Admin UI |

CLAUDE.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,7 @@ The gate has a **second pass** for sources that emit `sleep_unspecified` only (n
264264
| `REPORT_EVENING_WEEKEND` | `21` | Evening report hour on weekends |
265265
| `REPORT_MORNING_CAP` | `morningHour+4` (floor 11) | Smart-retry deadline for morning report — past this hour the report force-sends with a stale-data banner regardless of `SleepSettled`. Server prefers an adaptive cap from `GetTypicalWakeTime(14) + 60min` when ≥7 days of per-segment sleep data are available; falls back to this env value otherwise. Override via env or `report_morning_cap` setting. **Floor:** `MorningCapTime` pushes the cap to `now + MinPromptWindow` (60min) whenever the computed cap is already in the past at call time — adaptive cap can land before `morning_hour` for users whose schedule puts the morning report well after typical wake, and without the floor the smart-retry loop enters past cap on first tick and silently skips `MorningActionPrompt`, disabling subjective check-in entirely. Pinned by `TestMorningCapTime_FloorsPastCapsToPromptWindow` |
266266
| `REPORT_TZ` | system local | Timezone for report scheduling (e.g. `Europe/Belgrade`) |
267+
| `TELEGRAM_RICH_MESSAGES` | `false` | Opt-in Telegram Bot API Rich Messages for morning/evening reports. DB setting `telegram_rich_messages` uses the same boolean semantics. Fallback stays on legacy `sendMessage` HTML when rich send fails. |
267268
| `GEMINI_API_KEY` || Gemini API key; enables AI morning briefing |
268269
| `GEMINI_MODEL` | `gemini-2.5-flash` | Gemini model; overridable in Admin UI |
269270
| `GEMINI_MAX_TOKENS` | `5000` | Max output tokens for AI briefing; overridable in Admin UI |

cmd/server/main.go

Lines changed: 29 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -48,15 +48,16 @@ func main() {
4848

4949
// Env-level defaults for the first/only tenant.
5050
envNotifyDefaults := storage.NotifyConfig{
51-
Token: os.Getenv("TELEGRAM_TOKEN"),
52-
ChatID: os.Getenv("TELEGRAM_CHAT_ID"),
53-
Lang: getEnv("REPORT_LANG", "en"),
54-
Timezone: getEnv("REPORT_TZ", ""),
55-
MorningWeekdayHour: getEnvInt("REPORT_MORNING_WEEKDAY", 8),
56-
MorningWeekendHour: getEnvInt("REPORT_MORNING_WEEKEND", 9),
57-
EveningWeekdayHour: getEnvInt("REPORT_EVENING_WEEKDAY", 20),
58-
EveningWeekendHour: getEnvInt("REPORT_EVENING_WEEKEND", 21),
59-
MorningCapHour: getEnvInt("REPORT_MORNING_CAP", 0),
51+
Token: os.Getenv("TELEGRAM_TOKEN"),
52+
ChatID: os.Getenv("TELEGRAM_CHAT_ID"),
53+
Lang: getEnv("REPORT_LANG", "en"),
54+
Timezone: getEnv("REPORT_TZ", ""),
55+
MorningWeekdayHour: getEnvInt("REPORT_MORNING_WEEKDAY", 8),
56+
MorningWeekendHour: getEnvInt("REPORT_MORNING_WEEKEND", 9),
57+
EveningWeekdayHour: getEnvInt("REPORT_EVENING_WEEKDAY", 20),
58+
EveningWeekendHour: getEnvInt("REPORT_EVENING_WEEKEND", 21),
59+
TelegramRichMessages: getEnvBool("TELEGRAM_RICH_MESSAGES", false),
60+
MorningCapHour: getEnvInt("REPORT_MORNING_CAP", 0),
6061
}
6162
envAIDefaults := storage.AIConfig{
6263
APIKey: os.Getenv("GEMINI_API_KEY"),
@@ -506,15 +507,16 @@ func tenantLocalNow(db *storage.DB, defaults storage.NotifyConfig) time.Time {
506507
// finding all three call sites.
507508
func buildNotifyCfg(db *storage.DB, c storage.NotifyConfig) notify.Config {
508509
cfg := notify.Config{
509-
Token: c.Token,
510-
ChatID: c.ChatID,
511-
Lang: c.Lang,
512-
Timezone: c.Timezone,
513-
MorningWeekdayHour: c.MorningWeekdayHour,
514-
MorningWeekendHour: c.MorningWeekendHour,
515-
EveningWeekdayHour: c.EveningWeekdayHour,
516-
EveningWeekendHour: c.EveningWeekendHour,
517-
MorningCapHour: c.MorningCapHour,
510+
Token: c.Token,
511+
ChatID: c.ChatID,
512+
Lang: c.Lang,
513+
Timezone: c.Timezone,
514+
MorningWeekdayHour: c.MorningWeekdayHour,
515+
MorningWeekendHour: c.MorningWeekendHour,
516+
EveningWeekdayHour: c.EveningWeekdayHour,
517+
EveningWeekendHour: c.EveningWeekendHour,
518+
TelegramRichMessages: c.TelegramRichMessages,
519+
MorningCapHour: c.MorningCapHour,
518520
}
519521
if h, m, ok := db.GetTypicalWakeTime(14); ok {
520522
cfg.TypicalWakeHour = h
@@ -1230,3 +1232,12 @@ func getEnvInt(key string, fallback int) int {
12301232
}
12311233
return fallback
12321234
}
1235+
1236+
func getEnvBool(key string, fallback bool) bool {
1237+
if v := os.Getenv(key); v != "" {
1238+
if b, err := strconv.ParseBool(v); err == nil {
1239+
return b
1240+
}
1241+
}
1242+
return fallback
1243+
}

0 commit comments

Comments
 (0)