UNPKG

@defikitdotnet/x-ai-combat

Version:

XCombatAI - Social Media Engagement Template for the Agent Framework

282 lines 11.6 kB
import { SearchMode } from "@defikitdotnet/agent-twitter-client"; import { PostResource } from "../models/Post"; import { XAIAdapter } from "../db/XAIAdapter"; import { TwitterService } from "../services/twitter-service"; import { v4 as uuidv4 } from "uuid"; import { RedisService } from "../services/redis-service"; import { log, warn, error } from '../utils/logger'; /** * InteractionScanner plugin * * Scans Twitter for posts mentioning the agent and identifies user interactions * Runs a scheduled job every 30 minutes to avoid excessive scraping */ export class InteractionScannerPlugin { constructor(runtime, twitterConfig) { this.name = "interaction-scanner"; this.version = "1.0.0"; this.description = "Scans Twitter for posts mentioning the agent and tracks user interactions"; this.scanInterval = null; this.scanIntervalMinutes = 30; // Default scan interval this.isScanning = false; this.lastScanTime = null; this.adapter = new XAIAdapter(runtime.databaseAdapter); this.runtime = runtime; this.twitterConfig = twitterConfig; this.agentId = runtime.agentId; this.twitterService = new TwitterService(runtime, twitterConfig); this.redisService = new RedisService(this.runtime.agentId); } /** * Initialize the plugin */ async initialize() { log(`Initializing InteractionScanner Plugin for agent ${this.agentId}...`); try { // Initialize Twitter service await this.twitterService.client.init(); log("Successfully connected to Twitter service"); // Start the scanner this.startScanner(); } catch (err) { error(`Failed to initialize InteractionScanner plugin for agent ${this.agentId}:`, err); } } /** * Start the scanner job */ startScanner() { if (this.scanInterval) { clearInterval(this.scanInterval); } // Run the scanner immediately on startup this.scanForInteractions().catch(err => error(`Error in initial interaction scan for agent ${this.agentId}:`, err)); // Schedule recurring scans this.scanInterval = setInterval(() => { if (!this.isScanning) { this.scanForInteractions().catch(err => error(`Error in scheduled interaction scan for agent ${this.agentId}:`, err)); } }, this.scanIntervalMinutes * 60 * 1000); log(`InteractionScanner for agent ${this.agentId} scheduled to run every ${this.scanIntervalMinutes} minutes`); } /** * Stop the scanner job */ stopScanner() { if (this.scanInterval) { clearInterval(this.scanInterval); this.scanInterval = null; log(`InteractionScanner for agent ${this.agentId} stopped`); } } /** * Set the scan interval */ setScanInterval(minutes) { if (minutes < 5) { warn("Scan interval too short, setting to minimum of 5 minutes"); minutes = 5; } this.scanIntervalMinutes = minutes; log(`Scan interval updated to ${minutes} minutes for agent ${this.agentId}`); // Restart scanner with new interval this.startScanner(); } /** * Run a scan for interactions */ async scanForInteractions() { if (this.isScanning) { log(`Scan already in progress for agent ${this.agentId}, skipping`); return; } this.isScanning = true; this.lastScanTime = new Date(); try { log(`Starting interaction scan for agent ${this.agentId}...`); // Search for tweets mentioning the agent const query = `@${process.env.TWITTER_USERNAME}` || '@XCombatAI'; const tweetsGenerator = await this.twitterService.searchTweets(query, 100, SearchMode.Latest); let tweetCount = 0; for await (const tweet of tweetsGenerator) { tweetCount++; await this.processTweet(tweet); } log(`Found ${tweetCount} tweets mentioning the agent ${this.agentId}`); log(`Interaction scan for agent ${this.agentId} completed`); } catch (err) { error(`Error during interaction scan for agent ${this.agentId}:`, err); // Attempt to send notification via Telegram try { const message = `⚠️ Error during InteractionScanner job for agent ${this.agentId}: ${err.message}`; await this.sendTelegramAlert(message); } catch (notifyError) { error("Failed to send Telegram notification:", notifyError); } } finally { this.isScanning = false; } } /** * Process a single tweet */ async processTweet(tweet) { try { // Store tweet if it's not already in our database let post = await this.adapter.findPostByTweetId(tweet.id); if (!post) { const newPost = new PostResource(); newPost.id = uuidv4(); newPost.tweetId = tweet.id; newPost.twitterUserId = tweet.userId || ''; newPost.content = ''; newPost.agentId = this.agentId; // Set agentId for the new post // Safely access potentially undefined properties if (typeof tweet.text === 'string') { newPost.content = tweet.text; } else if (typeof tweet.content === 'string') { newPost.content = tweet.content; } // Store the full tweet object for future reference newPost.raw = JSON.stringify(tweet); // Use current date if tweet doesn't have a creation date newPost.createdAt = new Date(); if (tweet.createdAt) { try { newPost.createdAt = new Date(tweet.createdAt); } catch (e) { warn(`Invalid tweet creation date: ${tweet.createdAt}, using current date`); } } // Initialize default stats newPost.stats = { likes: 0, retweets: 0, quotes: 0, replies: 0, total: 0, famePoints: 0 }; // Set lastInteractionAt to now newPost.lastInteractionAt = new Date(); // Check if this user has linked their account with this agent const linkedUser = await this.adapter.findUserByTwitterId(tweet.userId || '', this.agentId); if (linkedUser) { // If user is linked, store the internal user ID newPost.userId = linkedUser.id; } await this.adapter.createPost(newPost); post = newPost; } // Process interactions as stats await this.processInteractionStats(tweet, post); } catch (err) { error(`Error processing tweet ${tweet.id} for agent ${this.agentId}:`, err); } } /** * Process interactions as statistics directly in post */ async processInteractionStats(tweet, post) { try { // Get current tweet stats const tweetStats = { likes: typeof tweet.likes === 'number' ? tweet.likes : 0, retweets: typeof tweet.retweets === 'number' ? tweet.retweets : 0, quotes: typeof tweet.quotes === 'number' ? tweet.quotes : 0, replies: typeof tweet.replies === 'number' ? tweet.replies : 0, total: 0, famePoints: 0 }; // Calculate total interactions tweetStats.total = tweetStats.likes + tweetStats.retweets + tweetStats.quotes + tweetStats.replies; // Calculate fame points tweetStats.famePoints = (tweetStats.likes * 0.5) + (tweetStats.retweets * 2) + (tweetStats.quotes * 2) + (tweetStats.replies * 1); // Update post with new stats post.stats = tweetStats; post.lastInteractionAt = new Date(); post.updatedAt = new Date(); // Ensure the post has an agentId if (!post.agentId) { post.agentId = this.agentId; } // Save the updated post await this.adapter.createPost(post); // Log the update const linkedUser = await this.adapter.findUserByTwitterId(tweet.userId || '', this.agentId); if (linkedUser) { log(`Updated interaction stats for linked user ${linkedUser.username} (${linkedUser.id}) on post ${post.id} for agent ${this.agentId}`); } else { log(`Updated interaction stats for unlinked Twitter user ${tweet.userId} on post ${post.id} for agent ${this.agentId}`); } log(`Stats for agent ${this.agentId}: likes=${tweetStats.likes}, retweets=${tweetStats.retweets}, quotes=${tweetStats.quotes}, replies=${tweetStats.replies}`); log(`Fame points for agent ${this.agentId}: ${tweetStats.famePoints.toFixed(1)}`); } catch (err) { error(`Error processing interaction stats for tweet ${tweet.id} for agent ${this.agentId}:`, err); } } /** * Send a Telegram alert */ async sendTelegramAlert(message) { const telegramBotToken = process.env.TELEGRAM_BOT_TOKEN; const telegramChatId = process.env.TELEGRAM_CHAT_ID; if (!telegramBotToken || !telegramChatId) { warn(`Telegram notifications not configured for agent ${this.agentId}, skipping alert`); return; } try { const telegramUrl = `https://api.telegram.org/bot${telegramBotToken}/sendMessage`; const response = await fetch(telegramUrl, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ chat_id: telegramChatId, text: message, parse_mode: 'HTML', }), }); if (!response.ok) { throw new Error(`Telegram API response: ${response.status} ${response.statusText}`); } log(`Telegram notification sent successfully for agent ${this.agentId}`); } catch (err) { error(`Error sending Telegram notification for agent ${this.agentId}:`, err); throw err; } } /** * Get the last scan time */ getLastScanTime() { return this.lastScanTime; } /** * Run a manual scan (admin feature) */ async runManualScan() { if (this.isScanning) { log(`A scan is already in progress for agent ${this.agentId}`); return; } log(`Starting manual interaction scan for agent ${this.agentId}...`); await this.scanForInteractions(); } } //# sourceMappingURL=interaction-scanner-plugin.js.map