Auto backup: 2026-02-17 18:00

This commit is contained in:
Krilly
2026-02-17 18:00:21 +00:00
parent 8902a93add
commit ebd2f70af7
9 changed files with 768 additions and 3 deletions

View 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`

View 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"

View 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"

View 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()

View 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()

View File

@@ -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" echo " Run: git config --global credential.helper store" | tee -a "$LOG_FILE"
fi 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 # Function to backup a repo
backup_repo() { backup_repo() {
local path=$1 local path=$1
@@ -36,8 +51,7 @@ backup_repo() {
echo "$name: Backup successful" | tee -a "$LOG_FILE" echo "$name: Backup successful" | tee -a "$LOG_FILE"
else else
echo "$name: Push failed - check credentials" | tee -a "$LOG_FILE" echo "$name: Push failed - check credentials" | tee -a "$LOG_FILE"
echo " To fix: git config --global credential.helper store" | tee -a "$LOG_FILE" send_gotify "⚠️ Backup Failed" "$name push to Gitea failed. Check credentials." 5
echo " Then: cd $path && git push (enter credentials once)" | tee -a "$LOG_FILE"
fi fi
else else
echo "⏭️ $name: No changes to backup" | tee -a "$LOG_FILE" 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 "" | tee -a "$LOG_FILE"
echo "📊 Recent backups:" | tee -a "$LOG_FILE" echo "📊 Recent backups:" | tee -a "$LOG_FILE"
cd /home/openclaw/.openclaw/workspace && git log --oneline -3 | 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"

View 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

View 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"

View 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"
}