@defikitdotnet/x-ai-combat
Version:
XCombatAI - Social Media Engagement Template for the Agent Framework
286 lines • 12.2 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.InteractionScannerPlugin = void 0;
const agent_twitter_client_1 = require("@defikitdotnet/agent-twitter-client");
const Post_1 = require("../models/Post");
const XAIAdapter_1 = require("../db/XAIAdapter");
const twitter_service_1 = require("../services/twitter-service");
const uuid_1 = require("uuid");
const redis_service_1 = require("../services/redis-service");
const logger_1 = require("../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
*/
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_1.XAIAdapter(runtime.databaseAdapter);
this.runtime = runtime;
this.twitterConfig = twitterConfig;
this.agentId = runtime.agentId;
this.twitterService = new twitter_service_1.TwitterService(runtime, twitterConfig);
this.redisService = new redis_service_1.RedisService(this.runtime.agentId);
}
/**
* Initialize the plugin
*/
async initialize() {
(0, logger_1.log)(`Initializing InteractionScanner Plugin for agent ${this.agentId}...`);
try {
// Initialize Twitter service
await this.twitterService.client.init();
(0, logger_1.log)("Successfully connected to Twitter service");
// Start the scanner
this.startScanner();
}
catch (err) {
(0, logger_1.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 => (0, logger_1.error)(`Error in initial interaction scan for agent ${this.agentId}:`, err));
// Schedule recurring scans
this.scanInterval = setInterval(() => {
if (!this.isScanning) {
this.scanForInteractions().catch(err => (0, logger_1.error)(`Error in scheduled interaction scan for agent ${this.agentId}:`, err));
}
}, this.scanIntervalMinutes * 60 * 1000);
(0, logger_1.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;
(0, logger_1.log)(`InteractionScanner for agent ${this.agentId} stopped`);
}
}
/**
* Set the scan interval
*/
setScanInterval(minutes) {
if (minutes < 5) {
(0, logger_1.warn)("Scan interval too short, setting to minimum of 5 minutes");
minutes = 5;
}
this.scanIntervalMinutes = minutes;
(0, logger_1.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) {
(0, logger_1.log)(`Scan already in progress for agent ${this.agentId}, skipping`);
return;
}
this.isScanning = true;
this.lastScanTime = new Date();
try {
(0, logger_1.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, agent_twitter_client_1.SearchMode.Latest);
let tweetCount = 0;
for await (const tweet of tweetsGenerator) {
tweetCount++;
await this.processTweet(tweet);
}
(0, logger_1.log)(`Found ${tweetCount} tweets mentioning the agent ${this.agentId}`);
(0, logger_1.log)(`Interaction scan for agent ${this.agentId} completed`);
}
catch (err) {
(0, logger_1.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) {
(0, logger_1.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 Post_1.PostResource();
newPost.id = (0, uuid_1.v4)();
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) {
(0, logger_1.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) {
(0, logger_1.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) {
(0, logger_1.log)(`Updated interaction stats for linked user ${linkedUser.username} (${linkedUser.id}) on post ${post.id} for agent ${this.agentId}`);
}
else {
(0, logger_1.log)(`Updated interaction stats for unlinked Twitter user ${tweet.userId} on post ${post.id} for agent ${this.agentId}`);
}
(0, logger_1.log)(`Stats for agent ${this.agentId}: likes=${tweetStats.likes}, retweets=${tweetStats.retweets}, quotes=${tweetStats.quotes}, replies=${tweetStats.replies}`);
(0, logger_1.log)(`Fame points for agent ${this.agentId}: ${tweetStats.famePoints.toFixed(1)}`);
}
catch (err) {
(0, logger_1.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) {
(0, logger_1.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}`);
}
(0, logger_1.log)(`Telegram notification sent successfully for agent ${this.agentId}`);
}
catch (err) {
(0, logger_1.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) {
(0, logger_1.log)(`A scan is already in progress for agent ${this.agentId}`);
return;
}
(0, logger_1.log)(`Starting manual interaction scan for agent ${this.agentId}...`);
await this.scanForInteractions();
}
}
exports.InteractionScannerPlugin = InteractionScannerPlugin;
//# sourceMappingURL=interaction-scanner-plugin.js.map