🤝 Customer Service

Automation src/automation
511 lines by @nichxbt

XActions Automation - Customer Service Bot

How to Use

  1. Open X home page
  2. Paste core.js, then paste this script
  3. Configure your accounts and response templates below
  4. Run and let it handle customer inquiries!

Note: This script requires pasting src/automation/core.js first for shared utilities.

Configuration Options

OptionDefaultDescription
mentionstrueMonitor @mentions
dmstrueMonitor DMs (requires manual navigation)
repliestrueMonitor replies to your posts
keywordstrueMonitor keywords related to your business
autoReplytrueAutomatically reply to mentions
autoReplyDMfalseAuto-reply to DMs (use with caution!)
requireApprovaltrueShow response before sending (recommended)
markAsReadtrueMark notifications as read after responding
start99 AM
end175 PM
RESPONSE_DELAY_SECONDS5Wait before responding (looks more natural)

Default Configuration

const CONFIG = {
    // Which account to use for customer service (must be in ACCOUNTS list)
    ACTIVE_ACCOUNT: 'business_account',
    
    // -------- MONITORING --------
    MONITOR: {
      mentions: true,           // Monitor @mentions
      dms: true,                // Monitor DMs (requires manual navigation)
      replies: true,            // Monitor replies to your posts
      keywords: true,           // Monitor keywords related to your business
    },
    
    // Keywords to monitor (for proactive customer service)
    BRAND_KEYWORDS: [
      // 'your_brand_name',
      // 'your_product_name',
    ],
    
    // -------- RESPONSE SETTINGS --------
    RESPONSE: {
      autoReply: true,          // Automatically reply to mentions
      autoReplyDM: false,       // Auto-reply to DMs (use with caution!)
      requireApproval: true,    // Show response before sending (recommended)
      markAsRead: true,         // Mark notifications as read after responding
    },
    
    // -------- BUSINESS HOURS --------
    BUSINESS_HOURS: {
      enabled: true,
      timezone: 'America/New_York',
      start: 9,                 // 9 AM
      end: 17,                  // 5 PM
      days: [1, 2, 3, 4, 5],    // Monday-Friday (0=Sunday)
      outsideHoursMessage: "Thanks for reaching out! We're currently outside business hours (Mon-Fri 9AM-5PM ET). We'll respond first thing next business day! 🙏",
    },
    
    // -------- TIMING --------
    CHECK_INTERVAL_SECONDS: 60,
    RESPONSE_DELAY_SECONDS: 5,  // Wait before responding (looks more natural)
    
    // -------- LIMITS --------
    MAX_RESPONSES_PER_HOUR: 20,
    MAX_RESPONSES_PER_DAY: 100,
  };

Full Script

Copy and paste this entire script into your browser DevTools console on x.com.

// XActions Automation - Customer Service Bot
// https://github.com/nirholas/XActions
//
// REQUIRES: Paste core.js first!
//
// Automate customer service responses on X for business accounts
// Monitor mentions, DMs, and provide templated responses
//
// HOW TO USE:
// 1. Open X home page
// 2. Paste core.js, then paste this script
// 3. Configure your accounts and response templates below
// 4. Run and let it handle customer inquiries!

