This guide will walk you through creating your own custom provider and action in Eliza. We'll build something practical: a news digest system that can fetch and summarize news from your favorite sources. I've used this action and provider with my own agent, arjux.
First, let's create a new repository from the eliza-starter template:
git clone https://github.com/YOUR_USERNAME/YOUR_REPO_NAME
cd YOUR_REPO_NAME
pnpm install
Let's create a provider that fetches news from your chosen RSS feeds. Make sure you replace the source URL's with your own.
// src/providers/newsDigest.provider.ts
import { Provider, IAgentRuntime, Memory, State } from "@ai16z/eliza";
import Parser from 'rss-parser';
import * as cheerio from 'cheerio';
interface NewsItem {
title: string;
link: string;
pubDate: string;
content: string;
source: string;
}
// Add your favorite news sources here!
const RSS_FEEDS = [
{ title: "Your Tech News Source", url: "https://your-tech-news-source.com/feed" },
{ title: "Another News Site", url: "https://another-news-site.com/rss" },
// Add more feeds as needed
];
const parser = new Parser();
const newsDigestProvider: Provider = {
get: async (_runtime: IAgentRuntime, message: Memory, _state?: State): Promise => {
try {
// Check if this is a news-related request
const text = message.content.text.toLowerCase().trim();
const hasNews = text.includes('news');
const hasSecurity = text.includes('security') || text.includes('cyber');
if (!hasNews || !hasSecurity) {
return "";
}
// You can customize these keywords based on what you want to filter out
const unwantedKeywords = ['trending', 'price', 'stocks'];
if (unwantedKeywords.some(keyword => text.includes(keyword))) {
return "";
}
const last24Hours = Date.now() - 24 * 60 * 60 * 1000;
const allNews: NewsItem[] = [];
// Fetch from your RSS feeds
for (const feed of RSS_FEEDS) {
try {
const feedData = await parser.parseURL(feed.url);
const item = feedData.items[0];
if (item) {
const pubDate = new Date(item.pubDate || '').getTime();
if (pubDate > last24Hours) {
// Get the full article
const response = await fetch(item.link || '');
const html = await response.text();
const $ = cheerio.load(html);
// Customize this selector based on your news sources
const content = $('.article-content').text();
allNews.push({
title: item.title || '',
link: item.link || '',
pubDate: item.pubDate || '',
content: content.trim(),
source: feed.title
});
}
}
} catch (error) {
console.error(`Error fetching ${feed.title}:`, error);
// Continue with other feeds
}
}
// Get the latest stories
const topStories = allNews
.sort((a, b) => new Date(b.pubDate).getTime() - new Date(a.pubDate).getTime())
.slice(0, 3);
// Format your digest however you like!
return topStories.map(story => `
📰 ${story.title}
Source: ${story.source}
Published: ${new Date(story.pubDate).toLocaleString()}
Summary: ${story.content.substring(0, 250)}...
Read more: ${story.link}
`).join('\n\n');
} catch (error) {
console.error('Error in newsDigest provider:', error);
return 'Unable to fetch news stories right now. Please try again later.';
}
}
};
Now let's create an action that handles user requests. You can customize the keywords and responses.
// src/actions/newsDigest.action.ts
import { Action, IAgentRuntime, Memory } from "@ai16z/eliza";
import { newsDigestProvider } from "../providers/newsDigest.provider.js";
export const newsDigestAction: Action = {
name: "NEWS_DIGEST",
// Add more variations of how users might ask for news
similes: ["NEWS_UPDATE", "LATEST_NEWS", "WHATS_NEW"],
description: "Fetch and summarize the latest news from your favorite sources",
examples: [
[
{
user: "{{user1}}",
content: { text: "What's happening in tech today?" }
},
{
user: "{{user2}}",
content: { text: "I'll fetch the latest tech news for you", action: "NEWS_DIGEST" }
}
]
],
validate: async (_runtime: IAgentRuntime, message: Memory) => {
const text = message.content.text.toLowerCase().trim();
// Customize these filters for your use case
const unwantedKeywords = ['weather', 'sports'];
if (unwantedKeywords.some(keyword => text.includes(keyword))) {
return false;
}
// Add your own keyword combinations
const hasNews = text.includes('news') || text.includes('update');
const hasTopic = text.includes('tech') || text.includes('security');
return hasNews && hasTopic;
},
handler: async (runtime: IAgentRuntime, message: Memory) => {
try {
const news = await newsDigestProvider.get(runtime, message);
await runtime.messageManager.createMemory({
...message,
content: {
text: news,
action: "NEWS_DIGEST"
}
});
return true;
} catch (error) {
console.error("Error in news digest action:", error);
return false;
}
}
};
Finally, let's register your provider and action with your Eliza agent:
// src/index.ts
import { createAgent } from '@ai16z/eliza';
import { newsDigestProvider } from './providers/newsDigest.provider';
import { newsDigestAction } from './actions/newsDigest.action';
const agent = createAgent({
providers: [
['newsDigest', newsDigestProvider],
// Add your other providers here
],
actions: [
newsDigestAction,
// Add your other actions here
]
});
Now that we have our provider and action registered, let's build and start our agent:
# Build the project
pnpm run build src/index.ts
# Start the agent with the Eliza character
pnpm start --character="characters/eliza.character.json"
Now you have your own custom news digest system! You can extend this further by: