💬 Auto-Reply
Auto-reply to tweets matching your filters. Configure keywords, users, and reply templates for automated engagement.
📋 What It Does
This script provides the following capabilities:
- Automated operation — Runs directly in your browser console on x.com
- Configurable settings — Customize behavior via the CONFIG object
- Real-time progress — Shows live status updates with emoji-coded logs
- Rate limiting — Built-in delays to respect X/Twitter's rate limits
- Data export — Results exported as JSON/CSV for further analysis
Use cases:
- Auto-reply to tweets matching your filters. Configure keywords, users, and reply templates for automated engagement.
- Automate repetitive engagement tasks on X/Twitter
- Save time with one-click automation — no API keys needed
- Works in any modern browser (Chrome, Firefox, Edge, Safari)
⚠️ Important Notes
Use responsibly! All automation should respect X/Twitter's Terms of Service. Use conservative settings and include breaks between sessions.
- This script runs in the browser DevTools console — not Node.js
- You must be logged in to x.com for the script to work
- Start with low limits and increase gradually
- Include random delays between actions to appear human
- Don't run multiple automation scripts simultaneously
🌐 Browser Console Usage
Steps:
- Go to
x.com/search or timeline - Open browser console (
F12→ Console tab) - Copy and paste the script from
src/autoReply.js - Press Enter to run
/**
* ============================================================
* 🤖 Auto-Reply / Auto-Respond — Production Grade
* ============================================================
*
* @name autoReply.js
* @description Automatically reply to tweets matching filters.
* Monitors timeline/search and posts contextual replies.
* @author nichxbt (https://x.com/nichxbt)
* @version 1.0.0
* @date 2026-02-24
* @repository https://github.com/nirholas/XActions
*
* ============================================================
* 📋 USAGE:
*
* 1. Go to: https://x.com/home (or a search page)
* 2. Configure triggers and reply templates
* 3. Set dryRun = false when ready
* 4. Paste and run
*
* ⚠️ AUTOMATED REPLIES CAN GET YOU RESTRICTED!
* - Use sparingly, with long delays
* - Vary reply text to avoid spam detection
* - Only reply to relevant content
* ============================================================
*/
(() => {
'use strict';
const CONFIG = {
// ── Triggers (at least one must match) ──────────────────
triggers: [
{
keywords: ['xactions', 'twitter automation'],
reply: 'Check out XActions — the complete X automation toolkit! 🚀 https://github.com/nirholas/XActions',
},
// Add more trigger/reply pairs here:
// { keywords: ['keyword'], reply: 'Your reply text' },
],
// ── Targeting ───────────────────────────────────────────
minLikes: 0, // Only reply to tweets with >= N likes
maxLikes: Infinity, // Skip viral tweets (too noisy)
ignoreVerified: false, // Skip verified accounts
ignoreReplies: true, // Don't reply to replies (only originals)
fromUsers: [], // Only reply to these users (empty = any)
ignoreUsers: [], // Never reply to these users
// ── Limits ──────────────────────────────────────────────
maxReplies: 10, // Max replies per session
scrollCycles: 20, // How far to scroll
minDelay: 30000, // 30s minimum between replies (CRITICAL for safety)
maxDelay: 60000, // 60s maximum between replies
// ── Safety ──────────────────────────────────────────────
dryRun: true, // START TRUE! Preview before acting.
rateLimitPauseMs: 120000, // 2min cooldown
// ── Variation ───────────────────────────────────────────
addRandomEmoji: true, // Append random emoji for variation
};
const sleep = ms => new Promise(r => setTimeout(r, ms + ms * 0.15 * (Math.random() - 0.5)));
const rand = (lo, hi) => sleep(lo + Math.random() * (hi - lo));
const emojis = ['🔥', '💯', '⚡', '🚀', '👏', '💡', '✨', '🙌', '💪', '🎯'];
const parseCount = (str) => {
if (!str) return 0;
str = str.replace(/,/g, '').trim();
const m = str.match(/([\d.]+)\s*([KMBkmb])?/);
if (!m) return 0;
let n = parseFloat(m[1]);
if (m[2]) n *= { k: 1e3, m: 1e6, b: 1e9 }[m[2].toLowerCase()] || 1;
return Math.round(n);
};
(async () => {
const W = 60;
console.log('╔' + '═'.repeat(W) + '╗');
console.log('║ 🤖 AUTO-REPLY' + ' '.repeat(W - 17) + '║');
console.log('║ ' + (CONFIG.dryRun ? '🔍 DRY RUN' : '⚡ LIVE MODE').padEnd(W - 2) + '║');
console.log('║ by nichxbt — v1.0' + ' '.repeat(W - 21) + '║');
console.log('╚' + '═'.repeat(W) + '╝');
console.log(`\n⚙️ ${CONFIG.triggers.length} trigger(s) | Max: ${CONFIG.maxReplies} replies | ` +
`Delay: ${CONFIG.minDelay/1000}-${CONFIG.maxDelay/1000}s\n`);
const repliedSet = new Set(
JSON.parse(localStorage.getItem('xactions_autoreplied') || '[]')
);
const ignoreSet = new Set(CONFIG.ignoreUsers.map(u => u.toLowerCase().replace('@', '')));
const fromSet = new Set(CONFIG.fromUsers.map(u => u.toLowerCase().replace('@', '')));
let replied = 0;
const t0 = Date.now();
const matchTrigger = (text) => {
const lower = text.toLowerCase();
for (const trigger of CONFIG.triggers) {
if (trigger.keywords.some(kw => lower.includes(kw.toLowerCase()))) {
return trigger;
}
}
return null;
};
for (let scroll = 0; scroll < CONFIG.scrollCycles && replied < CONFIG.maxReplies; scroll++) {
// Rate-limit check
for (const sel of ['[data-testid="toast"]', '[role="alert"]']) {
const el = document.querySelector(sel);
if (el && /rate.limit|try.again|too.many|slow.down/i.test(el.textContent)) {
console.warn(`⏳ Rate-limited — waiting ${CONFIG.rateLimitPauseMs/1000}s...`);
await sleep(CONFIG.rateLimitPauseMs);
}
}
const articles = document.querySelectorAll('article[data-testid="tweet"]');
for (const article of articles) {
if (replied >= CONFIG.maxReplies) break;
// Get tweet ID
const link = article.querySelector('a[href*="/status/"]');
const tweetUrl = link?.getAttribute('href') || '';
const tweetId = tweetUrl.split('/status/')[1]?.split(/[?/]/)[0];
if (!tweetId || repliedSet.has(tweetId)) continue;
// Get tweet data
const text = article.querySelector('[data-testid="tweetText"]')?.textContent || '';
const authorLink = article.querySelector('[data-testid="User-Name"] a[href^="/"]');
const author = authorLink?.getAttribute('href')?.replace('/', '').split('/')[0] || '';
// Check if it's a reply
const isReply = !!article.querySelector('[data-testid="socialContext"]')?.textContent?.match(/replying/i);
if (CONFIG.ignoreReplies && isReply) continue;
// Author checks
if (ignoreSet.has(author.toLowerCase())) continue;
if (fromSet.size > 0 && !fromSet.has(author.toLowerCase())) continue;
// Verified check
if (CONFIG.ignoreVerified && article.querySelector('[data-testid="icon-verified"]')) continue;
// Engagement checks
const likeBtn = article.querySelector('[data-testid="like"], [data-testid="unlike"]');
const likes = parseCount(likeBtn?.getAttribute('aria-label')?.match(/([\d,.]+[KMBkmb]?)/)?.[1] || '0');
if (likes < CONFIG.minLikes || likes > CONFIG.maxLikes) continue;
// Match trigger
const trigger = matchTrigger(text);
if (!trigger) continue;
let replyText = trigger.reply;
if (CONFIG.addRandomEmoji) {
replyText += ' ' + emojis[Math.floor(Math.random() * emojis.length)];
}
repliedSet.add(tweetId);
if (CONFIG.dryRun) {
console.log(`🔍 [DRY] Would reply to @${author}: "${text.slice(0, 50)}..."`);
console.log(` Reply: "${replyText}"`);
replied++;
continue;
}
try {
// Click reply button
const replyBtn = article.querySelector('[data-testid="reply"]');
if (!replyBtn) continue;
replyBtn.scrollIntoView({ block: 'center', behavior: 'smooth' });
await sleep(300);
replyBtn.click();
await sleep(1500);
// Find reply textbox
const textbox = document.querySelector('[data-testid="tweetTextarea_0"]') ||
document.querySelector('div[contenteditable="true"][role="textbox"]');
if (!textbox) {
console.warn('⚠️ Reply textbox not found');
document.body.click();
await sleep(500);
continue;
}
textbox.focus();
await sleep(300);
// Type reply
for (const char of replyText) {
document.execCommand('insertText', false, char);
await sleep(20 + Math.random() * 40);
}
await sleep(500);
// Click send
const sendBtn = document.querySelector('[data-testid="tweetButtonInline"]') ||
document.querySelector('[data-testid="tweetButton"]');
if (sendBtn) {
sendBtn.click();
replied++;
console.log(`💬 #${replied} Replied to @${author}: "${replyText.slice(0, 40)}..."`);
// Save to localStorage
localStorage.setItem('xactions_autoreplied', JSON.stringify([...repliedSet]));
// Long delay between replies (critical)
await rand(CONFIG.minDelay, CONFIG.maxDelay);
} else {
console.warn('⚠️ Send button not found');
document.body.click();
await sleep(1000);
}
} catch (e) {
console.warn(`⚠️ Error replying: ${e.message}`);
document.body.click();
await sleep(2000);
}
}
window.scrollTo({ top: document.body.scrollHeight, behavior: 'smooth' });
await sleep(2000);
}
// Results
const elapsed = ((Date.now() - t0) / 1000).toFixed(0);
console.log('\n╔' + '═'.repeat(W) + '╗');
console.log('║ ✅ AUTO-REPLY COMPLETE' + ' '.repeat(W - 25) + '║');
console.log('╚' + '═'.repeat(W) + '╝');
console.log(` 💬 Replied: ${replied}`);
console.log(` ⏱️ Time: ${elapsed}s`);
console.log(` 📝 Total tracked: ${repliedSet.size}`);
if (CONFIG.dryRun && replied > 0) {
console.log(`\n ⚡ Set dryRun = false to actually send ${replied} replies.\n`);
}
})();
})();
⚙️ Configuration
| Setting | Default | Description |
|---|---|---|
triggers |
[ |
Triggers |
keywords |
['xactions', 'twitter automation'] |
Keywords |
reply |
'Check out XActions — the complete X automation toolkit! 🚀 https: |
github.com/nirholas/XActions' |
📖 Step-by-Step Tutorial
Step 1: Navigate to the right page
Open your browser and go to x.com/search or timeline. Make sure you're logged in to your X/Twitter account.
Step 2: Open the browser console
- Chrome/Edge: Press
F12orCtrl+Shift+J(Mac:Cmd+Option+J) - Firefox: Press
F12orCtrl+Shift+K - Safari: Enable Developer menu in Preferences → Advanced, then press
Cmd+Option+C
Step 3: Paste the script
Copy the entire script from src/autoReply.js and paste it into the console.
Step 4: Customize the CONFIG (optional)
Before running, you can modify the CONFIG object at the top of the script to adjust behavior:
const CONFIG = {
// Edit these values before running
// See Configuration table above for all options
};
Step 5: Run and monitor
Press Enter to run the script. Watch the console for real-time progress logs:
- ✅ Green messages = success
- 🔄 Blue messages = in progress
- ⚠️ Yellow messages = warnings
- ❌ Red messages = errors
Step 6: Export results
Most scripts automatically download results as JSON/CSV when complete. Check your Downloads folder.
🖥️ CLI Usage
You can also run this via the XActions CLI:
# Install XActions globally
npm install -g xactions
# Run via CLI
xactions --help
🤖 MCP Server Usage
Use with AI agents (Claude, Cursor, etc.) via the MCP server:
# Start MCP server
npm run mcp
See the MCP Setup Guide for integration with Claude Desktop, Cursor, and other AI tools.
📁 Source Files
| File | Description |
|---|---|
src/autoReply.js |
Main script |
🔗 Related Scripts
| Script | Description |
|---|---|
| Auto-Plug Replies | Automatically reply to viral tweets with your own content plug or CTA |
| Engagement Booster | Systematically engage with target accounts by liking, bookmarking, and retweeting their content to build relationships |
| Engagement Manager | All-in-one engagement toolkit: like, unlike, reply, bookmark, and manage interactions from a single interface |
| Quote Tweet Automation | Auto-retweet with quote-tweet templates |
| Welcome New Followers | Auto-send welcome DMs to new followers |
Author: nich (@nichxbt) — XActions on GitHub
⚡ Ready to try Auto-Reply?
XActions is 100% free and open-source. No API keys, no fees, no signup.
Browse All Scripts