⚡ XActions
📖 Guides & Reference

♻️ Evergreen Content Recycler

Identify top-performing tweets and automatically re-post them on a schedule with content variation. Competes with Hypefury and Publer.


Overview

The Evergreen Recycler finds your best content and recycles it:

  • Candidate analysis — ranks tweets by engagement rate, filters time-sensitive content
  • Queue management — scheduled re-posting with configurable frequency and time slots
  • Content variation — synonym swaps, emoji rotation, and punctuation changes to avoid duplicate detection
  • Pause/resume — control the recycler without losing queue state
  • Stats — track posted vs pending items

Available via: CLI, MCP tools, and Node.js library.


Quick Start

CLI

# Analyze evergreen candidates (preview only)
unfollowx evergreen nichxbt --analyze

# Find and queue tweets with 50+ likes, 30+ days old
unfollowx evergreen nichxbt --min-likes 50 --min-age 30

# Run one recycling cycle (posts due items)
unfollowx evergreen nichxbt

MCP (AI Agents)

Tool: x_evergreen_analyze
Args: { "username": "nichxbt", "minLikes": 10 }

Node.js

import {
  analyzeEvergreenCandidates,
  createEvergreenQueue,
  runEvergreenCycle,
  pauseEvergreen,
  resumeEvergreen,
  getEvergreenStats,
  varyTweet
} from 'xactions/src/automation/evergreenRecycler.js';

// Find candidates
const { candidates } = await analyzeEvergreenCandidates('nichxbt', {
  minAge: 30,
  minLikes: 10,
  minEngagementRate: 0.02,
  limit: 50,
  excludeReplies: true,
  excludeRetweets: true
});

// Create a posting queue
await createEvergreenQueue('nichxbt', candidates, {
  frequency: 'daily',           // 'hourly' | 'daily' | 'weekly'
  timeSlots: ['09:00', '14:00', '19:00'],
  maxPerDay: 2,
  variation: true               // apply content variation
});

// Run one cycle (posts due items)
const result = await runEvergreenCycle();
// { status: 'posted', count: 2 }

// Manage the queue
await pauseEvergreen();
await resumeEvergreen();
const stats = await getEvergreenStats();

// Test content variation
const { varied, changeDescription } = varyTweet('This is amazing! 🔥 Check it out');
// { varied: "This is incredible! 🌟 Check it out", changeDescription: "Swapped 1 synonym, 1 emoji" }

How It Works

1. Candidate Selection

Tweets are scored and filtered:

Filter Default Description
minAge 30 days Only recycle old tweets
minLikes 10 Minimum like count
minEngagementRate 0.02 (likes + retweets + replies) / impressions
excludeReplies true Skip reply tweets
excludeRetweets true Skip retweets

Time-sensitive tweets (containing words like "today", "yesterday", "breaking", "just happened") are automatically excluded.

2. Content Variation

To avoid X's duplicate-detection:

Technique Example
Synonym swap "amazing" → "incredible"
Emoji rotation 🔥 → 🌟 or ✨
Punctuation change "!" → " !" or "!!"
Line break variation Added/removed line breaks

3. Queue & Scheduling

Queue saved to ~/.xactions/evergreen-queue.json:

{
  "username": "nichxbt",
  "queue": [
    {
      "tweetId": "1234567890",
      "originalText": "...",
      "scheduledAt": "2026-02-25T14:00:00Z",
      "posted": false
    }
  ],
  "settings": {
    "frequency": "daily",
    "timeSlots": ["09:00", "14:00", "19:00"],
    "maxPerDay": 2
  }
}

Function Reference

analyzeEvergreenCandidates(username, options)

Scrapes recent tweets and ranks by engagement rate.

Returns: { username, candidates: [{ id, text, likes, retweets, replies, engagementRate, age }], total }

createEvergreenQueue(username, tweets, options)

Creates a posting schedule from candidate tweets.

Option Type Default Description
frequency string 'daily' 'hourly', 'daily', 'weekly'
timeSlots string[] ['09:00','14:00','19:00'] Preferred posting times
maxPerDay number 2 Maximum posts per day
variation boolean true Apply content variation

runEvergreenCycle()

Posts all due items in the queue. Returns { status, count } or { status: 'idle', dueCount: 0 }.

pauseEvergreen() / resumeEvergreen()

Toggles the recycler on/off without clearing the queue.

getEvergreenStats()

Returns: { username, totalQueued, posted, pending, paused, lastPosted, createdAt }

varyTweet(text)

Applies random content variation.

Returns: { original, varied, changeDescription }


MCP Tools

Tool Input Description
x_evergreen_analyze username, minLikes? Find recyclable tweets

⚡ Free and open source

No API keys, no monthly fees, no signup. Star the repo if it saved you a subscription.

View on GitHub