Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion __tests__/env.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ describe('Environment Setup', () => {
it('should have project package.json with correct name', () => {
const packageJsonPath = path.join(process.cwd(), 'package.json');
const pkg = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
expect(pkg.name).toBe('twitter-rss-agent');
expect(pkg.name).toBe('@elizaos/plugin-twitter-rss');
expect(pkg.license).toBe('MIT');
});
});
2 changes: 1 addition & 1 deletion __tests__/error-handling.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { describe, it, expect } from 'vitest';
import { RSSServerService } from '../src/plugin';
import { RSSServerService } from '../src/services/rssServerService';

describe('Service Stop Handling', () => {
it('should handle missing service gracefully', async () => {
Expand Down
2 changes: 1 addition & 1 deletion __tests__/service.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { describe, it, expect, vi } from 'vitest';
import { TwitterRSSService } from '../src/plugin';
import { TwitterRSSService } from '../src/services/twitterRSSService';
import { createMockRuntime } from './test-utils';

describe('TwitterRSSService failure cases', () => {
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"name": "twitter-rss-agent",
"name": "@elizaos/plugin-twitter-rss",
"description": "ElizaOS Twitter List RSS Agent",
"version": "0.1.0",
"type": "module",
Expand Down
75 changes: 75 additions & 0 deletions src/actions/getRSSStatus.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import { Action, Content, HandlerCallback, IAgentRuntime, Memory, State } from '@elizaos/core';
import fs from 'fs/promises';
import path from 'path';
import { logger } from '@elizaos/core';
Comment on lines +1 to +4
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Yo, throw the node: protocol on those built-ins, queen.

Biome is already side-eyeing you. Do the explicit import dance so future linters stop nagging.

-import fs from 'fs/promises';
-import path from 'path';
+import fs from 'node:fs/promises';
+import path from 'node:path';
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
import { Action, Content, HandlerCallback, IAgentRuntime, Memory, State } from '@elizaos/core';
import fs from 'fs/promises';
import path from 'path';
import { logger } from '@elizaos/core';
import { Action, Content, HandlerCallback, IAgentRuntime, Memory, State } from '@elizaos/core';
import fs from 'node:fs/promises';
import path from 'node:path';
import { logger } from '@elizaos/core';
🧰 Tools
🪛 Biome (1.9.4)

[error] 2-2: A Node.js builtin module should be imported with the node: protocol.

Using the node: protocol is more explicit and signals that the imported module belongs to Node.js.
Unsafe fix: Add the node: protocol.

(lint/style/useNodejsImportProtocol)


[error] 3-3: A Node.js builtin module should be imported with the node: protocol.

Using the node: protocol is more explicit and signals that the imported module belongs to Node.js.
Unsafe fix: Add the node: protocol.

(lint/style/useNodejsImportProtocol)

🤖 Prompt for AI Agents
In src/actions/getRSSStatus.ts at lines 1 to 4, the imports of built-in Node.js
modules 'fs/promises' and 'path' should use the 'node:' protocol prefix to
comply with modern Node.js import standards and avoid linter warnings. Update
the import statements to import from 'node:fs/promises' and 'node:path'
respectively, keeping the rest of the import syntax unchanged.


export const getRSSStatusAction: Action = {
name: 'GET_RSS_STATUS',
similes: ['RSS_STATUS', 'FEED_STATUS', 'CHECK_RSS'],
description: 'Get current status of RSS feed and monitoring lists',

validate: async (
_runtime: IAgentRuntime,
_message: Memory,
_state: State
): Promise<boolean> => {
return true;
},

Comment on lines +6 to +18
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Double dip on @elizaos/core imports is extra carbs.

You’re importing from the same package twice. Collapse them – your bundle (and my patience) will thank you.

🤖 Prompt for AI Agents
In src/actions/getRSSStatus.ts between lines 6 and 18, you have multiple import
statements from '@elizaos/core'. Combine these into a single import statement to
reduce redundancy and improve code clarity. This means merging all imported
members from '@elizaos/core' into one import line at the top of the file.

handler: async (
runtime: IAgentRuntime,
message: Memory,
_state: State,
_options: any,
callback: HandlerCallback
) => {
try {
const outputDir =
runtime.getSetting?.('RSS_OUTPUT_DIR') || process.env.RSS_OUTPUT_DIR || './rss-feeds';
const rssFile = path.join(outputDir, 'twitter_lists.xml');

let status = 'RSS Feed Status:\n';

try {
const stats = await fs.stat(rssFile);
status += `📄 RSS file exists: ${rssFile}\n`;
status += `📅 Last modified: ${stats.mtime.toLocaleString()}\n`;
status += `📊 File size: ${(stats.size / 1024).toFixed(1)} KB\n`;
} catch {
status += `❌ RSS file not found\n`;
}

const lists = (
runtime.getSetting?.('TWITTER_LISTS') || process.env.TWITTER_LISTS || ''
)
.split(',')
.filter(Boolean);
status += `📋 Monitoring ${lists.length} lists: ${lists.join(', ')}\n`;
status += `⏱️ Update interval: ${
runtime.getSetting?.('RSS_UPDATE_INTERVAL') || process.env.RSS_UPDATE_INTERVAL || '30'
} minutes\n`;
status += `🎯 Max tweets per update: ${
runtime.getSetting?.('MAX_TWEETS_PER_LIST') || process.env.MAX_TWEETS_PER_LIST || '50'
}\n`;

const responseContent: Content = {
text: status,
source: message.content.source,
actions: ['GET_RSS_STATUS'],
};

await callback(responseContent);
return responseContent;
} catch (error: any) {
Comment on lines +55 to +63
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Callback before return? Sweet. But what if it explodes?

await callback() failure will nuke the whole handler and never hit your catch.
Consider wrapping the callback itself in try/catch so one flaky downstream doesn’t torch the action.

🤖 Prompt for AI Agents
In src/actions/getRSSStatus.ts around lines 55 to 63, the await callback call is
not wrapped in a try/catch block, so if the callback throws an error, it will
bypass the outer catch and crash the handler. To fix this, wrap the await
callback(responseContent) call in its own try/catch block to safely handle any
errors from the callback without affecting the main flow, allowing the outer
catch to handle other errors as intended.

logger.error('Status check failed:', error);

const errorContent: Content = {
text: `❌ Status check failed: ${error.message}`,
source: message.content.source,
};

await callback(errorContent);
return errorContent;
}
},
};
57 changes: 57 additions & 0 deletions src/actions/updateRSS.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { Action, Content, HandlerCallback, IAgentRuntime, Memory, State } from '@elizaos/core';
import { TwitterRSSService } from '../services/twitterRSSService';
import { logger } from '@elizaos/core';

export const updateRSSAction: Action = {
name: 'UPDATE_RSS_FEED',
similes: ['REFRESH_RSS', 'UPDATE_FEED', 'FETCH_LISTS'],
description: 'Update RSS feed with latest tweets from monitored Twitter lists',

validate: async (
runtime: IAgentRuntime,
_message: Memory,
_state: State
): Promise<boolean> => {
const service = runtime.getService(TwitterRSSService.serviceType);
return service instanceof TwitterRSSService;
},
Comment on lines +10 to +17
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Your validation is playing it safe, but maybe too safe.

You're checking if the service exists and is the right type, but what if the service is there but not initialized? That's like checking if someone's home but not if they're awake, honey!

Consider a more thorough validation:

  validate: async (
    runtime: IAgentRuntime,
    _message: Memory,
    _state: State
  ): Promise<boolean> => {
    const service = runtime.getService(TwitterRSSService.serviceType);
-   return service instanceof TwitterRSSService;
+   if (!(service instanceof TwitterRSSService)) {
+     return false;
+   }
+   // Could add additional checks here like service initialization state
+   return true;
  },
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
validate: async (
runtime: IAgentRuntime,
_message: Memory,
_state: State
): Promise<boolean> => {
const service = runtime.getService(TwitterRSSService.serviceType);
return service instanceof TwitterRSSService;
},
validate: async (
runtime: IAgentRuntime,
_message: Memory,
_state: State
): Promise<boolean> => {
const service = runtime.getService(TwitterRSSService.serviceType);
if (!(service instanceof TwitterRSSService)) {
return false;
}
// Could add additional checks here like service initialization state
return true;
},
🤖 Prompt for AI Agents
In src/actions/updateRSS.ts around lines 10 to 17, the validation currently only
checks if the service exists and is an instance of TwitterRSSService, but does
not verify if the service is properly initialized. Enhance the validation by
adding a check to confirm that the service is fully initialized or ready before
returning true. This might involve checking an initialization flag or a
readiness method on the service instance to ensure it is operational.


handler: async (
runtime: IAgentRuntime,
message: Memory,
_state: State,
_options: any,
callback: HandlerCallback
) => {
try {
const service = runtime.getService(
TwitterRSSService.serviceType
) as TwitterRSSService;

if (!service) {
throw new Error('Twitter RSS service not available');
}

const result = await service.processAllLists();

const responseContent: Content = {
text: `RSS feed updated successfully!\n📊 Processed ${result.totalTweets} new tweets\n📁 RSS file: ${result.rssPath}\n🕒 Last updated: ${new Date().toLocaleString()}`,
source: message.content.source,
actions: ['UPDATE_RSS_FEED'],
};

await callback(responseContent);
return responseContent;
} catch (error: any) {
logger.error('RSS update failed:', error);

const errorContent: Content = {
text: `❌ RSS update failed: ${error.message}`,
source: message.content.source,
};

await callback(errorContent);
return errorContent;
}
Comment on lines +45 to +55
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Error handling with style, but missing some tea.

You're catching and logging errors like a pro, but you're not giving users much to work with. When things go sideways, people want to know why, not just that it happened.

Consider providing more context in error messages:

    } catch (error: any) {
-     logger.error('RSS update failed:', error);
+     logger.error('RSS update failed:', { 
+       error: error.message, 
+       stack: error.stack,
+       timestamp: new Date().toISOString()
+     });

      const errorContent: Content = {
-       text: `❌ RSS update failed: ${error.message}`,
+       text: `❌ RSS update failed: ${error.message}\n💡 Check logs for more details or try again in a few minutes`,
        source: message.content.source,
      };
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
} catch (error: any) {
logger.error('RSS update failed:', error);
const errorContent: Content = {
text: `❌ RSS update failed: ${error.message}`,
source: message.content.source,
};
await callback(errorContent);
return errorContent;
}
} catch (error: any) {
logger.error('RSS update failed:', {
error: error.message,
stack: error.stack,
timestamp: new Date().toISOString()
});
const errorContent: Content = {
text: `❌ RSS update failed: ${error.message}\n💡 Check logs for more details or try again in a few minutes`,
source: message.content.source,
};
await callback(errorContent);
return errorContent;
}
🤖 Prompt for AI Agents
In src/actions/updateRSS.ts between lines 45 and 55, the error handling logs the
error but provides limited context to users. Enhance the error message by
including additional details such as the error stack or relevant operation info
in the text property of errorContent. This will give users more insight into why
the RSS update failed, improving troubleshooting and user experience.

},
};
Loading
Loading