Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Typescript implementation #2

Merged
merged 2 commits into from
Jan 10, 2025
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
47 changes: 29 additions & 18 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,29 +3,40 @@
"version": "1.0.0",
"description": "An autonomous AI-driven agent that manages social media accounts",
"main": "dist/index.js",
"directories": {
"test": "tests"
},
"scripts": {
"build": "tsc",
"start": "node dist/index.js",
"dev": "ts-node src/index.ts",
"test": "echo \"Error: no test specified\" && exit 1"
"test": "jest",
"lint": "eslint src/**/*.ts",
"format": "prettier --write src/**/*.ts"
},
"dependencies": {
"axios": "^1.6.2",
"dotenv": "^16.3.1",
"ethers": "^6.9.0",
"twitter-api-v2": "^1.15.2",
"winston": "^3.11.0"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"@types/dotenv": "^6.1.1",
"@types/node": "^22.10.5",
"ts-node": "^10.9.2",
"typescript": "^5.7.3"
"@types/jest": "^29.5.11",
"@types/node": "^20.10.4",
"@typescript-eslint/eslint-plugin": "^6.13.2",
"@typescript-eslint/parser": "^6.13.2",
"eslint": "^8.55.0",
"jest": "^29.7.0",
"prettier": "^3.1.0",
"ts-jest": "^29.1.1",
"ts-node": "^10.9.1",
"typescript": "^5.3.3"
},
"dependencies": {
"axios": "^1.7.9",
"dotenv": "^16.4.7",
"ethers": "^6.13.5",
"twitter-api-v2": "^1.19.0",
"winston": "^3.17.0"
}
"keywords": [
"ai",
"twitter",
"bot",
"autonomous",
"cryptocurrency"
],
"author": "BTB Finance",
"license": "MIT"
}
57 changes: 57 additions & 0 deletions src/config/config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { config as dotenvConfig } from 'dotenv';
import { AgentConfig } from '../types';

dotenvConfig();

function validateConfig(config: Partial<AgentConfig>): config is AgentConfig {
const requiredFields = [
'twitter.apiKey',
'twitter.apiSecret',
'twitter.accessToken',
'twitter.accessTokenSecret',
'llm.apiKey',
'wallet.privateKey',
'wallet.rpcUrl'
];

for (const field of requiredFields) {
const value = field.split('.').reduce((obj, key) => obj?.[key], config as any);
if (!value) {
throw new Error(`Missing required configuration: ${field}`);
}
}

return true;
}

export function loadConfig(): AgentConfig {
const config: AgentConfig = {
twitter: {
apiKey: process.env.TWITTER_API_KEY!,
apiSecret: process.env.TWITTER_API_SECRET!,
accessToken: process.env.TWITTER_ACCESS_TOKEN!,
accessTokenSecret: process.env.TWITTER_ACCESS_TOKEN_SECRET!,
},
llm: {
provider: 'claude',
apiKey: process.env.CLAUDE_API_KEY!,
model: 'claude-2',
maxTokens: 1000,
},
wallet: {
network: process.env.ETH_NETWORK || 'mainnet',
rpcUrl: process.env.ETH_RPC_URL!,
privateKey: process.env.WALLET_PRIVATE_KEY!,
},
monitoring: {
logLevel: (process.env.LOG_LEVEL || 'info') as AgentConfig['monitoring']['logLevel'],
enableMetrics: process.env.ENABLE_METRICS === 'true',
},
};

if (!validateConfig(config)) {
throw new Error('Invalid configuration');
}

return config;
}
151 changes: 89 additions & 62 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,93 +1,120 @@
import dotenv from 'dotenv';
import { TwitterApi } from './services/TwitterApi';
import { LLMClient } from './services/LLMClient';
import { WalletManager } from './services/WalletManager';
import { BotState, Conversation } from './types';
import { AgentState, Conversation, Tweet } from './types';
import { loadConfig } from './config/config';
import { TwitterService } from './services/TwitterService';
import { LLMService } from './services/LLMService';
import { WalletService } from './services/WalletService';
import { logger } from './utils/logger';

// Load environment variables
dotenv.config();

