Last active
December 16, 2025 16:07
-
-
Save zalez/941e52d8df0a6feea488578427923d72 to your computer and use it in GitHub Desktop.
AI news briefing generator, using RSS and llm with Perplexity and Claude plugins.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # Define a function for creating an AI-generated news briefing. | |
| # | |
| # Prerequisites: | |
| # - fish: https://fishshell.com/ | |
| # - Python: https://python.org/ | |
| # - uv: https://docs.astral.sh/uv/ | |
| # - llm: https://github.com/simonw/llm | |
| # - llm-perplexity: https://github.com/hex/llm-perplexity | |
| # - llm-anthropic: https://github.com/simonw/llm-anthropic | |
| # - API keys for Perplexity (https://www.perplexity.ai/api-platform) and Claude (https://claude.com/platform/api) | |
| # | |
| # Installation: | |
| # `source briefing.fish`` | |
| # | |
| # Usage: | |
| # `briefing >briefing.md` | |
| # `cat briefing.md` | |
| # | |
| # Use `glow -p <briefing.md` for a nicer experience (https://github.com/charmbracelet/glow) | |
| # | |
| function briefing | |
| set -gx today (date) | |
| set -gx location "Munich, Germany" | |
| set -gx reader "Constantin, a German freelance advisor born on 1971 and based in Munich, Germany, with a wide variety of knowledge and interests including cloud computing, AI and ML, technology in general, public speaking, health and fitness, science-fiction, electronic music, blogging, YouTube film making, and more. He is married, has two daughters and a Dachshund called Elvis. He enjoys coding up pet projects, programming synths, cooking, and running barefoot." | |
| set -gx briefing_dir (mktemp -d) | |
| set feeds \ | |
| "https://www.tagesschau.de/infoservices/alle-meldungen-100~rss2.xml" \ | |
| "https://www.spiegel.de/schlagzeilen/rss/0,5291,,00.xml" \ | |
| "https://rss.sueddeutsche.de/rss/Topthemen" \ | |
| "https://feeds.cms.handelsblatt.com/schlagzeilen" \ | |
| "https://rss.dw.com/atom/rss-en-all" \ | |
| "https://feeds.bbci.co.uk/news/world/rss.xml" \ | |
| "https://rss.nytimes.com/services/xml/rss/nyt/HomePage.xml" \ | |
| "https://feeds.washingtonpost.com/rss/world" \ | |
| "https://www.ft.com/rss/home/international" \ | |
| "https://www.heise.de/rss/heise-atom.xml" \ | |
| "https://www.cnet.com/rss/news/" \ | |
| "https://news.mit.edu/rss" \ | |
| "https://news.smol.ai/rss.xml" \ | |
| "https://news.ycombinator.com/rss" \ | |
| "https://www.positive.news/feed/" \ | |
| "https://feeds.feedburner.com/azquotes/quoteoftheday" | |
| # Utility script in Python to use feedparser to extract the newest 10 items, | |
| # Then print as Markdown. | |
| set rss_to_md_script " | |
| import sys, feedparser, time | |
| f = feedparser.parse(sys.stdin.read()) | |
| entries = sorted( | |
| f.entries, | |
| key=lambda e: e.get('published_parsed') or e.get('updated_parsed') or (0,) | |
| ) | |
| for e in entries[-10:][::-1]: | |
| ts = e.get('published_parsed') or e.get('updated_parsed') | |
| date = time.strftime('%Y-%m-%d %H:%M:%S', ts) if ts else '' | |
| title = (e.get('title') or '').replace('\n', ' ').strip() | |
| link = e.get('link') or '' | |
| # Prefer summary, then description, then full content; keep all text | |
| content = e.get('summary', '') | |
| if not content: | |
| content = e.get('description', '') | |
| if not content and e.get('content'): | |
| content = ' '.join( | |
| (c.get('value') or '') for c in e.get('content') if isinstance(c, dict) | |
| ) | |
| content = content.strip() | |
| if content: | |
| print(f'- [{title}]({link}) ({date})\\n\\n {content}\\n') | |
| else: | |
| print(f'- [{title}]({link}) ({date})') | |
| " | |
| echo "Gathering information..." >&2 | |
| # Create a temporary venv with feedparser installed to run our Python code. | |
| set venv_dir $briefing_dir/venv | |
| uv venv $venv_dir 2> /dev/null | |
| uv pip install --python $venv_dir feedparser 2> /dev/null | |
| set python_bin $venv_dir/bin/python | |
| # Download all feeds in parallel (the & after end) | |
| set counter 0 | |
| echo "Starting news feed download jobs..." >&2 | |
| for feed in $feeds | |
| set file $briefing_dir/newsfeed-$counter.txt | |
| echo -e "Feed: $feed\n\n" >>$file | |
| curl -s $feed | \ | |
| $python_bin -c "$rss_to_md_script" >> $file 2> /dev/null & | |
| set counter (math $counter + 1) | |
| end | |
| # Get additional information from Perplexity in parallel | |
| echo "Starting Perplexity query jobs..." >&2 | |
| llm -m sonar-pro "Provide the weather forecast for today, $today in $location, including UV index and best times to go outdoors." >>$briefing_dir/weather.txt & | |
| llm -m sonar-pro "Compile a list of 10 recently published AI productivity tips from sources like the Claude team, the OpenAI team, from Ethan Mollick or similar. Make it diverse and instructive." >>$briefing_dir/ai-tips.txt & | |
| llm -m sonar-pro "Compile a list of 10 recently published personal productivity tips from people similar to or including Merlin Mann, Leo Babauta, Cal Newport, and Ryder Carroll. Make it diverse and instructive." >>$briefing_dir/productivity-tips.txt & | |
| llm -m sonar-pro "Today is $today. List any German or international holidays for today, as well as any other interesting facts for this day, famous birthdays or interesting historic events that happened on the same day." >>$briefing_dir/todays-facts.txt & | |
| # Wait for all parallel jobs to finish | |
| set total (count (jobs)) | |
| while true | |
| set remaining (count (jobs)) | |
| if test $remaining -eq 0 | |
| echo -e "\r\033[KAll jobs done." >&2 | |
| echo >&2 | |
| break | |
| end | |
| set done (math $total - $remaining) | |
| echo -ne "\r\033[K$done / $total jobs finished, $remaining remaining..." >&2 | |
| sleep 1 | |
| end | |
| echo "Combining news data..." >&2 | |
| for file in $briefing_dir/*.txt | |
| echo -e "$file:\n" >>$briefing_dir/all-news.txt | |
| cat $file >>$briefing_dir/all-news.txt | |
| echo -e "\n---\n" >>$briefing_dir/all-news.txt | |
| end | |
| set total_size (wc -c < $briefing_dir/all-news.txt | string trim) | |
| set approx_tokens (math "$total_size / 4" ) | |
| echo "Total news data size: $total_size Bytes (approx. $approx_tokens tokens)" >&2 | |
| set briefing_prompt " | |
| Review the previously provided information carefully, then create a daily briefing for the current date in Munich, Germany, based on this information. | |
| Rules: | |
| - This report is for: $reader | |
| - For each section, focus on the most relevant, significant or impactful information | |
| - Always use English language, translate if necessary into English | |
| - Format in clean Markdown | |
| - Feel free to add emojis to lighten things up | |
| Use this exact structure: | |
| # π [Today's day of week], [Today's date] | |
| [If there are any German or international holidays, or other interesting facts for today, mention them here. Otherwise skip this line.] | |
| --- | |
| ## π Quote of the Day | |
| [Provide ONE random inspirational quote, fortune cookie wisdom, or daily wisdom. Include author if applicable. Make it relevant and motivational.] | |
| --- | |
| ## π‘οΈ Munich Weather | |
| [Current weather for Munich: temperature, conditions, UV index if relevant, best time for outdoor activities] | |
| --- | |
| ## π° News | |
| ### π©πͺTop 3 Germany | |
| 1. **[Headline]** - [2-3 sentence summary] (Source: [main source URL]) | |
| 2. **[Headline]** - [2-3 sentence summary] (Source: [main source URL] | |
| 3. **[Headline]** - [2-3 sentence summary] (Source: [main source URL]) | |
| ### π Top 3 World | |
| 1. **[Headline]** - [2-3 sentence summary] (Source: [main source URL]) | |
| 2. **[Headline]** - [2-3 sentence summary] (Source: [main source URL]) | |
| 3. **[Headline]** - [2-3 sentence summary] (Source: [main source URL]) | |
| ### π° Top 3 Economy and Finance | |
| 1. **[Headline]** - [2-3 sentence summary] (Source: [main source URL]) | |
| 2. **[Headline]** - [2-3 sentence summary] (Source: [main source URL]) | |
| 3. **[Headline]** - [2-3 sentence summary] (Source: [main source URL]) | |
| ### π€ Top 3 Science/Tech/AI | |
| 1. **[Headline]** - [2-3 sentence summary] (Source: [main source URL]) | |
| 2. **[Headline]** - [2-3 sentence summary] (Source: [main source URL]) | |
| 3. **[Headline]** - [2-3 sentence summary] (Source: [main source URL]) | |
| ### π¬ Top 3 Entertainment | |
| 1. **[Headline]** - [2-3 sentence summary] (Source: [main source URL]) | |
| 2. **[Headline]** - [2-3 sentence summary] (Source: [main source URL]) | |
| 3. **[Headline]** - [2-3 sentence summary] (Source: [main source URL]) | |
| ### π Top 3 Positive News | |
| 1. **[Headline]** - [2-3 sentence summary] (Source: [main source URL]) | |
| 2. **[Headline]** - [2-3 sentence summary] (Source: [main source URL]) | |
| 3. **[Headline]** - [2-3 sentence summary] (Source: [main source URL]) | |
| --- | |
| ## π‘ AI Productivity Tip | |
| [Choose one AI productivity tip. Mention the author/source. 2-3 sentences maximum.] | |
| --- | |
| ## β‘ Personal Productivity Tip | |
| [Choose one personal productivity tip. Mention the author/source. 2-3 sentences maximum.] | |
| --- | |
| ## π΄ Oblique Strategy | |
| [Provide one Oblique Strategy card from Brian Eno & Peter Schmidt's deck. Just the strategy text, no explanation needed.] | |
| --- | |
| ## βοΈ Writing Prompt | |
| [Generate ONE creative writing prompt suitable for a blog post or YouTube video script. Make it specific, actionable, and interesting. One sentence.] | |
| --- | |
| ## π€‘ Editor's Corner | |
| [Write a funny, but respectful comment or joke about the news you just provided as an editor.] | |
| " | |
| # Construct prompt | |
| echo "Constructing prompt and sending to Claude for final editing..." >&2 | |
| echo -e "Today is: $today\n\nRead the following collection of news and other information carefully:\n<INFORMATION_BLOCK>\n" >$briefing_dir/prefix.txt | |
| echo -e "\n<\INFORMATION_BLOCK>\n\nInstructions:\n" >$briefing_dir/end-news.txt | |
| echo -e "$briefing_prompt" >$briefing_dir/briefing-prompt.txt | |
| cat \ | |
| $briefing_dir/prefix.txt \ | |
| $briefing_dir/all-news.txt \ | |
| $briefing_dir/end-news.txt \ | |
| $briefing_dir/briefing-prompt.txt \ | |
| | llm -m claude-sonnet-4.5 | |
| echo "Done." >&2 | |
| echo >&2 | |
| echo "Cleaning up..." >&2 | |
| /bin/rm -rf $briefing_dir | |
| # echo $briefing_dir >&2 # For debugging | |
| echo "Done." >&2 | |
| echo >&2 | |
| end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment