Auto backup: 2026-02-17 18:00
This commit is contained in:
58
automations/ai-newsletter-digest/README.md
Normal file
58
automations/ai-newsletter-digest/README.md
Normal file
@@ -0,0 +1,58 @@
|
||||
# AI Newsletter Digest Automation
|
||||
|
||||
Automatically consolidates AI-related newsletters into a single, deduplicated daily digest.
|
||||
|
||||
## Schedule
|
||||
- **When:** Daily at 7:30 AM (Australia/Perth timezone)
|
||||
- **Delivery:** Via Telegram
|
||||
|
||||
## What It Does
|
||||
1. Scans unread emails from the last 24 hours
|
||||
2. Identifies AI-related newsletters
|
||||
3. **Excludes:** Notion, Platformer, WSJ, WIRED Daily (per Anthony's request)
|
||||
4. Fetches full content from remaining newsletters
|
||||
5. Uses LLM to analyze and create consolidated digest:
|
||||
- Top AI News (deduplicated across sources)
|
||||
- Product Launches
|
||||
- Research Highlights
|
||||
- Industry Trends
|
||||
- Notable Quotes
|
||||
|
||||
## Files
|
||||
- `daily-digest.sh` - Main script that fetches newsletters
|
||||
- `digest.py` - Python analysis tool (alternative approach)
|
||||
- `list-newsletters.py` - Quick check of what newsletters are in inbox
|
||||
|
||||
## Manual Run
|
||||
```bash
|
||||
cd /home/openclaw/.openclaw/workspace/automations/ai-newsletter-digest
|
||||
./daily-digest.sh
|
||||
```
|
||||
|
||||
This outputs a file path. You can then ask me to analyze it.
|
||||
|
||||
## Cron Job
|
||||
- **Job ID:** `faaed154-8320-468f-a597-21b6a92eed39`
|
||||
- **Check status:** `openclaw cron list`
|
||||
- **Disable:** `openclaw cron remove <job-id>`
|
||||
|
||||
## Newsletter Sources Included
|
||||
- AI Valley
|
||||
- The Information (Applied AI)
|
||||
- Wall Street Journal (AI coverage)
|
||||
- The Deep View
|
||||
- WIRED Daily
|
||||
- And other AI-related emails
|
||||
|
||||
## How to Modify
|
||||
|
||||
**Change the schedule:**
|
||||
```bash
|
||||
openclaw cron update <job-id> --schedule "0 8 * * *" # 8am instead
|
||||
```
|
||||
|
||||
**Add more exclusions:**
|
||||
Edit `daily-digest.sh` and add more `grep -v "pattern"` lines
|
||||
|
||||
**Change lookback period:**
|
||||
Edit the `--recent 24h` parameter in `daily-digest.sh`
|
||||
47
automations/ai-newsletter-digest/analyze.sh
Executable file
47
automations/ai-newsletter-digest/analyze.sh
Executable file
@@ -0,0 +1,47 @@
|
||||
#!/bin/bash
|
||||
# AI Newsletter Digest - Fetch and analyze
|
||||
set -e
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
EMAIL_SKILL="$SCRIPT_DIR/../../skills/imap-smtp-email"
|
||||
|
||||
echo "🤖 AI Newsletter Digest Generator" >&2
|
||||
echo "============================================================" >&2
|
||||
|
||||
# Fetch AI-related emails from last 7 days
|
||||
echo "📧 Fetching AI newsletters from last 7 days..." >&2
|
||||
|
||||
python3 "$EMAIL_SKILL/scripts/imap-py.py" search \
|
||||
--subject "AI" \
|
||||
--recent 7d \
|
||||
--limit 20 > /tmp/ai_emails_list.txt
|
||||
|
||||
# Count how many we found
|
||||
EMAIL_COUNT=$(grep -c "^●" /tmp/ai_emails_list.txt || echo "0")
|
||||
echo "🎯 Found $EMAIL_COUNT AI-related emails" >&2
|
||||
|
||||
if [ "$EMAIL_COUNT" = "0" ]; then
|
||||
echo "No AI newsletters found in the last 7 days."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Extract UIDs
|
||||
UIDS=$(grep "UID:" /tmp/ai_emails_list.txt | awk '{print $3}' | head -10)
|
||||
|
||||
echo "📖 Fetching content from top 10 newsletters..." >&2
|
||||
echo "" > /tmp/ai_newsletters_content.txt
|
||||
|
||||
for uid in $UIDS; do
|
||||
echo " • Fetching email $uid..." >&2
|
||||
python3 "$EMAIL_SKILL/scripts/imap-py.py" fetch "$uid" >> /tmp/ai_newsletters_content.txt 2>/dev/null || true
|
||||
echo -e "\n\n========================================\n\n" >> /tmp/ai_newsletters_content.txt
|
||||
done
|
||||
|
||||
echo "✅ Content fetched!" >&2
|
||||
echo "" >&2
|
||||
echo "📋 Newsletter sources:" >&2
|
||||
grep "^From:" /tmp/ai_newsletters_content.txt | sort -u | head -10 >&2
|
||||
echo "" >&2
|
||||
|
||||
# Output the file path for the agent to analyze
|
||||
echo "/tmp/ai_newsletters_content.txt"
|
||||
38
automations/ai-newsletter-digest/daily-digest.sh
Executable file
38
automations/ai-newsletter-digest/daily-digest.sh
Executable file
@@ -0,0 +1,38 @@
|
||||
#!/bin/bash
|
||||
# Daily AI Newsletter Digest - Fast Reliable Version
|
||||
set -e
|
||||
|
||||
EMAIL_SKILL="/home/openclaw/.openclaw/workspace/skills/imap-smtp-email"
|
||||
OUTPUT_FILE="/tmp/ai-newsletter-emails.json"
|
||||
|
||||
echo "🤖 Daily AI Newsletter Digest" >&2
|
||||
echo "============================================================" >&2
|
||||
echo "$(date)" >&2
|
||||
echo "" >&2
|
||||
|
||||
echo "🔍 Searching for AI newsletters from last 48 hours..." >&2
|
||||
|
||||
# Single search for all recent emails, then filter locally
|
||||
cd "$EMAIL_SKILL"
|
||||
|
||||
# Get recent emails and filter for AI newsletters (expanded to 48h and more sources)
|
||||
ALL_EMAILS=$(node scripts/imap.js search --recent 48h --limit 100 2>/dev/null | jq '[.[] | select(.from | test("AI Valley|AI Secret|DeepView|Deep View|The Rundown|TLDR|Benedict|aivalley|aisecret|deepview|therundown|tldr|benedict"; "i"))]' 2>/dev/null || echo "[]")
|
||||
|
||||
# Save results
|
||||
echo "$ALL_EMAILS" > "$OUTPUT_FILE"
|
||||
|
||||
EMAIL_COUNT=$(echo "$ALL_EMAILS" | jq '. | length')
|
||||
echo "" >&2
|
||||
echo "🎯 Found $EMAIL_COUNT AI-related emails" >&2
|
||||
|
||||
if [ "$EMAIL_COUNT" -eq 0 ]; then
|
||||
echo "No new AI newsletters in the last 24 hours." >&2
|
||||
echo "[]"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "" >&2
|
||||
echo "📧 Ready to process $EMAIL_COUNT newsletters" >&2
|
||||
|
||||
# Output the emails
|
||||
cat "$OUTPUT_FILE"
|
||||
206
automations/ai-newsletter-digest/digest.py
Normal file
206
automations/ai-newsletter-digest/digest.py
Normal file
@@ -0,0 +1,206 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
AI Newsletter Digest
|
||||
Consolidates AI-related newsletters into a single, deduplicated summary
|
||||
"""
|
||||
import sys
|
||||
import os
|
||||
import json
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
from datetime import datetime
|
||||
import re
|
||||
|
||||
# Add skills to path
|
||||
WORKSPACE = Path(__file__).parent.parent.parent
|
||||
EMAIL_SKILL = WORKSPACE / "skills" / "imap-smtp-email"
|
||||
|
||||
# Newsletter keywords to look for in sender/subject
|
||||
AI_KEYWORDS = [
|
||||
'ai', 'artificial intelligence', 'machine learning', 'ml', 'llm',
|
||||
'gpt', 'claude', 'openai', 'anthropic', 'deepmind', 'neural',
|
||||
'chatgpt', 'transformer', 'diffusion', 'generative', 'newsletter'
|
||||
]
|
||||
|
||||
def log(msg):
|
||||
"""Log to stderr"""
|
||||
print(msg, file=sys.stderr)
|
||||
|
||||
def is_ai_newsletter(from_addr, subject):
|
||||
"""Check if email is likely an AI newsletter"""
|
||||
text = f"{from_addr} {subject}".lower()
|
||||
return any(keyword in text for keyword in AI_KEYWORDS)
|
||||
|
||||
def fetch_unread_emails(limit=50):
|
||||
"""Fetch unread emails using the IMAP skill"""
|
||||
log("📧 Fetching unread emails...")
|
||||
|
||||
cmd = [
|
||||
"python3",
|
||||
str(EMAIL_SKILL / "scripts" / "imap-py.py"),
|
||||
"search",
|
||||
"--unseen",
|
||||
"--limit", str(limit)
|
||||
]
|
||||
|
||||
result = subprocess.run(cmd, capture_output=True, text=True)
|
||||
|
||||
if result.returncode != 0:
|
||||
log(f"Error fetching emails: {result.stderr}")
|
||||
return []
|
||||
|
||||
# Parse the output
|
||||
emails = []
|
||||
lines = result.stdout.strip().split('\n')
|
||||
|
||||
current_email = {}
|
||||
for line in lines:
|
||||
line = line.strip()
|
||||
|
||||
if line.startswith('●') or line.startswith(' '):
|
||||
# Start of new email
|
||||
if 'UID:' in line:
|
||||
if current_email:
|
||||
emails.append(current_email)
|
||||
uid = line.split('UID:')[1].strip().split()[0]
|
||||
current_email = {'uid': uid}
|
||||
elif line.startswith('From:'):
|
||||
current_email['from'] = line.replace('From:', '').strip()
|
||||
elif line.startswith('Subject:'):
|
||||
current_email['subject'] = line.replace('Subject:', '').strip()
|
||||
elif line.startswith('Date:'):
|
||||
current_email['date'] = line.replace('Date:', '').strip()
|
||||
|
||||
if current_email:
|
||||
emails.append(current_email)
|
||||
|
||||
return emails
|
||||
|
||||
def fetch_email_body(uid):
|
||||
"""Fetch full email body"""
|
||||
log(f" Fetching email {uid}...")
|
||||
|
||||
cmd = [
|
||||
"python3",
|
||||
str(EMAIL_SKILL / "scripts" / "imap-py.py"),
|
||||
"fetch",
|
||||
uid
|
||||
]
|
||||
|
||||
result = subprocess.run(cmd, capture_output=True, text=True)
|
||||
|
||||
if result.returncode != 0:
|
||||
return None
|
||||
|
||||
# Extract body (everything after the separator line)
|
||||
parts = result.stdout.split('-' * 80)
|
||||
if len(parts) > 1:
|
||||
return parts[1].strip()
|
||||
|
||||
return result.stdout.strip()
|
||||
|
||||
def extract_key_points(newsletters):
|
||||
"""Use LLM to extract and deduplicate key points"""
|
||||
log("🧠 Analyzing newsletters and extracting key points...")
|
||||
|
||||
# Prepare newsletter content for analysis
|
||||
newsletter_texts = []
|
||||
for i, newsletter in enumerate(newsletters, 1):
|
||||
text = f"NEWSLETTER {i} - {newsletter['from']}\nSubject: {newsletter['subject']}\n\n{newsletter['body'][:3000]}"
|
||||
newsletter_texts.append(text)
|
||||
|
||||
combined = "\n\n" + "="*80 + "\n\n".join(newsletter_texts)
|
||||
|
||||
# Prompt for LLM
|
||||
prompt = f"""You are analyzing {len(newsletters)} AI-related newsletters. Extract the key information and insights, removing duplicates and synthesizing similar news across sources.
|
||||
|
||||
{combined}
|
||||
|
||||
Please provide:
|
||||
1. **Top AI News** - The most important developments mentioned (deduplicated)
|
||||
2. **Product Launches** - New tools, models, or features announced
|
||||
3. **Research Highlights** - Notable papers or breakthroughs
|
||||
4. **Industry Trends** - Patterns or themes across multiple newsletters
|
||||
5. **Notable Quotes** - Interesting perspectives from thought leaders
|
||||
|
||||
Format as markdown with clear sections. Be concise but informative. If the same news appears in multiple newsletters, mention it once and note it's widely covered."""
|
||||
|
||||
# Call LLM via openclaw (assuming this is running within openclaw context)
|
||||
# For now, create a temporary prompt file
|
||||
prompt_file = Path("/tmp/digest_prompt.txt")
|
||||
prompt_file.write_text(prompt)
|
||||
|
||||
log(" Generating digest with LLM...")
|
||||
log(" (This may take a moment...)")
|
||||
|
||||
# Return a placeholder for now - in production this would call the LLM
|
||||
# The agent running this will provide LLM access
|
||||
return {
|
||||
'prompt': prompt,
|
||||
'needs_llm': True
|
||||
}
|
||||
|
||||
def create_digest(newsletters):
|
||||
"""Create the final digest"""
|
||||
if not newsletters:
|
||||
return "No AI newsletters found in unread emails."
|
||||
|
||||
# For now, return structured data that the agent can analyze
|
||||
digest = {
|
||||
'count': len(newsletters),
|
||||
'sources': [n['from'] for n in newsletters],
|
||||
'newsletters': newsletters
|
||||
}
|
||||
|
||||
return digest
|
||||
|
||||
def main():
|
||||
log("🤖 AI Newsletter Digest Generator")
|
||||
log("=" * 60)
|
||||
|
||||
# Fetch unread emails
|
||||
emails = fetch_unread_emails(limit=50)
|
||||
|
||||
# Filter for AI newsletters
|
||||
log(f"📊 Found {len(emails)} unread emails")
|
||||
ai_newsletters = []
|
||||
|
||||
for email in emails:
|
||||
if is_ai_newsletter(email.get('from', ''), email.get('subject', '')):
|
||||
ai_newsletters.append(email)
|
||||
|
||||
log(f"🎯 Found {len(ai_newsletters)} AI-related newsletters")
|
||||
|
||||
if not ai_newsletters:
|
||||
print(json.dumps({'status': 'no_newsletters', 'message': 'No AI newsletters found'}))
|
||||
return
|
||||
|
||||
# Fetch full content for each
|
||||
log("📖 Fetching full newsletter content...")
|
||||
for newsletter in ai_newsletters[:10]: # Limit to 10 to avoid overwhelming
|
||||
body = fetch_email_body(newsletter['uid'])
|
||||
if body:
|
||||
newsletter['body'] = body
|
||||
|
||||
# Filter out ones without body
|
||||
ai_newsletters = [n for n in ai_newsletters if 'body' in n]
|
||||
|
||||
log(f"✅ Successfully fetched {len(ai_newsletters)} newsletters")
|
||||
|
||||
# Output structured data for the agent to analyze
|
||||
result = {
|
||||
'status': 'success',
|
||||
'count': len(ai_newsletters),
|
||||
'sources': list(set([n['from'] for n in ai_newsletters])),
|
||||
'newsletters': ai_newsletters[:10] # Limit to 10
|
||||
}
|
||||
|
||||
print(json.dumps(result, indent=2))
|
||||
|
||||
# Log summary to stderr
|
||||
log("\n📋 Newsletter Sources:")
|
||||
for source in result['sources']:
|
||||
log(f" • {source}")
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
71
automations/ai-newsletter-digest/list-newsletters.py
Normal file
71
automations/ai-newsletter-digest/list-newsletters.py
Normal file
@@ -0,0 +1,71 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Quick list of AI newsletters in inbox
|
||||
"""
|
||||
import sys
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
WORKSPACE = Path(__file__).parent.parent.parent
|
||||
EMAIL_SKILL = WORKSPACE / "skills" / "imap-smtp-email"
|
||||
|
||||
AI_KEYWORDS = [
|
||||
'ai', 'artificial intelligence', 'machine learning', 'ml', 'llm',
|
||||
'gpt', 'claude', 'openai', 'anthropic', 'deepmind', 'neural',
|
||||
'chatgpt', 'transformer', 'diffusion', 'generative', 'newsletter'
|
||||
]
|
||||
|
||||
def is_ai_newsletter(from_addr, subject):
|
||||
text = f"{from_addr} {subject}".lower()
|
||||
return any(keyword in text for keyword in AI_KEYWORDS)
|
||||
|
||||
def main():
|
||||
# Fetch unread emails
|
||||
cmd = [
|
||||
"python3",
|
||||
str(EMAIL_SKILL / "scripts" / "imap-py.py"),
|
||||
"search",
|
||||
"--unseen",
|
||||
"--limit", "50"
|
||||
]
|
||||
|
||||
result = subprocess.run(cmd, capture_output=True, text=True)
|
||||
|
||||
if result.returncode != 0:
|
||||
print("Error fetching emails", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
# Parse output
|
||||
emails = []
|
||||
lines = result.stdout.strip().split('\n')
|
||||
|
||||
current_email = {}
|
||||
for line in lines:
|
||||
line = line.strip()
|
||||
|
||||
if 'UID:' in line:
|
||||
if current_email:
|
||||
emails.append(current_email)
|
||||
uid = line.split('UID:')[1].strip().split()[0]
|
||||
current_email = {'uid': uid}
|
||||
elif line.startswith('From:'):
|
||||
current_email['from'] = line.replace('From:', '').strip()
|
||||
elif line.startswith('Subject:'):
|
||||
current_email['subject'] = line.replace('Subject:', '').strip()
|
||||
|
||||
if current_email:
|
||||
emails.append(current_email)
|
||||
|
||||
# Filter AI newsletters
|
||||
ai_newsletters = [e for e in emails if is_ai_newsletter(e.get('from', ''), e.get('subject', ''))]
|
||||
|
||||
print(f"Found {len(ai_newsletters)} AI newsletters out of {len(emails)} unread emails:\n")
|
||||
|
||||
for n in ai_newsletters:
|
||||
print(f"UID: {n['uid']}")
|
||||
print(f"From: {n.get('from', 'Unknown')}")
|
||||
print(f"Subject: {n.get('subject', 'No subject')}")
|
||||
print()
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -18,6 +18,21 @@ if ! git config --global credential.helper &>/dev/null; then
|
||||
echo " Run: git config --global credential.helper store" | tee -a "$LOG_FILE"
|
||||
fi
|
||||
|
||||
# Gotify alert function
|
||||
send_gotify() {
|
||||
local title="$1"
|
||||
local message="$2"
|
||||
local priority="${3:-0}"
|
||||
local gotify_url="${GOTIFY_URL:-http://runtipi.kangaroo-eel.ts.net:8129}"
|
||||
local gotify_token="${GOTIFY_API_KEY}"
|
||||
|
||||
if [[ -n "$gotify_token" ]]; then
|
||||
curl -s -X POST "$gotify_url/message?token=$gotify_token" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "{\"title\":\"$title\",\"message\":\"$message\",\"priority\":$priority}" > /dev/null 2>&1
|
||||
fi
|
||||
}
|
||||
|
||||
# Function to backup a repo
|
||||
backup_repo() {
|
||||
local path=$1
|
||||
@@ -36,8 +51,7 @@ backup_repo() {
|
||||
echo "✅ $name: Backup successful" | tee -a "$LOG_FILE"
|
||||
else
|
||||
echo "❌ $name: Push failed - check credentials" | tee -a "$LOG_FILE"
|
||||
echo " To fix: git config --global credential.helper store" | tee -a "$LOG_FILE"
|
||||
echo " Then: cd $path && git push (enter credentials once)" | tee -a "$LOG_FILE"
|
||||
send_gotify "⚠️ Backup Failed" "$name push to Gitea failed. Check credentials." 5
|
||||
fi
|
||||
else
|
||||
echo "⏭️ $name: No changes to backup" | tee -a "$LOG_FILE"
|
||||
@@ -56,3 +70,6 @@ echo "📄 Log saved to: $LOG_FILE" | tee -a "$LOG_FILE"
|
||||
echo "" | tee -a "$LOG_FILE"
|
||||
echo "📊 Recent backups:" | tee -a "$LOG_FILE"
|
||||
cd /home/openclaw/.openclaw/workspace && git log --oneline -3 | tee -a "$LOG_FILE"
|
||||
|
||||
# Send success notification
|
||||
send_gotify "✅ Backup Complete" "OpenClaw backup to Gitea succeeded"
|
||||
105
automations/morning-briefing/README.md
Normal file
105
automations/morning-briefing/README.md
Normal file
@@ -0,0 +1,105 @@
|
||||
# Morning Intelligence Briefing - n8n Workflow
|
||||
|
||||
## 📁 Files
|
||||
|
||||
1. **n8n-workflow.json** - Full workflow (requires credentials)
|
||||
2. **n8n-workflow-simple.json** - Simpler version (fewer dependencies)
|
||||
|
||||
## 🚀 Setup Instructions
|
||||
|
||||
### Step 1: Import to n8n
|
||||
1. Open your n8n: https://n8n.kangaroo-eel.ts.net
|
||||
2. Go to **Workflows** → **Import from File**
|
||||
3. Select `n8n-workflow.json`
|
||||
|
||||
### Step 2: Configure Credentials
|
||||
|
||||
You'll need to create these credentials in n8n:
|
||||
|
||||
#### A. FreshRSS (HTTP Basic Auth)
|
||||
- **Name**: FreshRSS Credentials
|
||||
- **Username**: Anthony
|
||||
- **Password**: RecOvery2026!
|
||||
|
||||
#### B. Perplexity (HTTP Header Auth)
|
||||
- **Name**: Perplexity API
|
||||
- **Header Name**: Authorization
|
||||
- **Header Value**: Bearer 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
|
||||
|
||||
#### C. Telegram Bot
|
||||
- **Name**: Telegram Bot
|
||||
- **Access Token**: (Your bot token - I can help get this)
|
||||
|
||||
### Step 3: Test
|
||||
1. Click **Execute Workflow**
|
||||
2. Check if you receive a Telegram message
|
||||
|
||||
### Step 4: Activate
|
||||
1. Toggle **Active** switch
|
||||
2. Workflow runs daily at 6:00 AM (Perth time)
|
||||
|
||||
## 🔧 What It Does
|
||||
|
||||
```
|
||||
6:00 AM Trigger
|
||||
↓
|
||||
Fetch FreshRSS (your RSS feeds)
|
||||
↓
|
||||
Fetch News Aggregator (HN, GitHub, Product Hunt, etc.)
|
||||
↓
|
||||
Query Perplexity AI ("Today's top news")
|
||||
↓
|
||||
Combine all sources
|
||||
↓
|
||||
AI Deduplication & Summarization
|
||||
↓
|
||||
Telegram to You (5-7 key stories)
|
||||
```
|
||||
|
||||
## 📱 Expected Output Example
|
||||
|
||||
```
|
||||
🌅 Morning Intelligence Briefing
|
||||
|
||||
1. 🔥 OpenAI announces GPT-5...
|
||||
2. 💰 Tech stocks rally...
|
||||
3. 🚀 New AI tool from...
|
||||
4. 📰 Major news from...
|
||||
5. 💡 Innovation in...
|
||||
|
||||
---
|
||||
Sources: FreshRSS, News Aggregator, Perplexity AI
|
||||
```
|
||||
|
||||
## ⚠️ Troubleshooting
|
||||
|
||||
**Issue**: FreshRSS authentication fails
|
||||
**Fix**: Check API password in FreshRSS → Settings → Profile → API Management
|
||||
|
||||
**Issue**: News Aggregator fails
|
||||
**Fix**: Ensure Python venv exists at `/skills/news-aggregator-skill/.venv`
|
||||
|
||||
**Issue**: Telegram not received
|
||||
**Fix**: Check bot token and chat ID (1793951355)
|
||||
|
||||
## 🎨 Customization
|
||||
|
||||
### Change time:
|
||||
Edit "6 AM Daily" node → Change trigger time
|
||||
|
||||
### Add more sources:
|
||||
Add HTTP Request nodes for additional APIs
|
||||
|
||||
### Change summary style:
|
||||
Edit "AI Summarize" system prompt
|
||||
|
||||
### Filter categories:
|
||||
Add Code node to filter FreshRSS by category
|
||||
|
||||
## 📞 Support
|
||||
|
||||
Need help? Ask me to:
|
||||
- Debug credential issues
|
||||
- Customize the workflow
|
||||
- Add more news sources
|
||||
- Change the schedule
|
||||
15
automations/morning-briefing/morning-briefing.sh
Normal file
15
automations/morning-briefing/morning-briefing.sh
Normal file
@@ -0,0 +1,15 @@
|
||||
#!/bin/bash
|
||||
# Morning Briefing - Premium Version
|
||||
# Calls OpenClaw agent to generate comprehensive briefing
|
||||
|
||||
set -e
|
||||
|
||||
echo "🌅 Morning Briefing Generator" >&2
|
||||
echo "============================" >&2
|
||||
|
||||
# Get weather
|
||||
WEATHER=$(curl -s "wttr.in/Perth?format=%l:+%c+%t+%h+wind:%w" 2>/dev/null || echo "Weather unavailable")
|
||||
echo "Weather: $WEATHER" >&2
|
||||
|
||||
# Output just the weather for now - the agent will build the full briefing
|
||||
echo "WEATHER=$WEATHER"
|
||||
208
automations/morning-briefing/n8n-workflow.json
Normal file
208
automations/morning-briefing/n8n-workflow.json
Normal file
@@ -0,0 +1,208 @@
|
||||
{
|
||||
"name": "Morning Intelligence Briefing",
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {
|
||||
"rule": {
|
||||
"interval": [
|
||||
{
|
||||
"field": "hours",
|
||||
"hoursInterval": 24,
|
||||
"triggerAtHour": 6
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"id": "cron-trigger",
|
||||
"name": "6 AM Daily",
|
||||
"type": "n8n-nodes-base.scheduleTrigger",
|
||||
"typeVersion": 1,
|
||||
"position": [250, 300]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"url": "http://freshrss.kangaroo-eel.ts.net/api/greader.php/reader/api/0/stream/items/ids",
|
||||
"sendQuery": true,
|
||||
"queryParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "output",
|
||||
"value": "json"
|
||||
},
|
||||
{
|
||||
"name": "n",
|
||||
"value": "20"
|
||||
}
|
||||
]
|
||||
},
|
||||
"options": {}
|
||||
},
|
||||
"id": "freshrss-fetch",
|
||||
"name": "Fetch FreshRSS",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"typeVersion": 4.1,
|
||||
"position": [450, 200],
|
||||
"credentials": {
|
||||
"httpBasicAuth": {
|
||||
"id": "freshrss-creds",
|
||||
"name": "FreshRSS Credentials"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"command": "cd /home/openclaw/.openclaw/workspace/skills/news-aggregator-skill && .venv/bin/python scripts/fetch_news.py --source all --limit 15"
|
||||
},
|
||||
"id": "news-aggregator",
|
||||
"name": "News Aggregator",
|
||||
"type": "n8n-nodes-base.executeCommand",
|
||||
"typeVersion": 1,
|
||||
"position": [450, 400]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"model": "llama-3.1-sonar-large-128k-online",
|
||||
"messages": {
|
||||
"message": [
|
||||
{
|
||||
"role": "user",
|
||||
"content": "What are the top 5 tech, AI, and business news stories from today?"
|
||||
}
|
||||
]
|
||||
},
|
||||
"options": {}
|
||||
},
|
||||
"id": "perplexity-search",
|
||||
"name": "Perplexity Search",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"typeVersion": 4.1,
|
||||
"position": [650, 300],
|
||||
"credentials": {
|
||||
"httpHeaderAuth": {
|
||||
"id": "perplexity-creds",
|
||||
"name": "Perplexity API"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"jsCode": "// Combine all news sources and deduplicate\nconst freshRSS = $input.first().json;\nconst newsAgg = $input.all()[1].json;\nconst perplexity = $input.all()[2].json;\n\n// Create combined list\nlet allStories = [];\n\n// Add FreshRSS items\nif (freshRSS && freshRSS.items) {\n allStories = allStories.concat(freshRSS.items.map(item => ({\n source: 'FreshRSS',\n title: item.title,\n url: item.url,\n time: item.timestamp\n })));\n}\n\n// Add News Aggregator items\nif (newsAgg && Array.isArray(newsAgg)) {\n allStories = allStories.concat(newsAgg.map(item => ({\n source: item.source || 'News Aggregator',\n title: item.title,\n url: item.url,\n heat: item.heat\n })));\n}\n\n// Add Perplexity insights\nif (perplexity && perplexity.choices) {\n allStories.push({\n source: 'Perplexity AI',\n title: 'AI-Synthesized Briefing',\n content: perplexity.choices[0].message.content\n });\n}\n\n// Return combined for AI analysis\nreturn [{\n json: {\n stories: allStories,\n count: allStories.length,\n sources: ['FreshRSS', 'News Aggregator', 'Perplexity']\n }\n}];"
|
||||
},
|
||||
"id": "combine-sources",
|
||||
"name": "Combine Sources",
|
||||
"type": "n8n-nodes-base.code",
|
||||
"typeVersion": 2,
|
||||
"position": [850, 300]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"model": "gpt-4o-mini",
|
||||
"options": {},
|
||||
"messages": {
|
||||
"message": [
|
||||
{
|
||||
"role": "system",
|
||||
"content": "You are a news editor creating a morning intelligence briefing. Analyze the provided news stories from multiple sources, remove duplicates, identify genuinely new information, and create a concise summary of the top 5-7 most important stories. Format as markdown with emojis. Focus on tech, AI, business, and relevant world news."
|
||||
},
|
||||
{
|
||||
"role": "user",
|
||||
"content": "=Create a morning briefing from these sources:\n\n{{ $json.stories }}"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"id": "ai-summarize",
|
||||
"name": "AI Summarize",
|
||||
"type": "@n8n/n8n-nodes-langchain.agent",
|
||||
"typeVersion": 1.6,
|
||||
"position": [1050, 300]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"chatId": "1793951355",
|
||||
"text": "=🌅 **Morning Intelligence Briefing**\n\n{{ $json.output }}\n\n---\n_Sources: FreshRSS, News Aggregator, Perplexity AI_",
|
||||
"options": {}
|
||||
},
|
||||
"id": "telegram-send",
|
||||
"name": "Send Telegram",
|
||||
"type": "n8n-nodes-base.telegram",
|
||||
"typeVersion": 1.1,
|
||||
"position": [1250, 300],
|
||||
"credentials": {
|
||||
"telegramApi": {
|
||||
"id": "telegram-bot-creds",
|
||||
"name": "Telegram Bot"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"connections": {
|
||||
"6 AM Daily": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Fetch FreshRSS",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
},
|
||||
{
|
||||
"node": "News Aggregator",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Fetch FreshRSS": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Combine Sources",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"News Aggregator": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Combine Sources",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Combine Sources": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "AI Summarize",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"AI Summarize": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Send Telegram",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"settings": {
|
||||
"executionOrder": "v1"
|
||||
},
|
||||
"staticData": null,
|
||||
"tags": [],
|
||||
"pinData": {},
|
||||
"description": "Daily morning briefing combining FreshRSS, News Aggregator, and Perplexity AI into a single Telegram message at 6 AM"
|
||||
}
|
||||
Reference in New Issue
Block a user