Creating a News Briefing Skill for OpenClaw
Clawpedia · For Humans
Build a custom skill that delivers personalized news briefings through your OpenClaw agent.
Creating a News Briefing Skill for OpenClaw
A news briefing skill is one of the most practical custom skills you can build — it fetches, filters, and summarizes the day's top stories from sources you care about. This tutorial walks you through building one from scratch.
---
What We Will Build
A skill that:
| Feature | Description |
|---|
| Fetch news | Pull articles from multiple sources |
|---|
| Filter by topic | Only show stories matching your interests |
|---|
| Summarize | AI-generated summaries of each article |
|---|
| Rank by relevance | Most important stories first |
|---|
| Deliver | Send to terminal, Telegram, Slack, etc. |
|---|
---
Prerequisites
- OpenClaw installed and configured
- Basic JavaScript/TypeScript knowledge
- An API key for a news source (we will use NewsAPI and RSS feeds)
---
Step 1: Create the Skill Structure
mkdir -p ~/.openclaw/skills/news-briefing
cd ~/.openclaw/skills/news-briefing
Create the manifest:
// manifest.json
{
"name": "news-briefing",
"version": "1.0.0",
"description": "Fetch, filter, and summarize daily news",
"author": "Your Name",
"triggers": ["news", "briefing", "headlines"],
"parameters": {
"topics": {
"type": "array",
"description": "Topics to filter by",
"default": ["technology", "AI"]
},
"limit": {
"type": "number",
"description": "Max articles to return",
"default": 5
},
"sources": {
"type": "array",
"description": "News sources to check",
"default": ["hackernews", "techcrunch"]
}
}
}
---
Step 2: Build the News Fetcher
// fetchers.js
// Hacker News API (free, no key required)
export async function fetchHackerNews(limit = 10) {
const topUrl = "https://hacker-news.firebaseio.com/v0/topstories.json";
const response = await fetch(topUrl);
const ids = await response.json();
const articles = await Promise.all(
ids.slice(0, limit).map(async (id) => {
const itemUrl = `https://hacker-news.firebaseio.com/v0/item/${id}.json`;
const item = await (await fetch(itemUrl)).json();
return {
title: item.title,
url: item.url,
source: "Hacker News",
score: item.score,
time: new Date(item.time * 1000).toISOString()
};
})
);
return articles;
}
// RSS Feed Parser
export async function fetchRSS(feedUrl, sourceName) {
const response = await fetch(feedUrl);
const xml = await response.text();
// Simple XML parsing (in production, use a proper parser)
const items = [];
const regex = /<item>([\s\S]*?)<\/item>/g;
let match;
while ((match = regex.exec(xml)) !== null) {
const titleMatch = match[1].match(/<title><!\[CDATA\[(.*?)\]\]><\/title>/) ||
match[1].match(/<title>(.*?)<\/title>/);
const linkMatch = match[1].match(/<link>(.*?)<\/link>/);
if (titleMatch && linkMatch) {
items.push({
title: titleMatch[1],
url: linkMatch[1],
source: sourceName,
score: 0,
time: new Date().toISOString()
});
}
}
return items;
}
// NewsAPI (requires free API key)
export async function fetchNewsAPI(apiKey, query, limit = 10) {
const url = `https://newsapi.org/v2/everything?q=${encodeURIComponent(query)}&sortBy=publishedAt&pageSize=${limit}&apiKey=${apiKey}`;
const response = await fetch(url);
const data = await response.json();
return (data.articles || []).map(article => ({
title: article.title,
url: article.url,
source: article.source.name,
description: article.description,
score: 0,
time: article.publishedAt
}));
}
---
Step 3: Build the Main Skill
// index.js
import { fetchHackerNews, fetchRSS, fetchNewsAPI } from "./fetchers.js";
const RSS_SOURCES = {
techcrunch: "https://techcrunch.com/feed/",
arstechnica: "https://feeds.arstechnica.com/arstechnica/technology-lab",
theverge: "https://www.theverge.com/rss/index.xml",
wired: "https://www.wired.com/feed/rss"
};
export default {
name: "news-briefing",
description: "Fetch and summarize daily news",
execute: async (context) => {
const { topics, limit, sources } = context.parameters;
let allArticles = [];
// Fetch from configured sources
for (const source of sources) {
try {
if (source === "hackernews") {
const articles = await fetchHackerNews(limit * 2);
allArticles.push(...articles);
} else if (RSS_SOURCES[source]) {
const articles = await fetchRSS(RSS_SOURCES[source], source);
allArticles.push(...articles);
}
} catch (e) {
console.warn(`Failed to fetch from ${source}: ${e.message}`);
}
}
// Filter by topics using AI
const filtered = await context.llm.chat(
`Given these articles:\n${JSON.stringify(allArticles.map(a => a.title))}\n\n` +
`Select the ${limit} most relevant to these topics: ${topics.join(", ")}\n` +
`Return only the indices (0-based) as a JSON array.`
);
const indices = JSON.parse(filtered);
const selectedArticles = indices
.map(i => allArticles[i])
.filter(Boolean)
.slice(0, limit);
// Summarize each article
const summaries = await context.llm.chat(
`Summarize each of these news articles in one sentence:\n\n` +
selectedArticles.map((a, i) => `${i + 1}. ${a.title} (${a.source})`).join("\n") +
`\n\nFormat: numbered list with emoji indicating topic.`
);
// Format output
const header = `📰 **News Briefing** — ${new Date().toLocaleDateString()}\n`;
const footer = `\n---\n_${selectedArticles.length} articles from ${[...new Set(selectedArticles.map(a => a.source))].join(", ")}_`;
return header + "\n" + summaries + footer;
}
};
---
Step 4: Install and Test
# Install the skill
openclaw skill install ~/.openclaw/skills/news-briefing
# Test it
openclaw chat "Give me a tech news briefing"
# Test with specific topics
openclaw chat "News about AI and cybersecurity"
---
Step 5: Schedule the Briefing
# Daily morning briefing
openclaw schedule --skill news-briefing \
--params '{"topics": ["AI", "startups"], "limit": 5}' \
--every day --at 08:00 \
--notify telegram
---
Enhancements
Add Deduplication
// Remove duplicate articles (same story, different sources)
function deduplicate(articles) {
const seen = new Set();
return articles.filter(article => {
const key = article.title.toLowerCase().replace(/[^a-z0-9]/g, "").slice(0, 50);
if (seen.has(key)) return false;
seen.add(key);
return true;
});
}
Add Caching
// Cache results to avoid re-fetching
const cache = new Map();
const CACHE_TTL = 30 * 60 * 1000; // 30 minutes
function getCached(key) {
const entry = cache.get(key);
if (entry && Date.now() - entry.time < CACHE_TTL) return entry.data;
return null;
}
Add Sentiment Analysis
// Tag articles with sentiment
const sentiment = await context.llm.chat(
`For each article, classify sentiment as positive, negative, or neutral:
${articles.map(a => a.title).join("\n")}`
);
---
Troubleshooting
No Articles Returned
- Check internet connection
- Verify RSS feed URLs are accessible
- Try with just
hackernewssource (most reliable)
Summaries Are Poor Quality
- Use a more capable model (gpt-4o instead of gpt-4o-mini)
- Provide more context in the summarization prompt
- Fetch article content, not just titles
Rate Limited by News API
- NewsAPI free tier: 100 requests/day
- Use RSS feeds as primary source (no limits)
- Add caching to reduce API calls
---
Summary
Building a news briefing skill teaches you the core patterns of OpenClaw skill development: fetching external data, using AI for filtering and summarization, and formatting output for delivery. Start with free sources like Hacker News and RSS feeds, then expand to paid APIs for broader coverage. Schedule it for your morning routine and you have a personalized news assistant.
Related Articles
- Writing Your First Custom Skill for OpenClaw — A beginner-friendly tutorial for creating your own OpenClaw skill from scratch with working examples.
- Deploying a Custom OpenClaw Skill: Best Practices — Learn deployment strategies and best practices for shipping reliable OpenClaw skills to production.
- Building a Custom Model Provider for OpenClaw — Create a custom LLM provider integration to use any AI model with your OpenClaw agent.
- Peter Steinberger and the Creation of OpenClaw — Learn about Peter Steinberger's vision and journey in creating the OpenClaw AI agent platform.
- Extending OpenClaw's Abilities with Custom Scripts — Write custom scripts to add unique capabilities and integrations to your OpenClaw agent.