class Bot {
private twitterApi: TwitterApi;
private llmClient: LLMClient;
private walletManager: WalletManager;
private state: BotState = {
lastCheckedMentionId: null,
conversations: new Map<string, Conversation>(),
class AutonomousAgent {
private config = loadConfig();
private twitter: TwitterService;
private llm: LLMService;
private wallet: WalletService;
private state: AgentState = {
conversations: new Map(),
userInteractions: new Map(),
};

constructor() {
// Initialize services
this.twitterApi = new TwitterApi(
process.env.TWITTER_API_KEY!,
process.env.TWITTER_API_SECRET!,
process.env.TWITTER_ACCESS_TOKEN!,
process.env.TWITTER_ACCESS_TOKEN_SECRET!
this.twitter = new TwitterService(
this.config.twitter.apiKey,
this.config.twitter.apiSecret,
this.config.twitter.accessToken,
this.config.twitter.accessTokenSecret
);

this.llmClient = new LLMClient(
process.env.CLAUDE_API_KEY!
this.llm = new LLMService(
this.config.llm.apiKey,
this.config.llm.model,
this.config.llm.maxTokens
);

this.walletManager = new WalletManager(
process.env.WALLET_PRIVATE_KEY!,
process.env.ETH_RPC_URL!
this.wallet = new WalletService(
this.config.wallet.privateKey,
this.config.wallet.rpcUrl
);
}

async processMentions(): Promise<void> {
private async processMention(tweet: Tweet): Promise<void> {
try {
const mentions = await this.twitterApi.getMentions(this.state.lastCheckedMentionId || undefined);
logger.info(`Processing mention: ${tweet.id}`);

for (const mention of mentions) {
console.log(`Processing mention: ${mention.id}`);
// Get or create conversation
let conversation = this.state.conversations.get(tweet.conversationId || tweet.id);
if (!conversation) {
conversation = {
id: tweet.conversationId || tweet.id,
tweets: [],
participants: [],
context: [],
lastInteraction: new Date(),
};
this.state.conversations.set(conversation.id, conversation);
}

// Generate reply using LLM
const reply = await this.llmClient.generateReply(
mention.text,
[`User: ${mention.authorId}`]
);
// Update conversation
conversation.tweets.push(tweet);
conversation.lastInteraction = new Date();

// Reply to tweet
await this.twitterApi.replyToTweet(reply, mention.id);
// Get user interaction data
const userInteraction = this.state.userInteractions.get(tweet.authorId) || {
userId: tweet.authorId,
lastInteraction: new Date(),
interactionCount: 0,
transactions: [],
};
userInteraction.interactionCount++;
userInteraction.lastInteraction = new Date();
this.state.userInteractions.set(tweet.authorId, userInteraction);

// Update state
this.state.conversations.set(mention.id, {
mentionId: mention.id,
authorId: mention.authorId,
text: mention.text,
timestamp: new Date(),
});
// Generate context for LLM
const context = conversation.tweets.map(t => t.text);

// Update user interaction
const userInteraction = this.state.userInteractions.get(mention.authorId) || {
userId: mention.authorId,
interactionCount: 0,
lastInteraction: new Date(),
};
userInteraction.interactionCount++;
userInteraction.lastInteraction = new Date();
this.state.userInteractions.set(mention.authorId, userInteraction);
// Generate reply
const reply = await this.llm.generateReply(
tweet.text,
context,
'You are a helpful AI assistant on Twitter.'
);

// Send reply
await this.twitter.replyToTweet(reply, tweet.id);
logger.info(`Replied to tweet: ${tweet.id}`);

// Update last checked mention ID
this.state.lastCheckedMentionId = mention.id;
}
} catch (error) {
console.error('Error processing mentions:', error);
logger.error('Error processing mention:', error);
throw error;
}
}

async start(): Promise<void> {
console.log('Starting Autonomous AI Twitter Agent');
logger.info('Starting Autonomous AI Agent');

while (true) {
await this.processMentions();
await new Promise(resolve => setTimeout(resolve, 60000)); // Wait for 60 seconds
try {
// Check mentions
const mentions = await this.twitter.getMentions(this.state.lastCheckedMentionId);

for (const mention of mentions) {
await this.processMention(mention);
this.state.lastCheckedMentionId = mention.id;
}

// Wait before next check
await new Promise(resolve => setTimeout(resolve, 60000)); // 1 minute delay
} catch (error) {
logger.error('Error in main loop:', error);
// Wait before retrying
await new Promise(resolve => setTimeout(resolve, 300000)); // 5 minutes delay on error
}
}
}
}

// Start the bot
const bot = new Bot();
bot.start().catch(console.error);
// Start the agent
const agent = new AutonomousAgent();
agent.start().catch(error => {
logger.error('Fatal error:', error);
process.exit(1);
});
39 changes: 0 additions & 39 deletions src/services/LLMClient.ts

This file was deleted.

Loading
Loading