(() => {
  if (!window.XActions?.Core) {
    console.error('❌ Core module not loaded! Paste core.js first.');
    return;
  }

  const { log, sleep, randomDelay, scrollBy, clickElement, waitForElement, storage, SELECTORS } = window.XActions.Core;

  // ============================================
  // ACCOUNT CONFIGURATION
  // ============================================
  // Format: 'username:password' or just 'username' if already logged in
  // You can paste these from a txt file
  const ACCOUNTS = `
personal_account
business_account
  `.trim().split('\n').map(line => {
    const [username, password] = line.trim().split(':');
    return { username: username?.toLowerCase(), password };
  }).filter(a => a.username);

  // ============================================
  // CUSTOMER SERVICE CONFIGURATION
  // ============================================
  const CONFIG = {
    // Which account to use for customer service (must be in ACCOUNTS list)
    ACTIVE_ACCOUNT: 'business_account',
    
    // -------- MONITORING --------
    MONITOR: {
      mentions: true,           // Monitor @mentions
      dms: true,                // Monitor DMs (requires manual navigation)
      replies: true,            // Monitor replies to your posts
      keywords: true,           // Monitor keywords related to your business
    },
    
    // Keywords to monitor (for proactive customer service)
    BRAND_KEYWORDS: [
      // 'your_brand_name',
      // 'your_product_name',
    ],
    
    // -------- RESPONSE SETTINGS --------
    RESPONSE: {
      autoReply: true,          // Automatically reply to mentions
      autoReplyDM: false,       // Auto-reply to DMs (use with caution!)
      requireApproval: true,    // Show response before sending (recommended)
      markAsRead: true,         // Mark notifications as read after responding
    },
    
    // -------- BUSINESS HOURS --------
    BUSINESS_HOURS: {
      enabled: true,
      timezone: 'America/New_York',
      start: 9,                 // 9 AM
      end: 17,                  // 5 PM
      days: [1, 2, 3, 4, 5],    // Monday-Friday (0=Sunday)
      outsideHoursMessage: "Thanks for reaching out! We're currently outside business hours (Mon-Fri 9AM-5PM ET). We'll respond first thing next business day! 🙏",
    },
    
    // -------- TIMING --------
    CHECK_INTERVAL_SECONDS: 60,
    RESPONSE_DELAY_SECONDS: 5,  // Wait before responding (looks more natural)
    
    // -------- LIMITS --------
    MAX_RESPONSES_PER_HOUR: 20,
    MAX_RESPONSES_PER_DAY: 100,
  };

  // ============================================
  // RESPONSE TEMPLATES
  // ============================================
  // Use {customer} placeholder for their username
  // Use {original} placeholder to reference their message
  const TEMPLATES = {
    // Default greeting
    greeting: [
      "Hi {customer}! Thanks for reaching out. How can I help you today? 😊",
      "Hey {customer}! We're here to help. What can we assist you with?",
      "Hello {customer}! Thanks for your message. What can we do for you?",
    ],
    
    // When customer mentions a problem/issue
    issue: [
      "Hi {customer}, I'm sorry to hear you're experiencing issues. Could you please DM us more details so we can help resolve this?",
      "Hey {customer}, we want to make this right! Please send us a DM with your order details and we'll look into it right away.",
      "Hi {customer}, thanks for letting us know. Can you share more details via DM so we can investigate?",
    ],
    
    // Positive feedback
    thanks: [
      "Thank you so much {customer}! We really appreciate your kind words! 🙏❤️",
      "Wow, thanks {customer}! Comments like yours make our day! 🌟",
      "Thanks for the love {customer}! We're so glad you're happy! 😊",
    ],
    
    // Questions about products/services
    question: [
      "Great question {customer}! Let me help you with that. [CUSTOMIZE RESPONSE]",
      "Hi {customer}! I'd be happy to help answer that. [CUSTOMIZE RESPONSE]",
    ],
    
    // Pricing inquiries
    pricing: [
      "Hi {customer}! For pricing info, please check our website or DM us for a personalized quote! 💰",
      "Hey {customer}! We'd love to discuss pricing with you. Send us a DM and we'll get you sorted!",
    ],
    
    // General support
    support: [
      "Hi {customer}! For the fastest support, please DM us or email support@yourcompany.com 📧",
      "Hey {customer}! We're here to help. Please DM us your details and we'll assist you right away!",
    ],
    
    // Out of stock / availability
    availability: [
      "Hi {customer}! Thanks for your interest. Let us check availability and get back to you via DM!",
      "Hey {customer}! We'll check on that for you. Expect a DM from us shortly!",
    ],
    
    // Escalation to DM
    escalate: [
      "Hi {customer}! To better assist you, please send us a DM with your details. We'll take care of you! 🙌",
      "Hey {customer}! Let's continue this in DMs so we can help you properly. Send us a message!",
    ],
  };

  // ============================================
  // KEYWORD DETECTION FOR AUTO-CATEGORIZATION
  // ============================================
  const KEYWORD_CATEGORIES = {
    issue: ['problem', 'issue', 'broken', 'doesn\'t work', 'not working', 'error', 'bug', 'complaint', 'disappointed', 'frustrated', 'terrible', 'awful', 'worst', 'refund', 'cancel'],
    thanks: ['thank', 'thanks', 'awesome', 'amazing', 'love', 'great', 'excellent', 'perfect', 'best', 'fantastic', 'wonderful', 'appreciate'],
    pricing: ['price', 'cost', 'how much', 'pricing', 'discount', 'coupon', 'deal', 'sale', 'expensive', 'cheap', 'afford'],
    availability: ['available', 'in stock', 'restock', 'when', 'back in stock', 'sold out', 'inventory'],
    question: ['?', 'how do', 'how can', 'what is', 'where', 'when', 'why', 'can you', 'could you', 'does it', 'is it'],
  };

  // ============================================
  // STATE
  // ============================================
  const state = {
    isRunning: true,
    responsesToday: 0,
    responsesThisHour: 0,
    hourReset: Date.now(),
    dayReset: Date.now(),
    processedIds: new Set(storage.get('cs_processed') || []),
    interactions: [],
  };

  const saveState = () => {
    storage.set('cs_processed', Array.from(state.processedIds));
    storage.set('cs_interactions', state.interactions.slice(-100)); // Keep last 100
  };

  // ============================================
  // BUSINESS HOURS CHECK
  // ============================================
  const isBusinessHours = () => {
    if (!CONFIG.BUSINESS_HOURS.enabled) return true;
    
    const now = new Date();
    const day = now.getDay();
    const hour = now.getHours();
    
    if (!CONFIG.BUSINESS_HOURS.days.includes(day)) return false;
    if (hour < CONFIG.BUSINESS_HOURS.start || hour >= CONFIG.BUSINESS_HOURS.end) return false;
    
    return true;
  };

  // ============================================
  // RATE LIMIT CHECK
  // ============================================
  const canRespond = () => {
    const now = Date.now();
    
    // Reset hourly counter
    if (now - state.hourReset > 60 * 60 * 1000) {
      state.responsesThisHour = 0;
      state.hourReset = now;
    }
    
    // Reset daily counter
    if (now - state.dayReset > 24 * 60 * 60 * 1000) {
      state.responsesToday = 0;
      state.dayReset = now;
    }
    
    return (
      state.responsesThisHour < CONFIG.MAX_RESPONSES_PER_HOUR &&
      state.responsesToday < CONFIG.MAX_RESPONSES_PER_DAY
    );
  };

  // ============================================
  // DETECT MESSAGE CATEGORY
  // ============================================
  const detectCategory = (message) => {
    const lower = message.toLowerCase();
    
    for (const [category, keywords] of Object.entries(KEYWORD_CATEGORIES)) {
      for (const keyword of keywords) {
        if (lower.includes(keyword)) {
          return category;
        }
      }
    }
    
    return 'greeting'; // Default to greeting
  };

  // ============================================
  // GET RESPONSE FOR CATEGORY
  // ============================================
  const getResponse = (category, customer, originalMessage) => {
    // Check business hours first
    if (!isBusinessHours()) {
      return CONFIG.BUSINESS_HOURS.outsideHoursMessage
        .replace('{customer}', `@${customer}`)
        .replace('{original}', originalMessage);
    }
    
    const templates = TEMPLATES[category] || TEMPLATES.greeting;
    const template = templates[Math.floor(Math.random() * templates.length)];
    
    return template
      .replace('{customer}', `@${customer}`)
      .replace('{original}', originalMessage);
  };

  // ============================================
  // SEND REPLY
  // ============================================
  const sendReply = async (tweetElement, response) => {
    // Find reply button
    const replyBtn = tweetElement.querySelector('[data-testid="reply"]');
    if (!replyBtn) {
      log('Reply button not found', 'error');
      return false;
    }
    
    await clickElement(replyBtn);
    await sleep(1500);
    
    // Find the reply input
    const input = await waitForElement('[data-testid="tweetTextarea_0"]', 5000);
    if (!input) {
      log('Reply input not found', 'error');
      return false;
    }
    
    // Type the response
    input.focus();
    document.execCommand('insertText', false, response);
    await sleep(500);
    
    // If requiring approval, wait for user confirmation
    if (CONFIG.RESPONSE.requireApproval) {
      log(`📝 Response ready: "${response.substring(0, 50)}..."`, 'info');
      log('Press Enter in the tweet box to send, or Escape to cancel', 'info');
      return true; // User will manually send
    }
    
    // Find and click send button
    const sendBtn = document.querySelector('[data-testid="tweetButton"]');
    if (sendBtn) {
      await clickElement(sendBtn);
      await sleep(1000);
      return true;
    }
    
    return false;
  };

  // ============================================
  // PROCESS MENTION
  // ============================================
  const processMention = async (notification) => {
    const id = notification.getAttribute('data-testid') || 
               notification.querySelector('a[href*="/status/"]')?.href;
    
    if (!id || state.processedIds.has(id)) return;
    
    // Extract customer username and message
    const userLink = notification.querySelector('a[href^="/"]');
    const customer = userLink?.getAttribute('href')?.replace('/', '');
    
    const messageEl = notification.querySelector('[data-testid="tweetText"]');
    const message = messageEl?.textContent || '';
    
    if (!customer || !message) return;
    
    // Skip if it's our own account
    if (customer.toLowerCase() === CONFIG.ACTIVE_ACCOUNT.toLowerCase()) return;
    
    log(`📩 New mention from @${customer}: "${message.substring(0, 50)}..."`, 'info');
    
    // Detect category and get response
    const category = detectCategory(message);
    const response = getResponse(category, customer, message);
    
    log(`Category: ${category}`, 'action');
    log(`Suggested response: ${response}`, 'info');
    
    // Check rate limits
    if (!canRespond()) {
      log('Rate limit reached, skipping response', 'warning');
      return;
    }
    
    // Send response if auto-reply is enabled
    if (CONFIG.RESPONSE.autoReply) {
      await sleep(CONFIG.RESPONSE_DELAY_SECONDS * 1000);
      
      // Click on the notification to open the tweet
      const tweetLink = notification.querySelector('a[href*="/status/"]');
      if (tweetLink) {
        await clickElement(tweetLink);
        await sleep(2000);
        
        // Find the tweet and reply
        const tweet = document.querySelector('[data-testid="tweet"]');
        if (tweet) {
          const sent = await sendReply(tweet, response);
          if (sent) {
            state.responsesThisHour++;
            state.responsesToday++;
            
            // Log interaction
            state.interactions.push({
              timestamp: Date.now(),
              customer,
              message: message.substring(0, 200),
              category,
              response,
              sent: !CONFIG.RESPONSE.requireApproval,
            });
            
            log(`✅ Response ${CONFIG.RESPONSE.requireApproval ? 'prepared' : 'sent'} to @${customer}`, 'success');
          }
        }
        
        // Go back to notifications
        window.history.back();
        await sleep(1500);
      }
    }
    
    state.processedIds.add(id);
    saveState();
  };

  // ============================================
  // CHECK NOTIFICATIONS
  // ============================================
  const checkNotifications = async () => {
    log('📬 Checking notifications...', 'action');
    
    // Navigate to notifications
    window.location.href = 'https://x.com/notifications/mentions';
    await sleep(3000);
    
    // Get notification items
    const notifications = document.querySelectorAll('[data-testid="cellInnerDiv"]');
    
    for (const notification of notifications) {
      if (!state.isRunning) break;
      await processMention(notification);
      await randomDelay(2000, 4000);
    }
  };

  // ============================================
  // MAIN LOOP
  // ============================================
  const run = async () => {
    console.log(`
╔═══════════════════════════════════════════════════════════╗
║  🎧 XActions Customer Service Bot                        ║
╠═══════════════════════════════════════════════════════════╣
║  Active Account: @${CONFIG.ACTIVE_ACCOUNT.padEnd(20)}                ║
║  Accounts Loaded: ${String(ACCOUNTS.length).padEnd(5)}                               ║
║                                                           ║
║  Monitoring:                                              ║
║    Mentions: ${CONFIG.MONITOR.mentions ? '✅' : '❌'}  │  DMs: ${CONFIG.MONITOR.dms ? '✅' : '❌'}  │  Replies: ${CONFIG.MONITOR.replies ? '✅' : '❌'}    ║
║                                                           ║
║  Auto-Reply: ${CONFIG.RESPONSE.autoReply ? '✅' : '❌'}  │  Approval: ${CONFIG.RESPONSE.requireApproval ? '✅' : '❌'}              ║
║  Business Hours: ${CONFIG.BUSINESS_HOURS.enabled ? '✅' : '❌'} (${CONFIG.BUSINESS_HOURS.start}:00-${CONFIG.BUSINESS_HOURS.end}:00)                 ║
║                                                           ║
║  Run stopCS() to stop the bot.                            ║
║  Run csStats() to see statistics.                         ║
╚═══════════════════════════════════════════════════════════╝
    `);

    if (ACCOUNTS.length === 0) {
      log('⚠️ No accounts configured! Add accounts to the ACCOUNTS section.', 'warning');
      return;
    }

    // Initial check
    await checkNotifications();
    
    // Continuous monitoring loop
    while (state.isRunning) {
      await sleep(CONFIG.CHECK_INTERVAL_SECONDS * 1000);
      
      if (!state.isRunning) break;
      
      await checkNotifications();
    }
  };

  // ============================================
  // STATS
  // ============================================
  const printStats = () => {
    const interactions = state.interactions;
    const categories = {};
    
    for (const int of interactions) {
      categories[int.category] = (categories[int.category] || 0) + 1;
    }
    
    console.log(`
╔═══════════════════════════════════════════════════════════╗
║  📊 Customer Service Stats                                ║
╠═══════════════════════════════════════════════════════════╣
║  Total Interactions: ${String(interactions.length).padEnd(5)}                            ║
║  Today: ${String(state.responsesToday).padEnd(5)}  │  This Hour: ${String(state.responsesThisHour).padEnd(5)}             ║
╠───────────────────────────────────────────────────────────╣
║  By Category:                                             ║
${Object.entries(categories).map(([cat, count]) => 
  `║    ${cat.padEnd(15)}: ${String(count).padEnd(5)}                            ║`
).join('\n')}
╚═══════════════════════════════════════════════════════════╝
    `);
    
    // Show recent interactions
    if (interactions.length > 0) {
      console.log('\n📜 Recent Interactions:');
      interactions.slice(-5).forEach(int => {
        console.log(`  @${int.customer} (${int.category}): "${int.message.substring(0, 40)}..."`);
      });
    }
  };

  // ============================================
  // MANUAL RESPONSE HELPER
  // ============================================
  const respond = (category, customMessage) => {
    // For manual use when you want to respond with a specific template
    const templates = TEMPLATES[category];
    if (!templates) {
      console.log('Available categories:', Object.keys(TEMPLATES).join(', '));
      return;
    }
    
    const response = customMessage || templates[Math.floor(Math.random() * templates.length)];
    console.log(`📝 Response: ${response}`);
    
    // Copy to clipboard
    navigator.clipboard.writeText(response);
    console.log('(Copied to clipboard!)');
  };

  run();

  // ============================================
  // EXPORTS
  // ============================================
  window.stopCS = () => {
    state.isRunning = false;
    log('Stopping customer service bot...', 'warning');
  };
  
  window.csStats = printStats;
  window.csRespond = respond;
  window.csTemplates = () => {
    console.log('📋 Available Templates:');
    Object.entries(TEMPLATES).forEach(([cat, temps]) => {
      console.log(`\n${cat.toUpperCase()}:`);
      temps.forEach((t, i) => console.log(`  ${i + 1}. ${t}`));
    });
  };

  window.XActions.CustomerService = {
    state: () => state,
    config: CONFIG,
    templates: TEMPLATES,
    accounts: ACCOUNTS,
    respond,
    printStats,
  };
})();

⚡ More XActions Scripts

Browse 300+ free browser scripts for X/Twitter automation. No API keys, no fees.

Browse All Scripts