@elizaos/plugin-rss 2.0.0-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/node/index.node.js +1056 -0
- package/dist/node/index.node.js.map +20 -0
- package/package.json +108 -0
|
@@ -0,0 +1,1056 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __export = (target, all) => {
|
|
3
|
+
for (var name in all)
|
|
4
|
+
__defProp(target, name, {
|
|
5
|
+
get: all[name],
|
|
6
|
+
enumerable: true,
|
|
7
|
+
configurable: true,
|
|
8
|
+
set: (newValue) => all[name] = () => newValue
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
12
|
+
|
|
13
|
+
// utils.ts
|
|
14
|
+
var exports_utils = {};
|
|
15
|
+
__export(exports_utils, {
|
|
16
|
+
formatRelativeTime: () => formatRelativeTime,
|
|
17
|
+
extractUrls: () => extractUrls,
|
|
18
|
+
createMessageReply: () => createMessageReply
|
|
19
|
+
});
|
|
20
|
+
import { createUniqueUuid } from "@elizaos/core";
|
|
21
|
+
function createMessageReply(runtime, message, reply) {
|
|
22
|
+
return {
|
|
23
|
+
text: reply,
|
|
24
|
+
attachments: [],
|
|
25
|
+
source: message.content.source ?? "",
|
|
26
|
+
channelType: message.content.channelType,
|
|
27
|
+
inReplyTo: createUniqueUuid(runtime, message.id ?? "")
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
function extractUrls(text) {
|
|
31
|
+
const URL_MATCH = /(?:(?:https?|ftp):\/\/|www\.)[^\s<>"'`]+/gi;
|
|
32
|
+
const candidates = text.match(URL_MATCH) || [];
|
|
33
|
+
const results = [];
|
|
34
|
+
const seen = new Set;
|
|
35
|
+
for (const raw of candidates) {
|
|
36
|
+
const candidate = raw.replace(/^[([{<'"]+/, "");
|
|
37
|
+
let withScheme = candidate.startsWith("www.") ? `http://${candidate}` : candidate;
|
|
38
|
+
const TRAIL = /[)\]}>,.;!?:'"\u2026]$/;
|
|
39
|
+
while (withScheme && TRAIL.test(withScheme.slice(-1)) && !isValidUrl(withScheme)) {
|
|
40
|
+
withScheme = withScheme.slice(0, -1);
|
|
41
|
+
}
|
|
42
|
+
if (!isValidUrl(withScheme))
|
|
43
|
+
continue;
|
|
44
|
+
const normalized = new URL(withScheme).toString();
|
|
45
|
+
if (!seen.has(normalized)) {
|
|
46
|
+
seen.add(normalized);
|
|
47
|
+
results.push(normalized);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return results;
|
|
51
|
+
}
|
|
52
|
+
function isValidUrl(u) {
|
|
53
|
+
try {
|
|
54
|
+
new URL(u);
|
|
55
|
+
return true;
|
|
56
|
+
} catch {
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
function formatRelativeTime(timestamp) {
|
|
61
|
+
const timeSince = Date.now() - timestamp;
|
|
62
|
+
const minutesSince = Math.floor(timeSince / 60000);
|
|
63
|
+
const hoursSince = Math.floor(minutesSince / 60);
|
|
64
|
+
const daysSince = Math.floor(hoursSince / 24);
|
|
65
|
+
if (daysSince > 0) {
|
|
66
|
+
return `${daysSince} day${daysSince > 1 ? "s" : ""} ago`;
|
|
67
|
+
} else if (hoursSince > 0) {
|
|
68
|
+
return `${hoursSince} hour${hoursSince > 1 ? "s" : ""} ago`;
|
|
69
|
+
} else if (minutesSince > 0) {
|
|
70
|
+
return `${minutesSince} minute${minutesSince > 1 ? "s" : ""} ago`;
|
|
71
|
+
} else {
|
|
72
|
+
return "just now";
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
var init_utils = () => {};
|
|
76
|
+
|
|
77
|
+
// parser.ts
|
|
78
|
+
var exports_parser = {};
|
|
79
|
+
__export(exports_parser, {
|
|
80
|
+
parseRssToJson: () => parseRssToJson,
|
|
81
|
+
createEmptyFeed: () => createEmptyFeed
|
|
82
|
+
});
|
|
83
|
+
function parseTag(tag, str) {
|
|
84
|
+
const regex = new RegExp(`<${tag}(?:\\s+[^>]*)?>(.*?)</${tag}>`, "gs");
|
|
85
|
+
const matches = [];
|
|
86
|
+
let match = regex.exec(str);
|
|
87
|
+
while (match !== null) {
|
|
88
|
+
const content = match[1];
|
|
89
|
+
if (content !== undefined) {
|
|
90
|
+
const value = content.replace(/</g, "<").replace(/>/g, ">").replace(/&/g, "&").replace(/"/g, '"').replace(/'/g, "'").trim();
|
|
91
|
+
matches.push(value);
|
|
92
|
+
}
|
|
93
|
+
match = regex.exec(str);
|
|
94
|
+
}
|
|
95
|
+
return matches;
|
|
96
|
+
}
|
|
97
|
+
function parseCDATA(str) {
|
|
98
|
+
if (!str)
|
|
99
|
+
return "";
|
|
100
|
+
return str.replace(/<!\[CDATA\[(.*?)\]\]>/gs, "$1");
|
|
101
|
+
}
|
|
102
|
+
function parseImage(imageXml) {
|
|
103
|
+
const imageMatch = /<image>(.*?)<\/image>/s.exec(imageXml);
|
|
104
|
+
if (imageMatch?.[1]) {
|
|
105
|
+
const imgContent = imageMatch[1];
|
|
106
|
+
return {
|
|
107
|
+
url: parseTag("url", imgContent)[0] ?? "",
|
|
108
|
+
title: parseTag("title", imgContent)[0] ?? "",
|
|
109
|
+
link: parseTag("link", imgContent)[0] ?? "",
|
|
110
|
+
width: parseTag("width", imgContent)[0] ?? "",
|
|
111
|
+
height: parseTag("height", imgContent)[0] ?? ""
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
return null;
|
|
115
|
+
}
|
|
116
|
+
function parseEnclosure(itemXml) {
|
|
117
|
+
const enclosureTag = /<enclosure[^>]*\/?>/i.exec(itemXml);
|
|
118
|
+
if (enclosureTag?.[0]) {
|
|
119
|
+
const urlMatch = /url="([^"]*)"/.exec(enclosureTag[0]);
|
|
120
|
+
const typeMatch = /type="([^"]*)"/.exec(enclosureTag[0]);
|
|
121
|
+
const lengthMatch = /length="([^"]*)"/.exec(enclosureTag[0]);
|
|
122
|
+
return {
|
|
123
|
+
url: urlMatch?.[1] ?? "",
|
|
124
|
+
type: typeMatch?.[1] ?? "",
|
|
125
|
+
length: lengthMatch?.[1] ?? ""
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
return null;
|
|
129
|
+
}
|
|
130
|
+
function parseItem(itemXml) {
|
|
131
|
+
return {
|
|
132
|
+
title: parseTag("title", itemXml)[0] ?? "",
|
|
133
|
+
link: parseTag("link", itemXml)[0] ?? "",
|
|
134
|
+
pubDate: parseTag("pubDate", itemXml)[0] ?? "",
|
|
135
|
+
description: parseCDATA(parseTag("description", itemXml)[0]),
|
|
136
|
+
author: parseTag("author", itemXml)[0] ?? "",
|
|
137
|
+
category: parseTag("category", itemXml) ?? [],
|
|
138
|
+
comments: parseTag("comments", itemXml)[0] ?? "",
|
|
139
|
+
guid: parseTag("guid", itemXml)[0] ?? "",
|
|
140
|
+
enclosure: parseEnclosure(itemXml)
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
function parseRssToJson(xml) {
|
|
144
|
+
try {
|
|
145
|
+
const cleanXml = xml.replace(/<!--[\s\S]*?-->/g, "").replace(/\s+/g, " ").trim();
|
|
146
|
+
const channelRegex = /<channel>(.*?)<\/channel>/s;
|
|
147
|
+
const channelMatch = channelRegex.exec(cleanXml);
|
|
148
|
+
if (!channelMatch || !channelMatch[1]) {
|
|
149
|
+
throw new Error("No channel element found in RSS feed");
|
|
150
|
+
}
|
|
151
|
+
const channelXml = channelMatch[1];
|
|
152
|
+
const channel = {
|
|
153
|
+
title: parseTag("title", channelXml)[0] ?? "",
|
|
154
|
+
description: parseCDATA(parseTag("description", channelXml)[0]),
|
|
155
|
+
link: parseTag("link", channelXml)[0] ?? "",
|
|
156
|
+
language: parseTag("language", channelXml)[0] ?? "",
|
|
157
|
+
copyright: parseTag("copyright", channelXml)[0] ?? "",
|
|
158
|
+
lastBuildDate: parseTag("lastBuildDate", channelXml)[0] ?? "",
|
|
159
|
+
generator: parseTag("generator", channelXml)[0] ?? "",
|
|
160
|
+
docs: parseTag("docs", channelXml)[0] ?? "",
|
|
161
|
+
ttl: parseTag("ttl", channelXml)[0] ?? "",
|
|
162
|
+
image: parseImage(channelXml),
|
|
163
|
+
items: []
|
|
164
|
+
};
|
|
165
|
+
const itemRegex = /<item>(.*?)<\/item>/gs;
|
|
166
|
+
let itemMatch = itemRegex.exec(channelXml);
|
|
167
|
+
while (itemMatch !== null) {
|
|
168
|
+
if (itemMatch[1]) {
|
|
169
|
+
channel.items.push(parseItem(itemMatch[1]));
|
|
170
|
+
}
|
|
171
|
+
itemMatch = itemRegex.exec(channelXml);
|
|
172
|
+
}
|
|
173
|
+
return channel;
|
|
174
|
+
} catch {
|
|
175
|
+
return {
|
|
176
|
+
title: "",
|
|
177
|
+
description: "",
|
|
178
|
+
link: "",
|
|
179
|
+
language: "",
|
|
180
|
+
copyright: "",
|
|
181
|
+
lastBuildDate: "",
|
|
182
|
+
generator: "",
|
|
183
|
+
docs: "",
|
|
184
|
+
ttl: "",
|
|
185
|
+
image: null,
|
|
186
|
+
items: []
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
function createEmptyFeed() {
|
|
191
|
+
return {
|
|
192
|
+
title: "",
|
|
193
|
+
description: "",
|
|
194
|
+
link: "",
|
|
195
|
+
language: "",
|
|
196
|
+
copyright: "",
|
|
197
|
+
lastBuildDate: "",
|
|
198
|
+
generator: "",
|
|
199
|
+
docs: "",
|
|
200
|
+
ttl: "",
|
|
201
|
+
image: null,
|
|
202
|
+
items: []
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// index.ts
|
|
207
|
+
import { logger as logger3 } from "@elizaos/core";
|
|
208
|
+
|
|
209
|
+
// actions/getFeed.ts
|
|
210
|
+
import { createUniqueUuid as createUniqueUuid2, MemoryType } from "@elizaos/core";
|
|
211
|
+
|
|
212
|
+
// generated/specs/specs.ts
|
|
213
|
+
var coreActionsSpec = {
|
|
214
|
+
version: "1.0.0",
|
|
215
|
+
actions: [
|
|
216
|
+
{
|
|
217
|
+
name: "GET_NEWSFEED",
|
|
218
|
+
description: "Download and parse an RSS/Atom feed from a URL",
|
|
219
|
+
similes: ["FETCH_RSS", "READ_FEED", "DOWNLOAD_FEED"],
|
|
220
|
+
parameters: []
|
|
221
|
+
},
|
|
222
|
+
{
|
|
223
|
+
name: "LIST_RSS_FEEDS",
|
|
224
|
+
description: "List all subscribed RSS/Atom feeds",
|
|
225
|
+
similes: ["SHOW_RSS_FEEDS", "GET_RSS_FEEDS", "RSS_SUBSCRIPTIONS"],
|
|
226
|
+
parameters: []
|
|
227
|
+
},
|
|
228
|
+
{
|
|
229
|
+
name: "SUBSCRIBE_RSS_FEED",
|
|
230
|
+
description: "Subscribe to an RSS/Atom feed for automatic monitoring",
|
|
231
|
+
similes: ["ADD_RSS_FEED", "FOLLOW_RSS_FEED", "SUBSCRIBE_TO_RSS"],
|
|
232
|
+
parameters: []
|
|
233
|
+
},
|
|
234
|
+
{
|
|
235
|
+
name: "UNSUBSCRIBE_RSS_FEED",
|
|
236
|
+
description: "Unsubscribe from an RSS/Atom feed",
|
|
237
|
+
similes: ["REMOVE_RSS_FEED", "UNFOLLOW_RSS_FEED", "DELETE_RSS_FEED"],
|
|
238
|
+
parameters: []
|
|
239
|
+
}
|
|
240
|
+
]
|
|
241
|
+
};
|
|
242
|
+
var allActionsSpec = {
|
|
243
|
+
version: "1.0.0",
|
|
244
|
+
actions: [
|
|
245
|
+
{
|
|
246
|
+
name: "GET_NEWSFEED",
|
|
247
|
+
description: "Download and parse an RSS/Atom feed from a URL",
|
|
248
|
+
similes: ["FETCH_RSS", "READ_FEED", "DOWNLOAD_FEED"],
|
|
249
|
+
parameters: []
|
|
250
|
+
},
|
|
251
|
+
{
|
|
252
|
+
name: "LIST_RSS_FEEDS",
|
|
253
|
+
description: "List all subscribed RSS/Atom feeds",
|
|
254
|
+
similes: ["SHOW_RSS_FEEDS", "GET_RSS_FEEDS", "RSS_SUBSCRIPTIONS"],
|
|
255
|
+
parameters: []
|
|
256
|
+
},
|
|
257
|
+
{
|
|
258
|
+
name: "SUBSCRIBE_RSS_FEED",
|
|
259
|
+
description: "Subscribe to an RSS/Atom feed for automatic monitoring",
|
|
260
|
+
similes: ["ADD_RSS_FEED", "FOLLOW_RSS_FEED", "SUBSCRIBE_TO_RSS"],
|
|
261
|
+
parameters: []
|
|
262
|
+
},
|
|
263
|
+
{
|
|
264
|
+
name: "UNSUBSCRIBE_RSS_FEED",
|
|
265
|
+
description: "Unsubscribe from an RSS/Atom feed",
|
|
266
|
+
similes: ["REMOVE_RSS_FEED", "UNFOLLOW_RSS_FEED", "DELETE_RSS_FEED"],
|
|
267
|
+
parameters: []
|
|
268
|
+
}
|
|
269
|
+
]
|
|
270
|
+
};
|
|
271
|
+
var coreProvidersSpec = {
|
|
272
|
+
version: "1.0.0",
|
|
273
|
+
providers: [
|
|
274
|
+
{
|
|
275
|
+
name: "FEEDITEMS",
|
|
276
|
+
description: "Provides recent news and articles from subscribed RSS feeds",
|
|
277
|
+
dynamic: true
|
|
278
|
+
}
|
|
279
|
+
]
|
|
280
|
+
};
|
|
281
|
+
var allProvidersSpec = {
|
|
282
|
+
version: "1.0.0",
|
|
283
|
+
providers: [
|
|
284
|
+
{
|
|
285
|
+
name: "FEEDITEMS",
|
|
286
|
+
description: "Provides recent news and articles from subscribed RSS feeds",
|
|
287
|
+
dynamic: true
|
|
288
|
+
}
|
|
289
|
+
]
|
|
290
|
+
};
|
|
291
|
+
var coreEvaluatorsSpec = {
|
|
292
|
+
version: "1.0.0",
|
|
293
|
+
evaluators: []
|
|
294
|
+
};
|
|
295
|
+
var allEvaluatorsSpec = {
|
|
296
|
+
version: "1.0.0",
|
|
297
|
+
evaluators: []
|
|
298
|
+
};
|
|
299
|
+
var coreActionDocs = coreActionsSpec.actions;
|
|
300
|
+
var allActionDocs = allActionsSpec.actions;
|
|
301
|
+
var coreProviderDocs = coreProvidersSpec.providers;
|
|
302
|
+
var allProviderDocs = allProvidersSpec.providers;
|
|
303
|
+
var coreEvaluatorDocs = coreEvaluatorsSpec.evaluators;
|
|
304
|
+
var allEvaluatorDocs = allEvaluatorsSpec.evaluators;
|
|
305
|
+
|
|
306
|
+
// generated/specs/spec-helpers.ts
|
|
307
|
+
var coreActionMap = new Map(coreActionDocs.map((doc) => [doc.name, doc]));
|
|
308
|
+
var allActionMap = new Map(allActionDocs.map((doc) => [doc.name, doc]));
|
|
309
|
+
var coreProviderMap = new Map(coreProviderDocs.map((doc) => [doc.name, doc]));
|
|
310
|
+
var allProviderMap = new Map(allProviderDocs.map((doc) => [doc.name, doc]));
|
|
311
|
+
var coreEvaluatorMap = new Map(coreEvaluatorDocs.map((doc) => [doc.name, doc]));
|
|
312
|
+
var allEvaluatorMap = new Map(allEvaluatorDocs.map((doc) => [doc.name, doc]));
|
|
313
|
+
function getActionSpec(name) {
|
|
314
|
+
return coreActionMap.get(name) ?? allActionMap.get(name);
|
|
315
|
+
}
|
|
316
|
+
function requireActionSpec(name) {
|
|
317
|
+
const spec = getActionSpec(name);
|
|
318
|
+
if (!spec) {
|
|
319
|
+
throw new Error(`Action spec not found: ${name}`);
|
|
320
|
+
}
|
|
321
|
+
return spec;
|
|
322
|
+
}
|
|
323
|
+
function getProviderSpec(name) {
|
|
324
|
+
return coreProviderMap.get(name) ?? allProviderMap.get(name);
|
|
325
|
+
}
|
|
326
|
+
function requireProviderSpec(name) {
|
|
327
|
+
const spec = getProviderSpec(name);
|
|
328
|
+
if (!spec) {
|
|
329
|
+
throw new Error(`Provider spec not found: ${name}`);
|
|
330
|
+
}
|
|
331
|
+
return spec;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
// actions/getFeed.ts
|
|
335
|
+
init_utils();
|
|
336
|
+
var spec = requireActionSpec("GET_FEED");
|
|
337
|
+
var getFeedAction = {
|
|
338
|
+
name: spec.name,
|
|
339
|
+
similes: spec.similes ? [...spec.similes] : [],
|
|
340
|
+
validate: async (_runtime, _message, _state) => {
|
|
341
|
+
return true;
|
|
342
|
+
},
|
|
343
|
+
description: spec.description,
|
|
344
|
+
handler: async (runtime, message, _state, _options, callback, _responses) => {
|
|
345
|
+
const service = runtime.getService("RSS");
|
|
346
|
+
if (!service) {
|
|
347
|
+
runtime.logger.error("RSS service not found");
|
|
348
|
+
callback?.(createMessageReply(runtime, message, "RSS service is not available"));
|
|
349
|
+
return { success: false, error: "RSS service not found" };
|
|
350
|
+
}
|
|
351
|
+
const urls = extractUrls(message.content.text || "");
|
|
352
|
+
const validUrls = urls.filter((u) => u.match(/rss|feed|atom|xml/i) || u.endsWith(".rss") || u.endsWith(".xml"));
|
|
353
|
+
if (!validUrls.length) {
|
|
354
|
+
if (urls.length > 0) {
|
|
355
|
+
validUrls.push(urls[0]);
|
|
356
|
+
} else {
|
|
357
|
+
runtime.logger.warn("No valid URLs found in message");
|
|
358
|
+
callback?.(createMessageReply(runtime, message, "No valid RSS feed URL provided"));
|
|
359
|
+
return { success: false, error: "No valid RSS feed URL provided" };
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
const url = validUrls[0];
|
|
363
|
+
if (!url) {
|
|
364
|
+
callback?.(createMessageReply(runtime, message, "No valid RSS feed URL provided"));
|
|
365
|
+
return { success: false, error: "No valid RSS feed URL provided" };
|
|
366
|
+
}
|
|
367
|
+
const res = await service.fetchUrl(url);
|
|
368
|
+
if (!res) {
|
|
369
|
+
runtime.logger.error({ url }, "Failed to fetch RSS feed");
|
|
370
|
+
callback?.(createMessageReply(runtime, message, "Failed to fetch RSS feed"));
|
|
371
|
+
return { success: false, error: "Failed to fetch RSS feed" };
|
|
372
|
+
}
|
|
373
|
+
runtime.logger.info({ count: res.items.length, title: res.title || url }, "Fetched items from RSS feed");
|
|
374
|
+
let newItemCount = 0;
|
|
375
|
+
for (const item of res.items) {
|
|
376
|
+
const primaryId = createUniqueUuid2(runtime, `${url}_${item.guid}`);
|
|
377
|
+
const fallbackId = createUniqueUuid2(runtime, `${url}_${item.title}_${item.pubDate}`);
|
|
378
|
+
const existingByGuid = await runtime.getMemoriesByIds([primaryId], "feeditems");
|
|
379
|
+
const existingByTitleDate = await runtime.getMemoriesByIds([fallbackId], "feeditems");
|
|
380
|
+
if ((!existingByGuid || existingByGuid.length === 0) && (!existingByTitleDate || existingByTitleDate.length === 0)) {
|
|
381
|
+
const itemId = item.guid ? primaryId : fallbackId;
|
|
382
|
+
const itemMemory = {
|
|
383
|
+
id: itemId,
|
|
384
|
+
entityId: runtime.agentId,
|
|
385
|
+
agentId: runtime.agentId,
|
|
386
|
+
content: {
|
|
387
|
+
text: item.title,
|
|
388
|
+
url: item.link
|
|
389
|
+
},
|
|
390
|
+
roomId: message.roomId,
|
|
391
|
+
createdAt: Date.now(),
|
|
392
|
+
metadata: {
|
|
393
|
+
type: MemoryType.CUSTOM,
|
|
394
|
+
feedUrl: url,
|
|
395
|
+
feedTitle: res.title,
|
|
396
|
+
title: item.title,
|
|
397
|
+
link: item.link,
|
|
398
|
+
pubDate: item.pubDate,
|
|
399
|
+
description: item.description,
|
|
400
|
+
author: item.author,
|
|
401
|
+
category: item.category.join(","),
|
|
402
|
+
comments: item.comments,
|
|
403
|
+
guid: item.guid
|
|
404
|
+
}
|
|
405
|
+
};
|
|
406
|
+
await runtime.createMemory(itemMemory, "feeditems");
|
|
407
|
+
newItemCount++;
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
if (url) {
|
|
411
|
+
await service.subscribeFeed(url, res.title);
|
|
412
|
+
}
|
|
413
|
+
const responseText = newItemCount > 0 ? `Downloaded ${res.items.length} articles from "${res.title}", ${newItemCount} new items stored. Feed auto-subscribed for periodic updates.` : `Downloaded ${res.items.length} articles from "${res.title}", all items already stored. Feed auto-subscribed for periodic updates.`;
|
|
414
|
+
callback?.(createMessageReply(runtime, message, responseText));
|
|
415
|
+
return { success: true };
|
|
416
|
+
},
|
|
417
|
+
examples: spec.examples ?? []
|
|
418
|
+
};
|
|
419
|
+
// actions/listFeeds.ts
|
|
420
|
+
init_utils();
|
|
421
|
+
var spec2 = requireActionSpec("LIST_FEEDS");
|
|
422
|
+
var listFeedsAction = {
|
|
423
|
+
name: spec2.name,
|
|
424
|
+
similes: spec2.similes ? [...spec2.similes] : [],
|
|
425
|
+
validate: async (_runtime, _message, _state) => {
|
|
426
|
+
return true;
|
|
427
|
+
},
|
|
428
|
+
description: spec2.description,
|
|
429
|
+
handler: async (runtime, message, _state, _options, callback, _responses) => {
|
|
430
|
+
const service = runtime.getService("RSS");
|
|
431
|
+
if (!service) {
|
|
432
|
+
runtime.logger.error("RSS service not found");
|
|
433
|
+
callback?.(createMessageReply(runtime, message, "RSS service is not available"));
|
|
434
|
+
return { success: false, error: "RSS service not found" };
|
|
435
|
+
}
|
|
436
|
+
const feeds = await service.getSubscribedFeeds();
|
|
437
|
+
if (!feeds || feeds.length === 0) {
|
|
438
|
+
callback?.(createMessageReply(runtime, message, "No RSS feeds are currently subscribed. Use the subscribe action to add feeds."));
|
|
439
|
+
return { success: true };
|
|
440
|
+
}
|
|
441
|
+
let feedList = `You are subscribed to ${feeds.length} RSS feed${feeds.length > 1 ? "s" : ""}:
|
|
442
|
+
|
|
443
|
+
`;
|
|
444
|
+
feeds.forEach((feed, index) => {
|
|
445
|
+
const title = feed.content.text || "Untitled Feed";
|
|
446
|
+
const url = feed.content.url || "Unknown URL";
|
|
447
|
+
const metadata = feed.metadata;
|
|
448
|
+
const lastChecked = metadata?.lastChecked;
|
|
449
|
+
const itemCount = metadata?.lastItemCount || 0;
|
|
450
|
+
feedList += `${index + 1}. ${title}
|
|
451
|
+
`;
|
|
452
|
+
feedList += ` URL: ${url}
|
|
453
|
+
`;
|
|
454
|
+
if (lastChecked && lastChecked > 0) {
|
|
455
|
+
const timeStr = formatRelativeTime(lastChecked);
|
|
456
|
+
feedList += ` Last checked: ${timeStr} (${itemCount} items)
|
|
457
|
+
`;
|
|
458
|
+
} else {
|
|
459
|
+
feedList += ` Last checked: Never
|
|
460
|
+
`;
|
|
461
|
+
}
|
|
462
|
+
feedList += `
|
|
463
|
+
`;
|
|
464
|
+
});
|
|
465
|
+
callback?.(createMessageReply(runtime, message, feedList.trim()));
|
|
466
|
+
return { success: true };
|
|
467
|
+
},
|
|
468
|
+
examples: spec2.examples ?? []
|
|
469
|
+
};
|
|
470
|
+
// actions/subscribeFeed.ts
|
|
471
|
+
init_utils();
|
|
472
|
+
var spec3 = requireActionSpec("SUBSCRIBE_FEED");
|
|
473
|
+
var subscribeFeedAction = {
|
|
474
|
+
name: spec3.name,
|
|
475
|
+
similes: spec3.similes ? [...spec3.similes] : [],
|
|
476
|
+
validate: async (_runtime, _message, _state) => {
|
|
477
|
+
return true;
|
|
478
|
+
},
|
|
479
|
+
description: spec3.description,
|
|
480
|
+
handler: async (runtime, message, _state, _options, callback, _responses) => {
|
|
481
|
+
const service = runtime.getService("RSS");
|
|
482
|
+
if (!service) {
|
|
483
|
+
runtime.logger.error("RSS service not found");
|
|
484
|
+
callback?.(createMessageReply(runtime, message, "RSS service is not available"));
|
|
485
|
+
return { success: false, error: "RSS service not found" };
|
|
486
|
+
}
|
|
487
|
+
const urls = extractUrls(message.content.text || "");
|
|
488
|
+
if (!urls.length) {
|
|
489
|
+
runtime.logger.warn("No valid URLs found in message");
|
|
490
|
+
callback?.(createMessageReply(runtime, message, "Please provide a valid RSS feed URL"));
|
|
491
|
+
return { success: false, error: "No valid URLs found" };
|
|
492
|
+
}
|
|
493
|
+
const url = urls[0];
|
|
494
|
+
if (!url) {
|
|
495
|
+
callback?.(createMessageReply(runtime, message, "Please provide a valid RSS feed URL"));
|
|
496
|
+
return { success: false, error: "No valid URLs found" };
|
|
497
|
+
}
|
|
498
|
+
runtime.logger.debug({ url }, "Attempting to subscribe to feed");
|
|
499
|
+
const feedData = await service.fetchUrl(url);
|
|
500
|
+
if (!feedData || !feedData.items) {
|
|
501
|
+
runtime.logger.error({ url }, "Invalid or empty RSS feed");
|
|
502
|
+
callback?.(createMessageReply(runtime, message, `Unable to fetch RSS feed from ${url}. Please check the URL and try again.`));
|
|
503
|
+
return { success: false, error: "Invalid or empty RSS feed" };
|
|
504
|
+
}
|
|
505
|
+
const success = await service.subscribeFeed(url, feedData.title);
|
|
506
|
+
if (success) {
|
|
507
|
+
const itemCount = feedData.items?.length || 0;
|
|
508
|
+
callback?.(createMessageReply(runtime, message, `Successfully subscribed to "${feedData.title}" (${url}). Found ${itemCount} items in the feed.`));
|
|
509
|
+
} else {
|
|
510
|
+
callback?.(createMessageReply(runtime, message, `Failed to subscribe to ${url}. You may already be subscribed to this feed.`));
|
|
511
|
+
}
|
|
512
|
+
return { success };
|
|
513
|
+
},
|
|
514
|
+
examples: spec3.examples ?? []
|
|
515
|
+
};
|
|
516
|
+
// actions/unsubscribeFeed.ts
|
|
517
|
+
init_utils();
|
|
518
|
+
var spec4 = requireActionSpec("UNSUBSCRIBE_FEED");
|
|
519
|
+
var unsubscribeFeedAction = {
|
|
520
|
+
name: spec4.name,
|
|
521
|
+
similes: spec4.similes ? [...spec4.similes] : [],
|
|
522
|
+
validate: async (_runtime, _message, _state) => {
|
|
523
|
+
return true;
|
|
524
|
+
},
|
|
525
|
+
description: spec4.description,
|
|
526
|
+
handler: async (runtime, message, _state, _options, callback, _responses) => {
|
|
527
|
+
const service = runtime.getService("RSS");
|
|
528
|
+
if (!service) {
|
|
529
|
+
runtime.logger.error("RSS service not found");
|
|
530
|
+
callback?.(createMessageReply(runtime, message, "RSS service is not available"));
|
|
531
|
+
return { success: false, error: "RSS service not found" };
|
|
532
|
+
}
|
|
533
|
+
const urls = extractUrls(message.content.text || "");
|
|
534
|
+
if (!urls.length) {
|
|
535
|
+
runtime.logger.warn("No valid URLs found in message");
|
|
536
|
+
callback?.(createMessageReply(runtime, message, "Please provide a valid RSS feed URL to unsubscribe from"));
|
|
537
|
+
return { success: false, error: "No valid URLs found" };
|
|
538
|
+
}
|
|
539
|
+
const url = urls[0];
|
|
540
|
+
if (!url) {
|
|
541
|
+
callback?.(createMessageReply(runtime, message, "Please provide a valid RSS feed URL to unsubscribe from"));
|
|
542
|
+
return { success: false, error: "No valid URLs found" };
|
|
543
|
+
}
|
|
544
|
+
runtime.logger.debug({ url }, "Attempting to unsubscribe from feed");
|
|
545
|
+
const success = await service.unsubscribeFeed(url);
|
|
546
|
+
if (success) {
|
|
547
|
+
callback?.(createMessageReply(runtime, message, `Successfully unsubscribed from ${url}`));
|
|
548
|
+
} else {
|
|
549
|
+
callback?.(createMessageReply(runtime, message, `Unable to unsubscribe from ${url}. You may not be subscribed to this feed.`));
|
|
550
|
+
}
|
|
551
|
+
return { success };
|
|
552
|
+
},
|
|
553
|
+
examples: spec4.examples ?? []
|
|
554
|
+
};
|
|
555
|
+
// providers/feedItems.ts
|
|
556
|
+
import { logger } from "@elizaos/core";
|
|
557
|
+
var spec5 = requireProviderSpec("feedItems");
|
|
558
|
+
var feedItemsProvider = {
|
|
559
|
+
name: spec5.name,
|
|
560
|
+
description: "Provides recent news and articles from subscribed RSS feeds",
|
|
561
|
+
get: async (runtime, _message, _state) => {
|
|
562
|
+
try {
|
|
563
|
+
const items = await runtime.getMemories({
|
|
564
|
+
tableName: "feeditems",
|
|
565
|
+
unique: false
|
|
566
|
+
});
|
|
567
|
+
if (!items || items.length === 0) {
|
|
568
|
+
return {
|
|
569
|
+
data: { count: 0 },
|
|
570
|
+
values: {},
|
|
571
|
+
text: "No RSS feed items available. Subscribe to feeds to see news articles here."
|
|
572
|
+
};
|
|
573
|
+
}
|
|
574
|
+
const sortedItems = [...items].sort((a, b) => {
|
|
575
|
+
const timeA = a.createdAt || 0;
|
|
576
|
+
const timeB = b.createdAt || 0;
|
|
577
|
+
return timeB - timeA;
|
|
578
|
+
});
|
|
579
|
+
const recentItems = sortedItems.slice(0, 50);
|
|
580
|
+
const itemsByFeed = new Map;
|
|
581
|
+
for (const item of recentItems) {
|
|
582
|
+
const metadata = item.metadata;
|
|
583
|
+
const feedTitle = metadata?.feedTitle || "Unknown Feed";
|
|
584
|
+
if (!itemsByFeed.has(feedTitle)) {
|
|
585
|
+
itemsByFeed.set(feedTitle, []);
|
|
586
|
+
}
|
|
587
|
+
const feedItems = itemsByFeed.get(feedTitle);
|
|
588
|
+
if (feedItems) {
|
|
589
|
+
feedItems.push(item);
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
const format = runtime.getSetting("RSS_FEED_FORMAT") || "csv";
|
|
593
|
+
let outputText;
|
|
594
|
+
if (format === "markdown") {
|
|
595
|
+
outputText = `# Recent RSS Feed Items (${recentItems.length} items from ${itemsByFeed.size} feeds)
|
|
596
|
+
|
|
597
|
+
`;
|
|
598
|
+
for (const [feedTitle, feedItems] of itemsByFeed) {
|
|
599
|
+
outputText += `## ${feedTitle} (${feedItems.length} items)
|
|
600
|
+
|
|
601
|
+
`;
|
|
602
|
+
for (const item of feedItems) {
|
|
603
|
+
const metadata = item.metadata;
|
|
604
|
+
const title = item.content.text || metadata?.title || "Untitled";
|
|
605
|
+
const url = item.content.url || metadata?.link || "";
|
|
606
|
+
const description = metadata?.description || "";
|
|
607
|
+
const pubDate = metadata?.pubDate || "";
|
|
608
|
+
const author = metadata?.author || "";
|
|
609
|
+
outputText += `### ${title}
|
|
610
|
+
`;
|
|
611
|
+
if (url) {
|
|
612
|
+
outputText += `- URL: ${url}
|
|
613
|
+
`;
|
|
614
|
+
}
|
|
615
|
+
if (pubDate) {
|
|
616
|
+
outputText += `- Published: ${pubDate}
|
|
617
|
+
`;
|
|
618
|
+
}
|
|
619
|
+
if (author) {
|
|
620
|
+
outputText += `- Author: ${author}
|
|
621
|
+
`;
|
|
622
|
+
}
|
|
623
|
+
if (description) {
|
|
624
|
+
const shortDesc = description.length > 200 ? `${description.substring(0, 200)}...` : description;
|
|
625
|
+
outputText += `- Description: ${shortDesc}
|
|
626
|
+
`;
|
|
627
|
+
}
|
|
628
|
+
outputText += `
|
|
629
|
+
`;
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
} else {
|
|
633
|
+
outputText = `# RSS Feed Items (${recentItems.length} from ${itemsByFeed.size} feeds)
|
|
634
|
+
`;
|
|
635
|
+
outputText += `Feed,Title,URL,Published,Description
|
|
636
|
+
`;
|
|
637
|
+
for (const item of recentItems) {
|
|
638
|
+
const metadata = item.metadata;
|
|
639
|
+
const feedTitle = (metadata?.feedTitle || "Unknown").replace(/"/g, '""');
|
|
640
|
+
const title = (item.content.text || "").replace(/"/g, '""');
|
|
641
|
+
const url = item.content.url || "";
|
|
642
|
+
const pubDate = metadata?.pubDate || "";
|
|
643
|
+
const description = (metadata?.description || "").replace(/"/g, '""').substring(0, 200);
|
|
644
|
+
outputText += `"${feedTitle}","${title}","${url}","${pubDate}","${description}"
|
|
645
|
+
`;
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
const data = {
|
|
649
|
+
count: recentItems.length,
|
|
650
|
+
totalCount: items.length,
|
|
651
|
+
feedCount: itemsByFeed.size
|
|
652
|
+
};
|
|
653
|
+
const values = {
|
|
654
|
+
itemCount: recentItems.length,
|
|
655
|
+
feedCount: itemsByFeed.size
|
|
656
|
+
};
|
|
657
|
+
return {
|
|
658
|
+
data,
|
|
659
|
+
values,
|
|
660
|
+
text: outputText
|
|
661
|
+
};
|
|
662
|
+
} catch (error) {
|
|
663
|
+
logger.error({ error }, "Error in FEEDITEMS provider");
|
|
664
|
+
return {
|
|
665
|
+
data: { count: 0, error: String(error) },
|
|
666
|
+
values: {},
|
|
667
|
+
text: "Error loading RSS feed items."
|
|
668
|
+
};
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
};
|
|
672
|
+
// service.ts
|
|
673
|
+
import {
|
|
674
|
+
createUniqueUuid as createUniqueUuid3,
|
|
675
|
+
logger as logger2,
|
|
676
|
+
MemoryType as MemoryType2,
|
|
677
|
+
Service
|
|
678
|
+
} from "@elizaos/core";
|
|
679
|
+
|
|
680
|
+
class RssService extends Service {
|
|
681
|
+
isRunning = false;
|
|
682
|
+
static serviceType = "RSS";
|
|
683
|
+
capabilityDescription = "The agent is able to deal with RSS/atom feeds";
|
|
684
|
+
async fetchUrl(urlToFetch) {
|
|
685
|
+
let response;
|
|
686
|
+
try {
|
|
687
|
+
const resp = await fetch(urlToFetch);
|
|
688
|
+
response = await resp.text();
|
|
689
|
+
if (!response) {
|
|
690
|
+
logger2.warn({ url: urlToFetch }, "No response for feed URL");
|
|
691
|
+
return null;
|
|
692
|
+
}
|
|
693
|
+
const data = parseRssToJson(response);
|
|
694
|
+
if (!data || !data.title) {
|
|
695
|
+
logger2.warn({ url: urlToFetch }, "No RSS data found in response");
|
|
696
|
+
return createEmptyFeed();
|
|
697
|
+
}
|
|
698
|
+
return data;
|
|
699
|
+
} catch (error) {
|
|
700
|
+
logger2.error({ error, url: urlToFetch }, "Error fetching RSS feed");
|
|
701
|
+
return null;
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
async subscribeFeed(url, title) {
|
|
705
|
+
try {
|
|
706
|
+
const feedId = createUniqueUuid3(this.runtime, `feed_sub_${url}`);
|
|
707
|
+
const existing = await this.runtime.getMemoriesByIds([feedId], "feedsubscriptions");
|
|
708
|
+
if (existing && existing.length > 0) {
|
|
709
|
+
logger2.info({ url }, "Already subscribed to feed");
|
|
710
|
+
return true;
|
|
711
|
+
}
|
|
712
|
+
let feedTitle = title;
|
|
713
|
+
if (!feedTitle) {
|
|
714
|
+
const feedData = await this.fetchUrl(url);
|
|
715
|
+
if (feedData) {
|
|
716
|
+
feedTitle = feedData.title;
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
const subscriptionMemory = {
|
|
720
|
+
id: feedId,
|
|
721
|
+
entityId: this.runtime.agentId,
|
|
722
|
+
agentId: this.runtime.agentId,
|
|
723
|
+
content: {
|
|
724
|
+
text: feedTitle || url,
|
|
725
|
+
url
|
|
726
|
+
},
|
|
727
|
+
roomId: this.runtime.agentId,
|
|
728
|
+
createdAt: Date.now(),
|
|
729
|
+
metadata: {
|
|
730
|
+
type: MemoryType2.CUSTOM,
|
|
731
|
+
subscribedAt: Date.now(),
|
|
732
|
+
lastChecked: 0,
|
|
733
|
+
lastItemCount: 0
|
|
734
|
+
}
|
|
735
|
+
};
|
|
736
|
+
await this.runtime.createMemory(subscriptionMemory, "feedsubscriptions");
|
|
737
|
+
logger2.info({ url, title: feedTitle }, "Subscribed to RSS feed");
|
|
738
|
+
return true;
|
|
739
|
+
} catch (error) {
|
|
740
|
+
logger2.error({ error, url }, "Error subscribing to feed");
|
|
741
|
+
return false;
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
async unsubscribeFeed(url) {
|
|
745
|
+
try {
|
|
746
|
+
const feedId = createUniqueUuid3(this.runtime, `feed_sub_${url}`);
|
|
747
|
+
const existing = await this.runtime.getMemoriesByIds([feedId], "feedsubscriptions");
|
|
748
|
+
if (!existing || existing.length === 0) {
|
|
749
|
+
logger2.warn({ url }, "Not subscribed to feed");
|
|
750
|
+
return false;
|
|
751
|
+
}
|
|
752
|
+
await this.runtime.deleteMemory(feedId);
|
|
753
|
+
logger2.info({ url }, "Unsubscribed from RSS feed");
|
|
754
|
+
return true;
|
|
755
|
+
} catch (error) {
|
|
756
|
+
logger2.error({ error, url }, "Error unsubscribing from feed");
|
|
757
|
+
return false;
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
async getSubscribedFeeds() {
|
|
761
|
+
try {
|
|
762
|
+
const feeds = await this.runtime.getMemories({
|
|
763
|
+
tableName: "feedsubscriptions",
|
|
764
|
+
unique: false
|
|
765
|
+
});
|
|
766
|
+
return feeds || [];
|
|
767
|
+
} catch (error) {
|
|
768
|
+
logger2.error({ error }, "Error getting subscribed feeds");
|
|
769
|
+
return [];
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
async checkAllFeeds() {
|
|
773
|
+
try {
|
|
774
|
+
const feeds = await this.getSubscribedFeeds();
|
|
775
|
+
logger2.info({ count: feeds.length }, "Checking subscribed RSS feeds");
|
|
776
|
+
for (const feed of feeds) {
|
|
777
|
+
try {
|
|
778
|
+
const url = feed.content.url;
|
|
779
|
+
if (!url) {
|
|
780
|
+
logger2.warn({ feedId: feed.id }, "Feed subscription missing URL");
|
|
781
|
+
continue;
|
|
782
|
+
}
|
|
783
|
+
const feedData = await this.fetchUrl(url);
|
|
784
|
+
if (!feedData || !feedData.items) {
|
|
785
|
+
logger2.warn({ url }, "No data returned for feed");
|
|
786
|
+
continue;
|
|
787
|
+
}
|
|
788
|
+
let newItemCount = 0;
|
|
789
|
+
for (const item of feedData.items) {
|
|
790
|
+
const primaryId = createUniqueUuid3(this.runtime, `${url}_${item.guid}`);
|
|
791
|
+
const fallbackId = createUniqueUuid3(this.runtime, `${url}_${item.title}_${item.pubDate}`);
|
|
792
|
+
const existingByGuid = await this.runtime.getMemoriesByIds([primaryId], "feeditems");
|
|
793
|
+
const existingByTitleDate = await this.runtime.getMemoriesByIds([fallbackId], "feeditems");
|
|
794
|
+
if ((!existingByGuid || existingByGuid.length === 0) && (!existingByTitleDate || existingByTitleDate.length === 0)) {
|
|
795
|
+
const itemId = item.guid ? primaryId : fallbackId;
|
|
796
|
+
const itemMemory = {
|
|
797
|
+
id: itemId,
|
|
798
|
+
entityId: this.runtime.agentId,
|
|
799
|
+
agentId: this.runtime.agentId,
|
|
800
|
+
content: {
|
|
801
|
+
text: item.title,
|
|
802
|
+
url: item.link
|
|
803
|
+
},
|
|
804
|
+
roomId: this.runtime.agentId,
|
|
805
|
+
createdAt: Date.now(),
|
|
806
|
+
metadata: {
|
|
807
|
+
type: MemoryType2.CUSTOM,
|
|
808
|
+
feedUrl: url,
|
|
809
|
+
feedTitle: feedData.title,
|
|
810
|
+
title: item.title,
|
|
811
|
+
link: item.link,
|
|
812
|
+
pubDate: item.pubDate,
|
|
813
|
+
description: item.description,
|
|
814
|
+
author: item.author,
|
|
815
|
+
category: item.category.join(","),
|
|
816
|
+
comments: item.comments,
|
|
817
|
+
guid: item.guid
|
|
818
|
+
}
|
|
819
|
+
};
|
|
820
|
+
await this.runtime.createMemory(itemMemory, "feeditems");
|
|
821
|
+
newItemCount++;
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
if (!feed.id) {
|
|
825
|
+
continue;
|
|
826
|
+
}
|
|
827
|
+
const currentMetadata = feed.metadata;
|
|
828
|
+
await this.runtime.updateMemory({
|
|
829
|
+
id: feed.id,
|
|
830
|
+
metadata: {
|
|
831
|
+
type: MemoryType2.CUSTOM,
|
|
832
|
+
subscribedAt: currentMetadata.subscribedAt ?? Date.now(),
|
|
833
|
+
lastChecked: Date.now(),
|
|
834
|
+
lastItemCount: feedData.items.length
|
|
835
|
+
}
|
|
836
|
+
});
|
|
837
|
+
if (newItemCount > 0) {
|
|
838
|
+
logger2.info({ count: newItemCount, feed: feedData.title || url }, "Found new items from feed");
|
|
839
|
+
} else {}
|
|
840
|
+
} catch (error) {
|
|
841
|
+
logger2.error({ error, url: feed.content.url }, "Error checking feed");
|
|
842
|
+
}
|
|
843
|
+
}
|
|
844
|
+
logger2.info("Completed checking all RSS feeds");
|
|
845
|
+
} catch (error) {
|
|
846
|
+
logger2.error({ error }, "Error in checkAllFeeds");
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
async loadInitialFeeds() {
|
|
850
|
+
const rssFeeds = this.runtime.getSetting("RSS_FEEDS");
|
|
851
|
+
if (!rssFeeds) {
|
|
852
|
+
return;
|
|
853
|
+
}
|
|
854
|
+
try {
|
|
855
|
+
let feedUrls = [];
|
|
856
|
+
if (typeof rssFeeds === "string") {
|
|
857
|
+
try {
|
|
858
|
+
const parsed = JSON.parse(rssFeeds);
|
|
859
|
+
if (Array.isArray(parsed)) {
|
|
860
|
+
feedUrls = parsed;
|
|
861
|
+
} else {
|
|
862
|
+
feedUrls = rssFeeds.split(",").map((url) => url.trim()).filter((url) => url.length > 0);
|
|
863
|
+
}
|
|
864
|
+
} catch {
|
|
865
|
+
feedUrls = rssFeeds.split(",").map((url) => url.trim()).filter((url) => url.length > 0);
|
|
866
|
+
}
|
|
867
|
+
} else if (Array.isArray(rssFeeds)) {
|
|
868
|
+
feedUrls = rssFeeds;
|
|
869
|
+
}
|
|
870
|
+
logger2.info({ count: feedUrls.length }, "Loading RSS feeds from configuration");
|
|
871
|
+
for (const url of feedUrls) {
|
|
872
|
+
await this.subscribeFeed(url);
|
|
873
|
+
}
|
|
874
|
+
logger2.info("Completed loading initial RSS feeds");
|
|
875
|
+
} catch (error) {
|
|
876
|
+
logger2.error({ error }, "Error loading initial RSS feeds from configuration");
|
|
877
|
+
}
|
|
878
|
+
}
|
|
879
|
+
registerFeedCheckWorker() {
|
|
880
|
+
const worker = {
|
|
881
|
+
name: "RSS_FEED_CHECK",
|
|
882
|
+
validate: async (_runtime, _message) => {
|
|
883
|
+
return true;
|
|
884
|
+
},
|
|
885
|
+
execute: async (runtime) => {
|
|
886
|
+
try {
|
|
887
|
+
logger2.debug("Executing RSS feed check task");
|
|
888
|
+
const rssService = runtime.getService("RSS");
|
|
889
|
+
if (rssService) {
|
|
890
|
+
await rssService.checkAllFeeds();
|
|
891
|
+
}
|
|
892
|
+
} catch (error) {
|
|
893
|
+
logger2.error({ error }, "Error executing RSS feed check task");
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
};
|
|
897
|
+
this.runtime.registerTaskWorker(worker);
|
|
898
|
+
}
|
|
899
|
+
async start() {
|
|
900
|
+
if (this.isRunning) {
|
|
901
|
+
logger2.warn("RSS service is already running");
|
|
902
|
+
return;
|
|
903
|
+
}
|
|
904
|
+
try {
|
|
905
|
+
logger2.info("Starting RSS service...");
|
|
906
|
+
this.registerFeedCheckWorker();
|
|
907
|
+
await this.loadInitialFeeds();
|
|
908
|
+
const checkIntervalSetting = this.runtime.getSetting("RSS_CHECK_INTERVAL_MINUTES");
|
|
909
|
+
const checkIntervalMinutes = typeof checkIntervalSetting === "number" ? checkIntervalSetting : 15;
|
|
910
|
+
const intervalInMs = checkIntervalMinutes * 60 * 1000;
|
|
911
|
+
await this.runtime.createTask({
|
|
912
|
+
name: "RSS_FEED_CHECK",
|
|
913
|
+
description: "Periodically check RSS feeds for new items",
|
|
914
|
+
worldId: this.runtime.agentId,
|
|
915
|
+
metadata: {
|
|
916
|
+
createdAt: String(Date.now()),
|
|
917
|
+
updatedAt: Date.now(),
|
|
918
|
+
updateInterval: intervalInMs
|
|
919
|
+
},
|
|
920
|
+
tags: ["queue", "repeat", "rss"]
|
|
921
|
+
});
|
|
922
|
+
logger2.info({ interval: checkIntervalMinutes }, "RSS periodic feed check task created");
|
|
923
|
+
this.isRunning = true;
|
|
924
|
+
logger2.info("RSS service started successfully");
|
|
925
|
+
} catch (error) {
|
|
926
|
+
logger2.error({ error }, "Error starting RSS service");
|
|
927
|
+
throw error;
|
|
928
|
+
}
|
|
929
|
+
}
|
|
930
|
+
static async start(runtime) {
|
|
931
|
+
const service = new RssService(runtime);
|
|
932
|
+
await service.start();
|
|
933
|
+
return service;
|
|
934
|
+
}
|
|
935
|
+
async stop() {
|
|
936
|
+
if (!this.isRunning) {
|
|
937
|
+
logger2.warn("RSS service is not running");
|
|
938
|
+
return;
|
|
939
|
+
}
|
|
940
|
+
try {
|
|
941
|
+
logger2.info("Stopping RSS service...");
|
|
942
|
+
this.isRunning = false;
|
|
943
|
+
logger2.info("RSS service stopped successfully");
|
|
944
|
+
} catch (error) {
|
|
945
|
+
logger2.error({ error }, "Error stopping RSS service");
|
|
946
|
+
throw error;
|
|
947
|
+
}
|
|
948
|
+
}
|
|
949
|
+
static async stop(runtime) {
|
|
950
|
+
const service = runtime.getService(RssService.serviceType);
|
|
951
|
+
if (!service) {
|
|
952
|
+
throw new Error(`${RssService.serviceType} service not found`);
|
|
953
|
+
}
|
|
954
|
+
await service.stop();
|
|
955
|
+
}
|
|
956
|
+
isServiceRunning() {
|
|
957
|
+
return this.isRunning;
|
|
958
|
+
}
|
|
959
|
+
}
|
|
960
|
+
|
|
961
|
+
// index.ts
|
|
962
|
+
var actionsDisabled = process.env.RSS_DISABLE_ACTIONS === "true";
|
|
963
|
+
var actions = [getFeedAction];
|
|
964
|
+
if (!actionsDisabled) {
|
|
965
|
+
actions.push(subscribeFeedAction, unsubscribeFeedAction, listFeedsAction);
|
|
966
|
+
}
|
|
967
|
+
var rssPlugin = {
|
|
968
|
+
name: "rss",
|
|
969
|
+
description: "RSS/Atom feed monitoring and subscription management",
|
|
970
|
+
config: {
|
|
971
|
+
RSS_FEEDS: process.env.RSS_FEEDS ?? null,
|
|
972
|
+
RSS_DISABLE_ACTIONS: process.env.RSS_DISABLE_ACTIONS ?? null,
|
|
973
|
+
RSS_FEED_FORMAT: process.env.RSS_FEED_FORMAT ?? null,
|
|
974
|
+
RSS_CHECK_INTERVAL_MINUTES: process.env.RSS_CHECK_INTERVAL_MINUTES ?? null
|
|
975
|
+
},
|
|
976
|
+
async init(_config, _runtime) {
|
|
977
|
+
logger3.info("Initializing RSS plugin");
|
|
978
|
+
},
|
|
979
|
+
evaluators: [],
|
|
980
|
+
providers: [feedItemsProvider],
|
|
981
|
+
actions,
|
|
982
|
+
services: [RssService],
|
|
983
|
+
tests: [
|
|
984
|
+
{
|
|
985
|
+
name: "rss_plugin_tests",
|
|
986
|
+
tests: [
|
|
987
|
+
{
|
|
988
|
+
name: "rss_test_parser",
|
|
989
|
+
fn: async (_runtime) => {
|
|
990
|
+
const { parseRssToJson: parseRssToJson2 } = await Promise.resolve().then(() => exports_parser);
|
|
991
|
+
const sampleRss = `<?xml version="1.0"?>
|
|
992
|
+
<rss version="2.0">
|
|
993
|
+
<channel>
|
|
994
|
+
<title>Test Feed</title>
|
|
995
|
+
<link>https://example.com</link>
|
|
996
|
+
<description>A test RSS feed</description>
|
|
997
|
+
<item>
|
|
998
|
+
<title>Test Article</title>
|
|
999
|
+
<link>https://example.com/article1</link>
|
|
1000
|
+
<description>This is a test article</description>
|
|
1001
|
+
<pubDate>Mon, 01 Jan 2024 00:00:00 GMT</pubDate>
|
|
1002
|
+
<guid>article-1</guid>
|
|
1003
|
+
</item>
|
|
1004
|
+
</channel>
|
|
1005
|
+
</rss>`;
|
|
1006
|
+
const feed = parseRssToJson2(sampleRss);
|
|
1007
|
+
if (feed.title !== "Test Feed") {
|
|
1008
|
+
throw new Error(`Expected title "Test Feed", got "${feed.title}"`);
|
|
1009
|
+
}
|
|
1010
|
+
if (feed.items.length !== 1) {
|
|
1011
|
+
throw new Error(`Expected 1 item, got ${feed.items.length}`);
|
|
1012
|
+
}
|
|
1013
|
+
const firstItem = feed.items[0];
|
|
1014
|
+
if (firstItem && firstItem.title !== "Test Article") {
|
|
1015
|
+
throw new Error(`Expected item title "Test Article", got "${firstItem.title}"`);
|
|
1016
|
+
}
|
|
1017
|
+
logger3.info("[RSS Test] Parser test passed");
|
|
1018
|
+
}
|
|
1019
|
+
},
|
|
1020
|
+
{
|
|
1021
|
+
name: "rss_test_url_extraction",
|
|
1022
|
+
fn: async (_runtime) => {
|
|
1023
|
+
const { extractUrls: extractUrls2 } = await Promise.resolve().then(() => (init_utils(), exports_utils));
|
|
1024
|
+
const text = "Check out https://example.com/feed.rss and http://test.com for more.";
|
|
1025
|
+
const urls = extractUrls2(text);
|
|
1026
|
+
if (urls.length !== 2) {
|
|
1027
|
+
throw new Error(`Expected 2 URLs, got ${urls.length}`);
|
|
1028
|
+
}
|
|
1029
|
+
if (!urls.includes("https://example.com/feed.rss")) {
|
|
1030
|
+
throw new Error("Missing expected URL: https://example.com/feed.rss");
|
|
1031
|
+
}
|
|
1032
|
+
logger3.info("[RSS Test] URL extraction test passed");
|
|
1033
|
+
}
|
|
1034
|
+
},
|
|
1035
|
+
{
|
|
1036
|
+
name: "rss_test_service_exists",
|
|
1037
|
+
fn: async (runtime) => {
|
|
1038
|
+
const service = runtime.getService("RSS");
|
|
1039
|
+
if (!service) {
|
|
1040
|
+
throw new Error("RSS service not found");
|
|
1041
|
+
}
|
|
1042
|
+
logger3.info("[RSS Test] Service existence test passed");
|
|
1043
|
+
}
|
|
1044
|
+
}
|
|
1045
|
+
]
|
|
1046
|
+
}
|
|
1047
|
+
]
|
|
1048
|
+
};
|
|
1049
|
+
var typescript_default = rssPlugin;
|
|
1050
|
+
export {
|
|
1051
|
+
rssPlugin,
|
|
1052
|
+
typescript_default as default
|
|
1053
|
+
};
|
|
1054
|
+
|
|
1055
|
+
//# debugId=989BA756F7E002CD64756E2164756E21
|
|
1056
|
+
//# sourceMappingURL=index.node.js.map
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../utils.ts", "../../parser.ts", "../../index.ts", "../../actions/getFeed.ts", "../../generated/specs/specs.ts", "../../generated/specs/spec-helpers.ts", "../../actions/listFeeds.ts", "../../actions/subscribeFeed.ts", "../../actions/unsubscribeFeed.ts", "../../providers/feedItems.ts", "../../service.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"import type { Content, IAgentRuntime, Memory } from \"@elizaos/core\";\nimport { createUniqueUuid } from \"@elizaos/core\";\n\nexport function createMessageReply(\n runtime: IAgentRuntime,\n message: Memory,\n reply: string\n): Content {\n return {\n text: reply,\n attachments: [],\n source: message.content.source ?? \"\",\n channelType: message.content.channelType,\n inReplyTo: createUniqueUuid(runtime, message.id ?? \"\"),\n };\n}\n\nexport function extractUrls(text: string): string[] {\n const URL_MATCH = /(?:(?:https?|ftp):\\/\\/|www\\.)[^\\s<>\"'`]+/gi;\n const candidates = text.match(URL_MATCH) || [];\n\n const results: string[] = [];\n const seen = new Set<string>();\n\n for (const raw of candidates) {\n const candidate = raw.replace(/^[([{<'\"]+/, \"\");\n let withScheme = candidate.startsWith(\"www.\") ? `http://${candidate}` : candidate;\n const TRAIL = /[)\\]}>,.;!?:'\"\\u2026]$/;\n while (withScheme && TRAIL.test(withScheme.slice(-1)) && !isValidUrl(withScheme)) {\n withScheme = withScheme.slice(0, -1);\n }\n\n if (!isValidUrl(withScheme)) continue;\n\n const normalized = new URL(withScheme).toString();\n if (!seen.has(normalized)) {\n seen.add(normalized);\n results.push(normalized);\n }\n }\n\n return results;\n}\n\nfunction isValidUrl(u: string): boolean {\n try {\n new URL(u);\n return true;\n } catch {\n return false;\n }\n}\n\nexport function formatRelativeTime(timestamp: number): string {\n const timeSince = Date.now() - timestamp;\n const minutesSince = Math.floor(timeSince / 60000);\n const hoursSince = Math.floor(minutesSince / 60);\n const daysSince = Math.floor(hoursSince / 24);\n\n if (daysSince > 0) {\n return `${daysSince} day${daysSince > 1 ? \"s\" : \"\"} ago`;\n } else if (hoursSince > 0) {\n return `${hoursSince} hour${hoursSince > 1 ? \"s\" : \"\"} ago`;\n } else if (minutesSince > 0) {\n return `${minutesSince} minute${minutesSince > 1 ? \"s\" : \"\"} ago`;\n } else {\n return \"just now\";\n }\n}\n",
|
|
6
|
+
"import type { RssEnclosure, RssFeed, RssImage, RssItem } from \"./types\";\n\nfunction parseTag(tag: string, str: string): string[] {\n const regex = new RegExp(`<${tag}(?:\\\\s+[^>]*)?>(.*?)</${tag}>`, \"gs\");\n const matches: string[] = [];\n let match: RegExpExecArray | null = regex.exec(str);\n while (match !== null) {\n const content = match[1];\n if (content !== undefined) {\n const value = content\n .replace(/</g, \"<\")\n .replace(/>/g, \">\")\n .replace(/&/g, \"&\")\n .replace(/"/g, '\"')\n .replace(/'/g, \"'\")\n .trim();\n matches.push(value);\n }\n match = regex.exec(str);\n }\n return matches;\n}\n\nfunction parseCDATA(str: string | undefined): string {\n if (!str) return \"\";\n return str.replace(/<!\\[CDATA\\[(.*?)\\]\\]>/gs, \"$1\");\n}\n\nfunction parseImage(imageXml: string): RssImage | null {\n const imageMatch = /<image>(.*?)<\\/image>/s.exec(imageXml);\n if (imageMatch?.[1]) {\n const imgContent = imageMatch[1];\n return {\n url: parseTag(\"url\", imgContent)[0] ?? \"\",\n title: parseTag(\"title\", imgContent)[0] ?? \"\",\n link: parseTag(\"link\", imgContent)[0] ?? \"\",\n width: parseTag(\"width\", imgContent)[0] ?? \"\",\n height: parseTag(\"height\", imgContent)[0] ?? \"\",\n };\n }\n return null;\n}\n\nfunction parseEnclosure(itemXml: string): RssEnclosure | null {\n const enclosureTag = /<enclosure[^>]*\\/?>/i.exec(itemXml);\n if (enclosureTag?.[0]) {\n const urlMatch = /url=\"([^\"]*)\"/.exec(enclosureTag[0]);\n const typeMatch = /type=\"([^\"]*)\"/.exec(enclosureTag[0]);\n const lengthMatch = /length=\"([^\"]*)\"/.exec(enclosureTag[0]);\n return {\n url: urlMatch?.[1] ?? \"\",\n type: typeMatch?.[1] ?? \"\",\n length: lengthMatch?.[1] ?? \"\",\n };\n }\n return null;\n}\n\nfunction parseItem(itemXml: string): RssItem {\n return {\n title: parseTag(\"title\", itemXml)[0] ?? \"\",\n link: parseTag(\"link\", itemXml)[0] ?? \"\",\n pubDate: parseTag(\"pubDate\", itemXml)[0] ?? \"\",\n description: parseCDATA(parseTag(\"description\", itemXml)[0]),\n author: parseTag(\"author\", itemXml)[0] ?? \"\",\n category: parseTag(\"category\", itemXml) ?? [],\n comments: parseTag(\"comments\", itemXml)[0] ?? \"\",\n guid: parseTag(\"guid\", itemXml)[0] ?? \"\",\n enclosure: parseEnclosure(itemXml),\n };\n}\n\nexport function parseRssToJson(xml: string): RssFeed {\n try {\n const cleanXml = xml\n .replace(/<!--[\\s\\S]*?-->/g, \"\")\n .replace(/\\s+/g, \" \")\n .trim();\n\n const channelRegex = /<channel>(.*?)<\\/channel>/s;\n const channelMatch = channelRegex.exec(cleanXml);\n\n if (!channelMatch || !channelMatch[1]) {\n throw new Error(\"No channel element found in RSS feed\");\n }\n\n const channelXml = channelMatch[1];\n const channel: RssFeed = {\n title: parseTag(\"title\", channelXml)[0] ?? \"\",\n description: parseCDATA(parseTag(\"description\", channelXml)[0]),\n link: parseTag(\"link\", channelXml)[0] ?? \"\",\n language: parseTag(\"language\", channelXml)[0] ?? \"\",\n copyright: parseTag(\"copyright\", channelXml)[0] ?? \"\",\n lastBuildDate: parseTag(\"lastBuildDate\", channelXml)[0] ?? \"\",\n generator: parseTag(\"generator\", channelXml)[0] ?? \"\",\n docs: parseTag(\"docs\", channelXml)[0] ?? \"\",\n ttl: parseTag(\"ttl\", channelXml)[0] ?? \"\",\n image: parseImage(channelXml),\n items: [],\n };\n\n const itemRegex = /<item>(.*?)<\\/item>/gs;\n let itemMatch: RegExpExecArray | null = itemRegex.exec(channelXml);\n\n while (itemMatch !== null) {\n if (itemMatch[1]) {\n channel.items.push(parseItem(itemMatch[1]));\n }\n itemMatch = itemRegex.exec(channelXml);\n }\n\n return channel;\n } catch {\n return {\n title: \"\",\n description: \"\",\n link: \"\",\n language: \"\",\n copyright: \"\",\n lastBuildDate: \"\",\n generator: \"\",\n docs: \"\",\n ttl: \"\",\n image: null,\n items: [],\n };\n }\n}\n\nexport function createEmptyFeed(): RssFeed {\n return {\n title: \"\",\n description: \"\",\n link: \"\",\n language: \"\",\n copyright: \"\",\n lastBuildDate: \"\",\n generator: \"\",\n docs: \"\",\n ttl: \"\",\n image: null,\n items: [],\n };\n}\n",
|
|
7
|
+
"import type { IAgentRuntime, Plugin, TestCase, TestSuite } from \"@elizaos/core\";\nimport { logger } from \"@elizaos/core\";\nimport {\n getFeedAction,\n listFeedsAction,\n subscribeFeedAction,\n unsubscribeFeedAction,\n} from \"./actions\";\nimport { feedItemsProvider } from \"./providers\";\nimport { RssService } from \"./service\";\n\nconst actionsDisabled = process.env.RSS_DISABLE_ACTIONS === \"true\";\nconst actions = [getFeedAction];\n\nif (!actionsDisabled) {\n actions.push(subscribeFeedAction, unsubscribeFeedAction, listFeedsAction);\n}\nexport const rssPlugin: Plugin = {\n name: \"rss\",\n description: \"RSS/Atom feed monitoring and subscription management\",\n\n config: {\n RSS_FEEDS: process.env.RSS_FEEDS ?? null,\n RSS_DISABLE_ACTIONS: process.env.RSS_DISABLE_ACTIONS ?? null,\n RSS_FEED_FORMAT: process.env.RSS_FEED_FORMAT ?? null,\n RSS_CHECK_INTERVAL_MINUTES: process.env.RSS_CHECK_INTERVAL_MINUTES ?? null,\n },\n\n async init(_config: Record<string, string>, _runtime: IAgentRuntime): Promise<void> {\n logger.info(\"Initializing RSS plugin\");\n },\n\n evaluators: [],\n providers: [feedItemsProvider],\n actions,\n services: [RssService],\n\n tests: [\n {\n name: \"rss_plugin_tests\",\n tests: [\n {\n name: \"rss_test_parser\",\n fn: async (_runtime: IAgentRuntime): Promise<void> => {\n const { parseRssToJson } = await import(\"./parser\");\n\n const sampleRss = `<?xml version=\"1.0\"?>\n <rss version=\"2.0\">\n <channel>\n <title>Test Feed</title>\n <link>https://example.com</link>\n <description>A test RSS feed</description>\n <item>\n <title>Test Article</title>\n <link>https://example.com/article1</link>\n <description>This is a test article</description>\n <pubDate>Mon, 01 Jan 2024 00:00:00 GMT</pubDate>\n <guid>article-1</guid>\n </item>\n </channel>\n </rss>`;\n\n const feed = parseRssToJson(sampleRss);\n\n if (feed.title !== \"Test Feed\") {\n throw new Error(`Expected title \"Test Feed\", got \"${feed.title}\"`);\n }\n\n if (feed.items.length !== 1) {\n throw new Error(`Expected 1 item, got ${feed.items.length}`);\n }\n\n const firstItem = feed.items[0];\n if (firstItem && firstItem.title !== \"Test Article\") {\n throw new Error(`Expected item title \"Test Article\", got \"${firstItem.title}\"`);\n }\n\n logger.info(\"[RSS Test] Parser test passed\");\n },\n },\n {\n name: \"rss_test_url_extraction\",\n fn: async (_runtime: IAgentRuntime): Promise<void> => {\n const { extractUrls } = await import(\"./utils\");\n\n const text = \"Check out https://example.com/feed.rss and http://test.com for more.\";\n const urls = extractUrls(text);\n\n if (urls.length !== 2) {\n throw new Error(`Expected 2 URLs, got ${urls.length}`);\n }\n\n if (!urls.includes(\"https://example.com/feed.rss\")) {\n throw new Error(\"Missing expected URL: https://example.com/feed.rss\");\n }\n\n logger.info(\"[RSS Test] URL extraction test passed\");\n },\n },\n {\n name: \"rss_test_service_exists\",\n fn: async (runtime: IAgentRuntime): Promise<void> => {\n const service = runtime.getService(\"RSS\");\n\n if (!service) {\n throw new Error(\"RSS service not found\");\n }\n\n logger.info(\"[RSS Test] Service existence test passed\");\n },\n },\n ] as TestCase[],\n },\n ] as TestSuite[],\n};\n\nexport default rssPlugin;\n",
|
|
8
|
+
"import type {\n Action,\n ActionExample,\n ActionResult,\n HandlerCallback,\n HandlerOptions,\n IAgentRuntime,\n Memory,\n State,\n} from \"@elizaos/core\";\nimport { createUniqueUuid, MemoryType } from \"@elizaos/core\";\nimport { requireActionSpec } from \"../generated/specs/spec-helpers\";\nimport type { RssService } from \"../service\";\nimport { createMessageReply, extractUrls } from \"../utils\";\n\nconst spec = requireActionSpec(\"GET_FEED\");\n\nexport const getFeedAction: Action = {\n name: spec.name,\n similes: spec.similes ? [...spec.similes] : [],\n validate: async (_runtime: IAgentRuntime, _message: Memory, _state?: State) => {\n return true;\n },\n description: spec.description,\n handler: async (\n runtime: IAgentRuntime,\n message: Memory,\n _state?: State,\n _options?: HandlerOptions,\n callback?: HandlerCallback,\n _responses?: Memory[]\n ): Promise<ActionResult> => {\n const service = runtime.getService(\"RSS\") as RssService;\n if (!service) {\n runtime.logger.error(\"RSS service not found\");\n callback?.(createMessageReply(runtime, message, \"RSS service is not available\"));\n return { success: false, error: \"RSS service not found\" };\n }\n\n const urls = extractUrls(message.content.text || \"\");\n const validUrls = urls.filter(\n (u) => u.match(/rss|feed|atom|xml/i) || u.endsWith(\".rss\") || u.endsWith(\".xml\")\n );\n\n if (!validUrls.length) {\n if (urls.length > 0) {\n validUrls.push(urls[0]);\n } else {\n runtime.logger.warn(\"No valid URLs found in message\");\n callback?.(createMessageReply(runtime, message, \"No valid RSS feed URL provided\"));\n return { success: false, error: \"No valid RSS feed URL provided\" };\n }\n }\n\n const url = validUrls[0];\n if (!url) {\n callback?.(createMessageReply(runtime, message, \"No valid RSS feed URL provided\"));\n return { success: false, error: \"No valid RSS feed URL provided\" };\n }\n const res = await service.fetchUrl(url);\n\n if (!res) {\n runtime.logger.error({ url }, \"Failed to fetch RSS feed\");\n callback?.(createMessageReply(runtime, message, \"Failed to fetch RSS feed\"));\n return { success: false, error: \"Failed to fetch RSS feed\" };\n }\n\n runtime.logger.info(\n { count: res.items.length, title: res.title || url },\n \"Fetched items from RSS feed\"\n );\n\n let newItemCount = 0;\n\n for (const item of res.items) {\n const primaryId = createUniqueUuid(runtime, `${url}_${item.guid}`);\n const fallbackId = createUniqueUuid(runtime, `${url}_${item.title}_${item.pubDate}`);\n\n const existingByGuid = await runtime.getMemoriesByIds([primaryId], \"feeditems\");\n const existingByTitleDate = await runtime.getMemoriesByIds([fallbackId], \"feeditems\");\n\n if (\n (!existingByGuid || existingByGuid.length === 0) &&\n (!existingByTitleDate || existingByTitleDate.length === 0)\n ) {\n const itemId = item.guid ? primaryId : fallbackId;\n\n const itemMemory: Memory = {\n id: itemId,\n entityId: runtime.agentId,\n agentId: runtime.agentId,\n content: {\n text: item.title,\n url: item.link,\n },\n roomId: message.roomId,\n createdAt: Date.now(),\n metadata: {\n type: MemoryType.CUSTOM,\n feedUrl: url,\n feedTitle: res.title,\n title: item.title,\n link: item.link,\n pubDate: item.pubDate,\n description: item.description,\n author: item.author,\n category: item.category.join(\",\"),\n comments: item.comments,\n guid: item.guid,\n },\n };\n\n await runtime.createMemory(itemMemory, \"feeditems\");\n newItemCount++;\n }\n }\n\n if (url) {\n await service.subscribeFeed(url, res.title);\n }\n\n const responseText =\n newItemCount > 0\n ? `Downloaded ${res.items.length} articles from \"${res.title}\", ${newItemCount} new items stored. Feed auto-subscribed for periodic updates.`\n : `Downloaded ${res.items.length} articles from \"${res.title}\", all items already stored. Feed auto-subscribed for periodic updates.`;\n\n callback?.(createMessageReply(runtime, message, responseText));\n return { success: true };\n },\n\n examples: (spec.examples ?? []) as ActionExample[][],\n};\n\nexport default getFeedAction;\n",
|
|
9
|
+
"/**\n * Auto-generated canonical action/provider/evaluator docs for plugin-rss.\n * DO NOT EDIT - Generated from prompts/specs/**.\n */\n\nexport type ActionDoc = {\n name: string;\n description: string;\n similes?: readonly string[];\n parameters?: readonly unknown[];\n examples?: readonly (readonly unknown[])[];\n};\n\nexport type ProviderDoc = {\n name: string;\n description: string;\n position?: number;\n dynamic?: boolean;\n};\n\nexport type EvaluatorDoc = {\n name: string;\n description: string;\n similes?: readonly string[];\n alwaysRun?: boolean;\n examples?: readonly unknown[];\n};\n\nexport const coreActionsSpec = {\n version: \"1.0.0\",\n actions: [\n {\n name: \"GET_NEWSFEED\",\n description: \"Download and parse an RSS/Atom feed from a URL\",\n similes: [\"FETCH_RSS\", \"READ_FEED\", \"DOWNLOAD_FEED\"],\n parameters: [],\n },\n {\n name: \"LIST_RSS_FEEDS\",\n description: \"List all subscribed RSS/Atom feeds\",\n similes: [\"SHOW_RSS_FEEDS\", \"GET_RSS_FEEDS\", \"RSS_SUBSCRIPTIONS\"],\n parameters: [],\n },\n {\n name: \"SUBSCRIBE_RSS_FEED\",\n description: \"Subscribe to an RSS/Atom feed for automatic monitoring\",\n similes: [\"ADD_RSS_FEED\", \"FOLLOW_RSS_FEED\", \"SUBSCRIBE_TO_RSS\"],\n parameters: [],\n },\n {\n name: \"UNSUBSCRIBE_RSS_FEED\",\n description: \"Unsubscribe from an RSS/Atom feed\",\n similes: [\"REMOVE_RSS_FEED\", \"UNFOLLOW_RSS_FEED\", \"DELETE_RSS_FEED\"],\n parameters: [],\n },\n ],\n} as const;\nexport const allActionsSpec = {\n version: \"1.0.0\",\n actions: [\n {\n name: \"GET_NEWSFEED\",\n description: \"Download and parse an RSS/Atom feed from a URL\",\n similes: [\"FETCH_RSS\", \"READ_FEED\", \"DOWNLOAD_FEED\"],\n parameters: [],\n },\n {\n name: \"LIST_RSS_FEEDS\",\n description: \"List all subscribed RSS/Atom feeds\",\n similes: [\"SHOW_RSS_FEEDS\", \"GET_RSS_FEEDS\", \"RSS_SUBSCRIPTIONS\"],\n parameters: [],\n },\n {\n name: \"SUBSCRIBE_RSS_FEED\",\n description: \"Subscribe to an RSS/Atom feed for automatic monitoring\",\n similes: [\"ADD_RSS_FEED\", \"FOLLOW_RSS_FEED\", \"SUBSCRIBE_TO_RSS\"],\n parameters: [],\n },\n {\n name: \"UNSUBSCRIBE_RSS_FEED\",\n description: \"Unsubscribe from an RSS/Atom feed\",\n similes: [\"REMOVE_RSS_FEED\", \"UNFOLLOW_RSS_FEED\", \"DELETE_RSS_FEED\"],\n parameters: [],\n },\n ],\n} as const;\nexport const coreProvidersSpec = {\n version: \"1.0.0\",\n providers: [\n {\n name: \"FEEDITEMS\",\n description: \"Provides recent news and articles from subscribed RSS feeds\",\n dynamic: true,\n },\n ],\n} as const;\nexport const allProvidersSpec = {\n version: \"1.0.0\",\n providers: [\n {\n name: \"FEEDITEMS\",\n description: \"Provides recent news and articles from subscribed RSS feeds\",\n dynamic: true,\n },\n ],\n} as const;\nexport const coreEvaluatorsSpec = {\n version: \"1.0.0\",\n evaluators: [],\n} as const;\nexport const allEvaluatorsSpec = {\n version: \"1.0.0\",\n evaluators: [],\n} as const;\n\nexport const coreActionDocs: readonly ActionDoc[] = coreActionsSpec.actions;\nexport const allActionDocs: readonly ActionDoc[] = allActionsSpec.actions;\nexport const coreProviderDocs: readonly ProviderDoc[] = coreProvidersSpec.providers;\nexport const allProviderDocs: readonly ProviderDoc[] = allProvidersSpec.providers;\nexport const coreEvaluatorDocs: readonly EvaluatorDoc[] = coreEvaluatorsSpec.evaluators;\nexport const allEvaluatorDocs: readonly EvaluatorDoc[] = allEvaluatorsSpec.evaluators;\n",
|
|
10
|
+
"/**\n * Helper functions to lookup action/provider/evaluator specs by name.\n * These allow language-specific implementations to import their text content\n * (description, similes, examples) from the centralized specs.\n *\n * DO NOT EDIT the spec data - update prompts/actions.json, prompts/providers.json, prompts/evaluators.json and regenerate.\n */\n\nimport {\n type ActionDoc,\n allActionDocs,\n allEvaluatorDocs,\n allProviderDocs,\n coreActionDocs,\n coreEvaluatorDocs,\n coreProviderDocs,\n type EvaluatorDoc,\n type ProviderDoc,\n} from \"./specs\";\n\n// Build lookup maps for O(1) access\nconst coreActionMap = new Map<string, ActionDoc>(coreActionDocs.map((doc) => [doc.name, doc]));\nconst allActionMap = new Map<string, ActionDoc>(allActionDocs.map((doc) => [doc.name, doc]));\nconst coreProviderMap = new Map<string, ProviderDoc>(\n coreProviderDocs.map((doc) => [doc.name, doc])\n);\nconst allProviderMap = new Map<string, ProviderDoc>(allProviderDocs.map((doc) => [doc.name, doc]));\nconst coreEvaluatorMap = new Map<string, EvaluatorDoc>(\n coreEvaluatorDocs.map((doc) => [doc.name, doc])\n);\nconst allEvaluatorMap = new Map<string, EvaluatorDoc>(\n allEvaluatorDocs.map((doc) => [doc.name, doc])\n);\n\n/**\n * Get an action spec by name from the core specs.\n * @param name - The action name\n * @returns The action spec or undefined if not found\n */\nexport function getActionSpec(name: string): ActionDoc | undefined {\n return coreActionMap.get(name) ?? allActionMap.get(name);\n}\n\n/**\n * Get an action spec by name, throwing if not found.\n * @param name - The action name\n * @returns The action spec\n * @throws Error if the action is not found\n */\nexport function requireActionSpec(name: string): ActionDoc {\n const spec = getActionSpec(name);\n if (!spec) {\n throw new Error(`Action spec not found: ${name}`);\n }\n return spec;\n}\n\n/**\n * Get a provider spec by name from the core specs.\n * @param name - The provider name\n * @returns The provider spec or undefined if not found\n */\nexport function getProviderSpec(name: string): ProviderDoc | undefined {\n return coreProviderMap.get(name) ?? allProviderMap.get(name);\n}\n\n/**\n * Get a provider spec by name, throwing if not found.\n * @param name - The provider name\n * @returns The provider spec\n * @throws Error if the provider is not found\n */\nexport function requireProviderSpec(name: string): ProviderDoc {\n const spec = getProviderSpec(name);\n if (!spec) {\n throw new Error(`Provider spec not found: ${name}`);\n }\n return spec;\n}\n\n/**\n * Get an evaluator spec by name from the core specs.\n * @param name - The evaluator name\n * @returns The evaluator spec or undefined if not found\n */\nexport function getEvaluatorSpec(name: string): EvaluatorDoc | undefined {\n return coreEvaluatorMap.get(name) ?? allEvaluatorMap.get(name);\n}\n\n/**\n * Get an evaluator spec by name, throwing if not found.\n * @param name - The evaluator name\n * @returns The evaluator spec\n * @throws Error if the evaluator is not found\n */\nexport function requireEvaluatorSpec(name: string): EvaluatorDoc {\n const spec = getEvaluatorSpec(name);\n if (!spec) {\n throw new Error(`Evaluator spec not found: ${name}`);\n }\n return spec;\n}\n\n// Re-export types for convenience\nexport type { ActionDoc, ProviderDoc, EvaluatorDoc };\n",
|
|
11
|
+
"import type {\n Action,\n ActionExample,\n ActionResult,\n HandlerCallback,\n HandlerOptions,\n IAgentRuntime,\n Memory,\n State,\n} from \"@elizaos/core\";\nimport { requireActionSpec } from \"../generated/specs/spec-helpers\";\nimport type { RssService } from \"../service\";\nimport type { FeedSubscriptionMetadata } from \"../types\";\nimport { createMessageReply, formatRelativeTime } from \"../utils\";\n\nconst spec = requireActionSpec(\"LIST_FEEDS\");\n\nexport const listFeedsAction: Action = {\n name: spec.name,\n similes: spec.similes ? [...spec.similes] : [],\n validate: async (_runtime: IAgentRuntime, _message: Memory, _state?: State) => {\n return true;\n },\n description: spec.description,\n handler: async (\n runtime: IAgentRuntime,\n message: Memory,\n _state?: State,\n _options?: HandlerOptions,\n callback?: HandlerCallback,\n _responses?: Memory[]\n ): Promise<ActionResult> => {\n const service = runtime.getService(\"RSS\") as RssService;\n if (!service) {\n runtime.logger.error(\"RSS service not found\");\n callback?.(createMessageReply(runtime, message, \"RSS service is not available\"));\n return { success: false, error: \"RSS service not found\" };\n }\n\n const feeds = await service.getSubscribedFeeds();\n\n if (!feeds || feeds.length === 0) {\n callback?.(\n createMessageReply(\n runtime,\n message,\n \"No RSS feeds are currently subscribed. Use the subscribe action to add feeds.\"\n )\n );\n return { success: true };\n }\n\n let feedList = `You are subscribed to ${feeds.length} RSS feed${feeds.length > 1 ? \"s\" : \"\"}:\\n\\n`;\n\n feeds.forEach((feed, index) => {\n const title = feed.content.text || \"Untitled Feed\";\n const url = feed.content.url || \"Unknown URL\";\n const metadata = feed.metadata as FeedSubscriptionMetadata;\n const lastChecked = metadata?.lastChecked;\n const itemCount = metadata?.lastItemCount || 0;\n\n feedList += `${index + 1}. ${title}\\n`;\n feedList += ` URL: ${url}\\n`;\n\n if (lastChecked && lastChecked > 0) {\n const timeStr = formatRelativeTime(lastChecked);\n feedList += ` Last checked: ${timeStr} (${itemCount} items)\\n`;\n } else {\n feedList += ` Last checked: Never\\n`;\n }\n\n feedList += \"\\n\";\n });\n\n callback?.(createMessageReply(runtime, message, feedList.trim()));\n return { success: true };\n },\n\n examples: (spec.examples ?? []) as ActionExample[][],\n};\n\nexport default listFeedsAction;\n",
|
|
12
|
+
"import type {\n Action,\n ActionExample,\n ActionResult,\n HandlerCallback,\n HandlerOptions,\n IAgentRuntime,\n Memory,\n State,\n} from \"@elizaos/core\";\nimport { requireActionSpec } from \"../generated/specs/spec-helpers\";\nimport type { RssService } from \"../service\";\nimport { createMessageReply, extractUrls } from \"../utils\";\n\nconst spec = requireActionSpec(\"SUBSCRIBE_FEED\");\n\nexport const subscribeFeedAction: Action = {\n name: spec.name,\n similes: spec.similes ? [...spec.similes] : [],\n validate: async (_runtime: IAgentRuntime, _message: Memory, _state?: State) => {\n return true;\n },\n description: spec.description,\n handler: async (\n runtime: IAgentRuntime,\n message: Memory,\n _state?: State,\n _options?: HandlerOptions,\n callback?: HandlerCallback,\n _responses?: Memory[]\n ): Promise<ActionResult> => {\n const service = runtime.getService(\"RSS\") as RssService;\n if (!service) {\n runtime.logger.error(\"RSS service not found\");\n callback?.(createMessageReply(runtime, message, \"RSS service is not available\"));\n return { success: false, error: \"RSS service not found\" };\n }\n\n const urls = extractUrls(message.content.text || \"\");\n\n if (!urls.length) {\n runtime.logger.warn(\"No valid URLs found in message\");\n callback?.(createMessageReply(runtime, message, \"Please provide a valid RSS feed URL\"));\n return { success: false, error: \"No valid URLs found\" };\n }\n\n const url = urls[0];\n if (!url) {\n callback?.(createMessageReply(runtime, message, \"Please provide a valid RSS feed URL\"));\n return { success: false, error: \"No valid URLs found\" };\n }\n runtime.logger.debug({ url }, \"Attempting to subscribe to feed\");\n\n const feedData = await service.fetchUrl(url);\n\n if (!feedData || !feedData.items) {\n runtime.logger.error({ url }, \"Invalid or empty RSS feed\");\n callback?.(\n createMessageReply(\n runtime,\n message,\n `Unable to fetch RSS feed from ${url}. Please check the URL and try again.`\n )\n );\n return { success: false, error: \"Invalid or empty RSS feed\" };\n }\n\n const success = await service.subscribeFeed(url, feedData.title);\n\n if (success) {\n const itemCount = feedData.items?.length || 0;\n callback?.(\n createMessageReply(\n runtime,\n message,\n `Successfully subscribed to \"${feedData.title}\" (${url}). Found ${itemCount} items in the feed.`\n )\n );\n } else {\n callback?.(\n createMessageReply(\n runtime,\n message,\n `Failed to subscribe to ${url}. You may already be subscribed to this feed.`\n )\n );\n }\n return { success };\n },\n\n examples: (spec.examples ?? []) as ActionExample[][],\n};\n\nexport default subscribeFeedAction;\n",
|
|
13
|
+
"import type {\n Action,\n ActionExample,\n ActionResult,\n HandlerCallback,\n HandlerOptions,\n IAgentRuntime,\n Memory,\n State,\n} from \"@elizaos/core\";\nimport { requireActionSpec } from \"../generated/specs/spec-helpers\";\nimport type { RssService } from \"../service\";\nimport { createMessageReply, extractUrls } from \"../utils\";\n\nconst spec = requireActionSpec(\"UNSUBSCRIBE_FEED\");\n\nexport const unsubscribeFeedAction: Action = {\n name: spec.name,\n similes: spec.similes ? [...spec.similes] : [],\n validate: async (_runtime: IAgentRuntime, _message: Memory, _state?: State) => {\n return true;\n },\n description: spec.description,\n handler: async (\n runtime: IAgentRuntime,\n message: Memory,\n _state?: State,\n _options?: HandlerOptions,\n callback?: HandlerCallback,\n _responses?: Memory[]\n ): Promise<ActionResult> => {\n const service = runtime.getService(\"RSS\") as RssService;\n if (!service) {\n runtime.logger.error(\"RSS service not found\");\n callback?.(createMessageReply(runtime, message, \"RSS service is not available\"));\n return { success: false, error: \"RSS service not found\" };\n }\n\n const urls = extractUrls(message.content.text || \"\");\n\n if (!urls.length) {\n runtime.logger.warn(\"No valid URLs found in message\");\n callback?.(\n createMessageReply(\n runtime,\n message,\n \"Please provide a valid RSS feed URL to unsubscribe from\"\n )\n );\n return { success: false, error: \"No valid URLs found\" };\n }\n\n const url = urls[0];\n if (!url) {\n callback?.(\n createMessageReply(\n runtime,\n message,\n \"Please provide a valid RSS feed URL to unsubscribe from\"\n )\n );\n return { success: false, error: \"No valid URLs found\" };\n }\n runtime.logger.debug({ url }, \"Attempting to unsubscribe from feed\");\n\n const success = await service.unsubscribeFeed(url);\n\n if (success) {\n callback?.(createMessageReply(runtime, message, `Successfully unsubscribed from ${url}`));\n } else {\n callback?.(\n createMessageReply(\n runtime,\n message,\n `Unable to unsubscribe from ${url}. You may not be subscribed to this feed.`\n )\n );\n }\n return { success };\n },\n\n examples: (spec.examples ?? []) as ActionExample[][],\n};\n\nexport default unsubscribeFeedAction;\n",
|
|
14
|
+
"import type { IAgentRuntime, Memory, Provider, ProviderResult, State } from \"@elizaos/core\";\nimport { logger } from \"@elizaos/core\";\nimport { requireProviderSpec } from \"../generated/specs/spec-helpers\";\nimport type { FeedItemMetadata } from \"../types\";\n\nconst spec = requireProviderSpec(\"feedItems\");\n\nexport const feedItemsProvider: Provider = {\n name: spec.name,\n description: \"Provides recent news and articles from subscribed RSS feeds\",\n get: async (runtime: IAgentRuntime, _message: Memory, _state: State): Promise<ProviderResult> => {\n try {\n const items = await runtime.getMemories({\n tableName: \"feeditems\",\n unique: false,\n });\n\n if (!items || items.length === 0) {\n return {\n data: { count: 0 },\n values: {},\n text: \"No RSS feed items available. Subscribe to feeds to see news articles here.\",\n };\n }\n\n const sortedItems = [...items].sort((a, b) => {\n const timeA = a.createdAt || 0;\n const timeB = b.createdAt || 0;\n return timeB - timeA;\n });\n\n const recentItems = sortedItems.slice(0, 50);\n const itemsByFeed = new Map<string, Memory[]>();\n for (const item of recentItems) {\n const metadata = item.metadata as FeedItemMetadata;\n const feedTitle = metadata?.feedTitle || \"Unknown Feed\";\n if (!itemsByFeed.has(feedTitle)) {\n itemsByFeed.set(feedTitle, []);\n }\n const feedItems = itemsByFeed.get(feedTitle);\n if (feedItems) {\n feedItems.push(item);\n }\n }\n\n const format = runtime.getSetting(\"RSS_FEED_FORMAT\") || \"csv\";\n\n let outputText: string;\n\n if (format === \"markdown\") {\n outputText = `# Recent RSS Feed Items (${recentItems.length} items from ${itemsByFeed.size} feeds)\\n\\n`;\n\n for (const [feedTitle, feedItems] of itemsByFeed) {\n outputText += `## ${feedTitle} (${feedItems.length} items)\\n\\n`;\n\n for (const item of feedItems) {\n const metadata = item.metadata as FeedItemMetadata;\n const title = item.content.text || metadata?.title || \"Untitled\";\n const url = item.content.url || metadata?.link || \"\";\n const description = metadata?.description || \"\";\n const pubDate = metadata?.pubDate || \"\";\n const author = metadata?.author || \"\";\n\n outputText += `### ${title}\\n`;\n if (url) {\n outputText += `- URL: ${url}\\n`;\n }\n if (pubDate) {\n outputText += `- Published: ${pubDate}\\n`;\n }\n if (author) {\n outputText += `- Author: ${author}\\n`;\n }\n if (description) {\n const shortDesc =\n description.length > 200 ? `${description.substring(0, 200)}...` : description;\n outputText += `- Description: ${shortDesc}\\n`;\n }\n outputText += \"\\n\";\n }\n }\n } else {\n outputText = `# RSS Feed Items (${recentItems.length} from ${itemsByFeed.size} feeds)\\n`;\n outputText += \"Feed,Title,URL,Published,Description\\n\";\n\n for (const item of recentItems) {\n const metadata = item.metadata as FeedItemMetadata;\n const feedTitle = (metadata?.feedTitle || \"Unknown\").replace(/\"/g, '\"\"');\n const title = (item.content.text || \"\").replace(/\"/g, '\"\"');\n const url = item.content.url || \"\";\n const pubDate = metadata?.pubDate || \"\";\n const description = (metadata?.description || \"\").replace(/\"/g, '\"\"').substring(0, 200);\n\n outputText += `\"${feedTitle}\",\"${title}\",\"${url}\",\"${pubDate}\",\"${description}\"\\n`;\n }\n }\n\n const data = {\n count: recentItems.length,\n totalCount: items.length,\n feedCount: itemsByFeed.size,\n };\n\n const values = {\n itemCount: recentItems.length,\n feedCount: itemsByFeed.size,\n };\n\n return {\n data,\n values,\n text: outputText,\n };\n } catch (error) {\n logger.error({ error }, \"Error in FEEDITEMS provider\");\n return {\n data: { count: 0, error: String(error) },\n values: {},\n text: \"Error loading RSS feed items.\",\n };\n }\n },\n};\n\nexport default feedItemsProvider;\n",
|
|
15
|
+
"import {\n createUniqueUuid,\n type IAgentRuntime,\n logger,\n type Memory,\n MemoryType,\n Service,\n type TaskWorker,\n} from \"@elizaos/core\";\nimport { createEmptyFeed, parseRssToJson } from \"./parser\";\nimport type { FeedSubscriptionMetadata, RssFeed } from \"./types\";\n\nexport class RssService extends Service {\n private isRunning = false;\n\n static serviceType = \"RSS\";\n capabilityDescription = \"The agent is able to deal with RSS/atom feeds\";\n\n async fetchUrl(urlToFetch: string): Promise<RssFeed | null> {\n let response: string | undefined;\n try {\n const resp = await fetch(urlToFetch);\n response = await resp.text();\n\n if (!response) {\n logger.warn({ url: urlToFetch }, \"No response for feed URL\");\n return null;\n }\n\n const data = parseRssToJson(response);\n if (!data || !data.title) {\n logger.warn({ url: urlToFetch }, \"No RSS data found in response\");\n return createEmptyFeed();\n }\n\n return data;\n } catch (error) {\n logger.error({ error, url: urlToFetch }, \"Error fetching RSS feed\");\n return null;\n }\n }\n\n async subscribeFeed(url: string, title?: string): Promise<boolean> {\n try {\n const feedId = createUniqueUuid(this.runtime, `feed_sub_${url}`);\n const existing = await this.runtime.getMemoriesByIds([feedId], \"feedsubscriptions\");\n if (existing && existing.length > 0) {\n logger.info({ url }, \"Already subscribed to feed\");\n return true;\n }\n\n let feedTitle = title;\n if (!feedTitle) {\n const feedData = await this.fetchUrl(url);\n if (feedData) {\n feedTitle = feedData.title;\n }\n }\n\n const subscriptionMemory: Memory = {\n id: feedId,\n entityId: this.runtime.agentId,\n agentId: this.runtime.agentId,\n content: {\n text: feedTitle || url,\n url: url,\n },\n roomId: this.runtime.agentId,\n createdAt: Date.now(),\n metadata: {\n type: MemoryType.CUSTOM,\n subscribedAt: Date.now(),\n lastChecked: 0,\n lastItemCount: 0,\n },\n };\n\n await this.runtime.createMemory(subscriptionMemory, \"feedsubscriptions\");\n logger.info({ url, title: feedTitle }, \"Subscribed to RSS feed\");\n return true;\n } catch (error) {\n logger.error({ error, url }, \"Error subscribing to feed\");\n return false;\n }\n }\n\n async unsubscribeFeed(url: string): Promise<boolean> {\n try {\n const feedId = createUniqueUuid(this.runtime, `feed_sub_${url}`);\n const existing = await this.runtime.getMemoriesByIds([feedId], \"feedsubscriptions\");\n if (!existing || existing.length === 0) {\n logger.warn({ url }, \"Not subscribed to feed\");\n return false;\n }\n\n await this.runtime.deleteMemory(feedId);\n logger.info({ url }, \"Unsubscribed from RSS feed\");\n return true;\n } catch (error) {\n logger.error({ error, url }, \"Error unsubscribing from feed\");\n return false;\n }\n }\n\n async getSubscribedFeeds(): Promise<Memory[]> {\n try {\n const feeds = await this.runtime.getMemories({\n tableName: \"feedsubscriptions\",\n unique: false,\n });\n return feeds || [];\n } catch (error) {\n logger.error({ error }, \"Error getting subscribed feeds\");\n return [];\n }\n }\n\n async checkAllFeeds(): Promise<void> {\n try {\n const feeds = await this.getSubscribedFeeds();\n logger.info({ count: feeds.length }, \"Checking subscribed RSS feeds\");\n\n for (const feed of feeds) {\n try {\n const url = feed.content.url;\n if (!url) {\n logger.warn({ feedId: feed.id }, \"Feed subscription missing URL\");\n continue;\n }\n\n const feedData = await this.fetchUrl(url);\n\n if (!feedData || !feedData.items) {\n logger.warn({ url }, \"No data returned for feed\");\n continue;\n }\n\n let newItemCount = 0;\n\n for (const item of feedData.items) {\n const primaryId = createUniqueUuid(this.runtime, `${url}_${item.guid}`);\n const fallbackId = createUniqueUuid(\n this.runtime,\n `${url}_${item.title}_${item.pubDate}`\n );\n\n const existingByGuid = await this.runtime.getMemoriesByIds([primaryId], \"feeditems\");\n const existingByTitleDate = await this.runtime.getMemoriesByIds(\n [fallbackId],\n \"feeditems\"\n );\n\n if (\n (!existingByGuid || existingByGuid.length === 0) &&\n (!existingByTitleDate || existingByTitleDate.length === 0)\n ) {\n const itemId = item.guid ? primaryId : fallbackId;\n\n const itemMemory: Memory = {\n id: itemId,\n entityId: this.runtime.agentId,\n agentId: this.runtime.agentId,\n content: {\n text: item.title,\n url: item.link,\n },\n roomId: this.runtime.agentId,\n createdAt: Date.now(),\n metadata: {\n type: MemoryType.CUSTOM,\n feedUrl: url,\n feedTitle: feedData.title,\n title: item.title,\n link: item.link,\n pubDate: item.pubDate,\n description: item.description,\n author: item.author,\n category: item.category.join(\",\"),\n comments: item.comments,\n guid: item.guid,\n },\n };\n\n await this.runtime.createMemory(itemMemory, \"feeditems\");\n newItemCount++;\n }\n }\n\n if (!feed.id) {\n continue;\n }\n const currentMetadata = feed.metadata as FeedSubscriptionMetadata;\n await this.runtime.updateMemory({\n id: feed.id,\n metadata: {\n type: MemoryType.CUSTOM,\n subscribedAt: currentMetadata.subscribedAt ?? Date.now(),\n lastChecked: Date.now(),\n lastItemCount: feedData.items.length,\n },\n });\n\n if (newItemCount > 0) {\n logger.info(\n { count: newItemCount, feed: feedData.title || url },\n \"Found new items from feed\"\n );\n } else {\n }\n } catch (error) {\n logger.error({ error, url: feed.content.url }, \"Error checking feed\");\n }\n }\n\n logger.info(\"Completed checking all RSS feeds\");\n } catch (error) {\n logger.error({ error }, \"Error in checkAllFeeds\");\n }\n }\n\n private async loadInitialFeeds(): Promise<void> {\n const rssFeeds = this.runtime.getSetting(\"RSS_FEEDS\");\n\n if (!rssFeeds) {\n return;\n }\n\n try {\n let feedUrls: string[] = [];\n\n if (typeof rssFeeds === \"string\") {\n try {\n const parsed = JSON.parse(rssFeeds);\n if (Array.isArray(parsed)) {\n feedUrls = parsed;\n } else {\n feedUrls = rssFeeds\n .split(\",\")\n .map((url) => url.trim())\n .filter((url) => url.length > 0);\n }\n } catch {\n feedUrls = rssFeeds\n .split(\",\")\n .map((url) => url.trim())\n .filter((url) => url.length > 0);\n }\n } else if (Array.isArray(rssFeeds)) {\n feedUrls = rssFeeds;\n }\n\n logger.info({ count: feedUrls.length }, \"Loading RSS feeds from configuration\");\n\n for (const url of feedUrls) {\n await this.subscribeFeed(url);\n }\n\n logger.info(\"Completed loading initial RSS feeds\");\n } catch (error) {\n logger.error({ error }, \"Error loading initial RSS feeds from configuration\");\n }\n }\n\n private registerFeedCheckWorker(): void {\n const worker: TaskWorker = {\n name: \"RSS_FEED_CHECK\",\n validate: async (_runtime, _message) => {\n return true;\n },\n execute: async (runtime) => {\n try {\n logger.debug(\"Executing RSS feed check task\");\n const rssService = runtime.getService(\"RSS\") as RssService;\n if (rssService) {\n await rssService.checkAllFeeds();\n }\n } catch (error) {\n logger.error({ error }, \"Error executing RSS feed check task\");\n }\n },\n };\n\n this.runtime.registerTaskWorker(worker);\n }\n\n async start(): Promise<void> {\n if (this.isRunning) {\n logger.warn(\"RSS service is already running\");\n return;\n }\n\n try {\n logger.info(\"Starting RSS service...\");\n this.registerFeedCheckWorker();\n await this.loadInitialFeeds();\n\n const checkIntervalSetting = this.runtime.getSetting(\"RSS_CHECK_INTERVAL_MINUTES\");\n const checkIntervalMinutes =\n typeof checkIntervalSetting === \"number\" ? checkIntervalSetting : 15;\n const intervalInMs = checkIntervalMinutes * 60 * 1000;\n\n await this.runtime.createTask({\n name: \"RSS_FEED_CHECK\",\n description: \"Periodically check RSS feeds for new items\",\n worldId: this.runtime.agentId,\n metadata: {\n createdAt: String(Date.now()),\n updatedAt: Date.now(),\n updateInterval: intervalInMs,\n },\n tags: [\"queue\", \"repeat\", \"rss\"],\n });\n\n logger.info({ interval: checkIntervalMinutes }, \"RSS periodic feed check task created\");\n\n this.isRunning = true;\n logger.info(\"RSS service started successfully\");\n } catch (error) {\n logger.error({ error }, \"Error starting RSS service\");\n throw error;\n }\n }\n\n static async start(runtime: IAgentRuntime): Promise<RssService> {\n const service = new RssService(runtime);\n await service.start();\n return service;\n }\n\n async stop(): Promise<void> {\n if (!this.isRunning) {\n logger.warn(\"RSS service is not running\");\n return;\n }\n\n try {\n logger.info(\"Stopping RSS service...\");\n\n this.isRunning = false;\n logger.info(\"RSS service stopped successfully\");\n } catch (error) {\n logger.error({ error }, \"Error stopping RSS service\");\n throw error;\n }\n }\n\n static async stop(runtime: IAgentRuntime): Promise<void> {\n const service = runtime.getService(RssService.serviceType);\n if (!service) {\n throw new Error(`${RssService.serviceType} service not found`);\n }\n await service.stop();\n }\n\n isServiceRunning(): boolean {\n return this.isRunning;\n }\n}\n\nexport const rssService = RssService;\n"
|
|
16
|
+
],
|
|
17
|
+
"mappings": "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",
|
|
18
|
+
"debugId": "989BA756F7E002CD64756E2164756E21",
|
|
19
|
+
"names": []
|
|
20
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@elizaos/plugin-rss",
|
|
3
|
+
"description": "RSS and Atom feed integration plugin for elizaOS - enables agents to fetch and monitor news feeds",
|
|
4
|
+
"version": "2.0.0-alpha.1",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/node/index.node.js",
|
|
7
|
+
"module": "dist/node/index.node.js",
|
|
8
|
+
"types": "dist/index.d.ts",
|
|
9
|
+
"sideEffects": false,
|
|
10
|
+
"license": "MIT",
|
|
11
|
+
"author": "elizaOS Team",
|
|
12
|
+
"keywords": [
|
|
13
|
+
"plugin",
|
|
14
|
+
"elizaos",
|
|
15
|
+
"rss",
|
|
16
|
+
"atom",
|
|
17
|
+
"feed",
|
|
18
|
+
"news",
|
|
19
|
+
"syndication"
|
|
20
|
+
],
|
|
21
|
+
"repository": {
|
|
22
|
+
"type": "git",
|
|
23
|
+
"url": "https://github.com/elizaos/eliza"
|
|
24
|
+
},
|
|
25
|
+
"homepage": "https://elizaos.ai",
|
|
26
|
+
"bugs": {
|
|
27
|
+
"url": "https://github.com/elizaos/eliza/issues"
|
|
28
|
+
},
|
|
29
|
+
"exports": {
|
|
30
|
+
"./package.json": "./package.json",
|
|
31
|
+
".": {
|
|
32
|
+
"types": "./dist/index.d.ts",
|
|
33
|
+
"node": {
|
|
34
|
+
"types": "./dist/node/index.d.ts",
|
|
35
|
+
"import": "./dist/node/index.node.js",
|
|
36
|
+
"default": "./dist/node/index.node.js"
|
|
37
|
+
},
|
|
38
|
+
"bun": {
|
|
39
|
+
"types": "./dist/node/index.d.ts",
|
|
40
|
+
"default": "./dist/node/index.node.js"
|
|
41
|
+
},
|
|
42
|
+
"default": "./dist/node/index.node.js"
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
"files": [
|
|
46
|
+
"dist",
|
|
47
|
+
"rust",
|
|
48
|
+
"README.md"
|
|
49
|
+
],
|
|
50
|
+
"dependencies": {
|
|
51
|
+
"@elizaos/core": "workspace:*"
|
|
52
|
+
},
|
|
53
|
+
"devDependencies": {
|
|
54
|
+
"@types/bun": "^1.3.5",
|
|
55
|
+
"@types/node": "^25.0.3",
|
|
56
|
+
"typescript": "^5.9.3",
|
|
57
|
+
"@biomejs/biome": "^2.3.11"
|
|
58
|
+
},
|
|
59
|
+
"scripts": {
|
|
60
|
+
"build:ts": "bun run build.ts",
|
|
61
|
+
"dev": "bun run build.ts --watch",
|
|
62
|
+
"clean": "rm -rf dist .turbo node_modules",
|
|
63
|
+
"format": "bunx @biomejs/biome format --write .",
|
|
64
|
+
"format:check": "bunx @biomejs/biome format .",
|
|
65
|
+
"typecheck": "tsc --noEmit",
|
|
66
|
+
"test": "vitest run",
|
|
67
|
+
"test:ts": "vitest run",
|
|
68
|
+
"lint": "bunx @biomejs/biome check --write --unsafe .",
|
|
69
|
+
"lint:check": "bunx @biomejs/biome check .",
|
|
70
|
+
"build": "bun run build.ts"
|
|
71
|
+
},
|
|
72
|
+
"publishConfig": {
|
|
73
|
+
"access": "public"
|
|
74
|
+
},
|
|
75
|
+
"agentConfig": {
|
|
76
|
+
"pluginType": "elizaos:plugin:1.0.0",
|
|
77
|
+
"pluginParameters": {
|
|
78
|
+
"RSS_FEEDS": {
|
|
79
|
+
"type": "string",
|
|
80
|
+
"description": "JSON array or comma-separated list of feed URLs to auto-subscribe",
|
|
81
|
+
"required": false,
|
|
82
|
+
"sensitive": false
|
|
83
|
+
},
|
|
84
|
+
"RSS_DISABLE_ACTIONS": {
|
|
85
|
+
"type": "boolean",
|
|
86
|
+
"description": "Set to true to disable subscription management actions",
|
|
87
|
+
"required": false,
|
|
88
|
+
"default": false,
|
|
89
|
+
"sensitive": false
|
|
90
|
+
},
|
|
91
|
+
"RSS_FEED_FORMAT": {
|
|
92
|
+
"type": "string",
|
|
93
|
+
"description": "Output format for feed items: 'csv' (compact) or 'markdown' (readable)",
|
|
94
|
+
"required": false,
|
|
95
|
+
"default": "csv",
|
|
96
|
+
"sensitive": false
|
|
97
|
+
},
|
|
98
|
+
"RSS_CHECK_INTERVAL_MINUTES": {
|
|
99
|
+
"type": "number",
|
|
100
|
+
"description": "Interval in minutes between feed checks",
|
|
101
|
+
"required": false,
|
|
102
|
+
"default": 15,
|
|
103
|
+
"sensitive": false
|
|
104
|
+
}
|
|
105
|
+
},
|
|
106
|
+
"description": "RSS and Atom feed integration plugin for elizaOS"
|
|
107
|
+
}
|
|
108
|
+
}
|