Work with Obsidian vaults (plain Markdown notes) and automate via obsidian-cli.
npx skills add LittleJakub/daily-brief
Or install specific skill: npx add-skill https://github.com/LittleJakub/daily-brief
# Description
Morning & evening personal briefings via Telegram β weather, ICS calendar (Outlook/Google/iCloud), Todoist tasks, life-ledger reminders, pulse-board rig status. OpenClaw skill. Stdlib only, no pip deps.
# SKILL.md
daily-brief
Version: 1.2.0
Location: ~/.openclaw/agents/main/workspace/skills/daily-brief/
What it does
daily-brief posts two personal briefings to a configurable channel (Feishu or Telegram) each day β a morning edition and an evening edition. Personal day planner, not a system health monitor (that's pulse-board).
Morning briefing β 06:00
| Section | Source | Required |
|---|---|---|
| Weather today | OpenWeatherMap | OPENWEATHER_API_KEY |
| Today's calendar | ICS feeds | configured in setup |
| Tasks due today + overdue | Todoist REST API v1 | TODOIST_API_TOKEN |
| Life-ledger reminders (7-day window) | ledger.json |
configured in setup |
| Rig status (one-liner) | pulse-board last-delivered.md |
configured in setup |
Evening briefing β 21:00
| Section | Source | Required |
|---|---|---|
| Tomorrow's calendar | ICS feeds | configured in setup |
| Weather tomorrow | OpenWeatherMap | OPENWEATHER_API_KEY |
| Unfinished items from today | Todoist REST API v1 | TODOIST_API_TOKEN |
| 7-day task + date horizon | Todoist + life-ledger | β |
| Life-ledger reminders | ledger.json |
configured in setup |
Manual run
python3 ~/.openclaw/agents/main/workspace/skills/daily-brief/daily_brief.py morning
python3 ~/.openclaw/agents/main/workspace/skills/daily-brief/daily_brief.py evening
Required secrets
All in ~/.openclaw/shared/secrets/openclaw-secrets.env:
| Variable | Description |
|---|---|
TELEGRAM_BOT_TOKEN |
Telegram bot token (when channel = telegram) |
TODOIST_API_TOKEN |
Todoist REST API token (shared with task-bridge) |
OPENWEATHER_API_KEY |
OpenWeatherMap API key (free β new keys take up to 2h to activate) |
FEISHU_APP_ID |
Feishu app ID (when channel = feishu) |
FEISHU_APP_SECRET |
Feishu app secret (when channel = feishu) |
FEISHU_HORIZON_ROOT_MSG |
Root om_xxx message ID of the target Feishu topic |
DAILY_BRIEF_ICS_* |
One per calendar β name is up to you |
Config file
~/.openclaw/config/daily-brief/config.json β written by setup.py.
{
"channel": "feishu",
"feishu": {
"chat_id": "oc_..."
},
"telegram": {
"chat_id": -1001234567890,
"thread_id": 99
},
"weather": {
"lat": 0.0000,
"lon": 0.0000,
"city_name": "Your City"
},
"calendar": {
"enabled": true,
"calendars": [
{"label": "Personal", "ics_secret_key": "DAILY_BRIEF_ICS_PERSONAL"},
{"label": "Work", "ics_secret_key": "DAILY_BRIEF_ICS_WORK"}
]
},
"life_ledger": {
"enabled": true,
"path": "/home/USER/.openclaw/shared/life-ledger/ledger.json"
},
"pulse_board": {
"enabled": true,
"last_delivered_path": "/home/USER/.openclaw/agents/main/workspace/skills/pulse-board/last-delivered.md"
},
"alert_window_days": 7
}
Notes:
- channel β "feishu" or "telegram". Defaults to "telegram" if omitted.
- feishu.thread_id β not used. Topic routing is via FEISHU_HORIZON_ROOT_MSG secret (reply API).
- calendar.calendars β add as many calendars as needed.
- life_ledger.enabled / pulse_board.enabled β set false to omit those sections entirely.
Feishu topic routing
Feishu rejects receive_id_type=thread_id with a field validation error. The only working method to post into an existing topic is the reply API, using the root om_xxx message ID of that thread.
To get the root message ID of a topic:
source ~/.openclaw/shared/secrets/openclaw-secrets.env
TOKEN=$(curl -s https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal \
-H "Content-Type: application/json" \
-d "{\"app_id\":\"$FEISHU_APP_ID\",\"app_secret\":\"$FEISHU_APP_SECRET\"}" \
| python3 -c 'import json,sys; print(json.load(sys.stdin)["tenant_access_token"])')
curl -s "https://open.feishu.cn/open-apis/im/v1/messages?container_id_type=thread&container_id=$FEISHU_TOPIC_HORIZON&page_size=1" \
-H "Authorization: Bearer $TOKEN" \
| python3 -c 'import json,sys; print(json.load(sys.stdin)["data"]["items"][0]["message_id"])'
Store the result as FEISHU_HORIZON_ROOT_MSG in openclaw-secrets.env.
Calendar β ICS feeds
Works with any calendar that provides an ICS URL. URLs stored in secrets, never in config.
| Service | Path |
|---|---|
| Outlook.com | Settings β Calendar β Shared calendars β Publish β copy ICS link |
| Office 365 | Same flow at outlook.office.com (requires IT to allow external publishing) |
| Google Calendar | Calendar settings β Integrate calendar β Secret address in iCal format |
| iCloud | Share β Public Calendar β copy link (change webcal:// to https://) |
Logs
~/.openclaw/agents/main/workspace/skills/daily-brief/daily-brief.log
Cron
crontab -l | grep daily-brief
Includes a PATH= line with ~/.npm-global/bin so openclaw resolves in cron context.
Related skills
| Skill | Relationship |
|---|---|
pulse-board |
daily-brief reads last-delivered.md for the morning rig one-liner |
task-bridge |
Shares TODOIST_API_TOKEN |
life-ledger |
daily-brief reads ledger.json β read-only |
# README.md
Daily Brief
An OpenClaw skill that posts a morning and evening personal briefing via a configurable channel (Feishu or Telegram) β weather, calendar events, tasks, life reminders, and a rig health check.
This is a personal day planner, not a system health monitor. For system health, see pulse-board.
What's in each briefing
Morning β 06:00
| Section | Source |
|---|---|
| Weather today | OpenWeatherMap (free tier) |
| Today's calendar | ICS feeds (Outlook, Google, iCloud, etc.) |
| Tasks due today + overdue | Todoist REST API v1 |
| Life-ledger reminders (7-day window) | life-ledger skill |
| Rig status (one-liner) | pulse-board |
Evening β 21:00
| Section | Source |
|---|---|
| Tomorrow's calendar | ICS feeds |
| Weather tomorrow | OpenWeatherMap |
| Unfinished items from today | Todoist REST API v1 |
| 7-day task + date horizon | Todoist + life-ledger |
| Life-ledger reminders | life-ledger skill |
Life-ledger and pulse-board are optional β setup asks whether they're installed and omits their sections cleanly if not.
Delivery channels
Set "channel" in config.json:
| Value | Description |
|---|---|
"feishu" |
Feishu chat topic via tenant access token + reply API |
"telegram" |
Telegram supergroup topic (default) |
Both configs can coexist β switching channels is a one-line change.
Requirements
- Python 3.9+ (stdlib only β no pip dependencies)
- OpenWeatherMap API key (free tier β new keys take up to 2 hours to activate)
- Todoist account + API token
- Feishu (primary): app with
app_id+app_secret, target chat, and root message ID of the topic - Telegram (fallback): bot token, supergroup
chat_id, topicthread_id
Optional:
- ICS calendar URLs (Outlook.com, Office 365, Google Calendar, iCloud, or any standard ICS source)
- life-ledger β for upcoming date/reminder alerts
- pulse-board β for the morning rig status one-liner
Install
cp daily_brief.py setup.py SKILL.md CHANGELOG.md _meta.json \
~/.openclaw/agents/main/workspace/skills/daily-brief/
python3 ~/.openclaw/agents/main/workspace/skills/daily-brief/setup.py
Setup will:
- Ask for your delivery channel and credentials
- Ask for city name, coordinates, and OpenWeatherMap key
- Ask for Todoist token
- Ask about ICS calendar feeds
- Ask whether life-ledger and pulse-board are installed
- Validate each API live before writing anything
- Write ~/.openclaw/config/daily-brief/config.json
- Install cron entries (06:00 and 21:00)
Feishu topic routing
Feishu rejects receive_id_type=thread_id. The only working method to post into an existing topic is the reply API, targeting the root om_xxx message ID of the thread.
Get the root message ID of your target topic:
source ~/.openclaw/shared/secrets/openclaw-secrets.env
TOKEN=$(curl -s https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal \
-H "Content-Type: application/json" \
-d "{\"app_id\":\"$FEISHU_APP_ID\",\"app_secret\":\"$FEISHU_APP_SECRET\"}" \
| python3 -c 'import json,sys; print(json.load(sys.stdin)["tenant_access_token"])')
curl -s "https://open.feishu.cn/open-apis/im/v1/messages?container_id_type=thread&container_id=$FEISHU_TOPIC_HORIZON&page_size=1" \
-H "Authorization: Bearer $TOKEN" \
| python3 -c 'import json,sys; print(json.load(sys.stdin)["data"]["items"][0]["message_id"])'
Store the result:
echo "FEISHU_HORIZON_ROOT_MSG=om_xxx..." \
>> ~/.openclaw/shared/secrets/openclaw-secrets.env
Calendar setup
ICS URLs are treated as secrets β store them in openclaw-secrets.env:
echo "DAILY_BRIEF_ICS_PERSONAL=https://..." \
>> ~/.openclaw/shared/secrets/openclaw-secrets.env
| Service | Path |
|---|---|
| Outlook.com | Settings β Calendar β Shared calendars β Publish β copy ICS link |
| Office 365 | Same flow at outlook.office.com (requires IT to allow external publishing) |
| Google Calendar | Calendar settings β Integrate calendar β Secret address in iCal format |
| iCloud | Share β Public Calendar β copy link (change webcal:// to https://) |
Required secrets
All in ~/.openclaw/shared/secrets/openclaw-secrets.env:
| Variable | Description |
|---|---|
FEISHU_APP_ID |
Feishu app ID |
FEISHU_APP_SECRET |
Feishu app secret |
FEISHU_HORIZON_ROOT_MSG |
Root om_xxx message ID of the target Feishu topic |
TELEGRAM_BOT_TOKEN |
Telegram bot token (fallback channel) |
TODOIST_API_TOKEN |
Todoist REST API token |
OPENWEATHER_API_KEY |
OpenWeatherMap API key |
DAILY_BRIEF_ICS_* |
One per calendar β name is up to you |
Manual run
python3 ~/.openclaw/agents/main/workspace/skills/daily-brief/daily_brief.py morning
python3 ~/.openclaw/agents/main/workspace/skills/daily-brief/daily_brief.py evening
Why ICS and not Microsoft Graph?
Microsoft deprecated personal account app registration outside a directory in June 2024. ICS export requires no auth, no app registration, and works for personal Outlook.com and (if IT allows external publishing) work/school Microsoft 365 accounts.
Formatting note
Briefs are composed in HTML internally. Telegram receives them with parse_mode: HTML. Feishu receives plain text β all HTML tags are stripped before delivery.
License
MIT
# Supported AI Coding Agents
This skill is compatible with the SKILL.md standard and works with all major AI coding agents:
Learn more about the SKILL.md standard and how to use these skills with your preferred AI coding agent.