// ==UserScript==
// @name YouTube "damn is 😂" Button
// @namespace http://tampermonkey.net/
// @version 1.0
// @description Adds a "damn is 😂" button to YouTube comments
// @author Claude
// @match https://www.youtube.com/*
// @grant none
// @run-at document-idle
// ==/UserScript==
(function() {
'use strict';
// Set debug mode to true for more console logging
const DEBUG = true;
function log(...args) {
if (DEBUG) {
console.log('[damn-is-button]', ...args);
}
}
// Function to generate random emoji string
function generateRandomEmojiString() {
const emojis = ["😂", "❤️", "🎉"];
const length = 5 + Math.floor(Math.random() * 46); // Between 5 and 50
let result = "";
for (let i = 0; i < length; i++) {
result += emojis[Math.floor(Math.random() * emojis.length)];
}
return "damn is " + result;
}
// Function to process a single toolbar
function processToolbar(toolbar) {
// Skip if already processed
if (toolbar.querySelector('.damn-is-button')) {
return;
}
// Create a simple button
const damnIsButton = document.createElement('button');
damnIsButton.className = 'damn-is-button';
damnIsButton.textContent = 'damn is 😂';
damnIsButton.title = 'Add a "damn is 😂" comment';
// Style the button to match YouTube's design
damnIsButton.style.marginLeft = '8px';
damnIsButton.style.border = 'none';
damnIsButton.style.borderRadius = '18px';
damnIsButton.style.padding = '0 16px';
damnIsButton.style.height = '36px';
damnIsButton.style.cursor = 'pointer';
damnIsButton.style.fontSize = '14px';
damnIsButton.style.fontFamily = 'Roboto, Arial, sans-serif';
damnIsButton.style.color = 'var(--yt-spec-text-primary, #0f0f0f)';
damnIsButton.style.backgroundColor = 'var(--yt-spec-badge-chip-background, rgba(0, 0, 0, 0.05))';
// Add hover effects
damnIsButton.addEventListener('mouseover', function() {
this.style.backgroundColor = 'var(--yt-spec-10-percent-layer, rgba(0, 0, 0, 0.1))';
});
damnIsButton.addEventListener('mouseout', function() {
this.style.backgroundColor = 'var(--yt-spec-badge-chip-background, rgba(0, 0, 0, 0.05))';
});
// Add click event
damnIsButton.addEventListener('click', function() {
log('Button clicked!');
// Step 1: Click the reply button
const replyButton = toolbar.querySelector('#reply-button-end>yt-button-shape>button');
if (replyButton) {
replyButton.click();
log('Clicked reply button');
} else {
log('Reply button not found');
return;
}
// Step 2: Wait for the input field to appear
setTimeout(() => {
// Find the input field
const commentContainer = toolbar.closest('ytd-comment-thread-renderer');
if (!commentContainer) {
log('Comment container not found');
return;
}
// Try different selectors for the input field
const possibleSelectors = [
'#contenteditable-root',
'div[contenteditable="true"]',
'#commentbox div[contenteditable="true"]',
'#reply-dialog div[contenteditable="true"]'
];
let inputField = null;
// Try selectors on the comment container first
for (const selector of possibleSelectors) {
inputField = commentContainer.querySelector(selector);
if (inputField) {
log('Found input field with selector:', selector);
break;
}
}
// If not found, try document-wide
if (!inputField) {
for (const selector of possibleSelectors) {
inputField = document.querySelector(selector);
if (inputField) {
log('Found input field in document with selector:', selector);
break;
}
}
}
if (!inputField) {
log('Input field not found');
return;
}
// Generate and set the text
const randomText = generateRandomEmojiString();
log('Setting text:', randomText);
inputField.textContent = randomText;
inputField.dispatchEvent(new Event('input', { bubbles: true }));
// Step 3: Wait for the submit button to become enabled
const trySubmit = (attempts = 0) => {
if (attempts > 10) {
log('Max submit attempts reached');
return;
}
// Try different selectors for the submit button
const submitSelectors = [
'#submit-button button',
'ytd-button-renderer[id="submit-button"] button',
'#reply-dialog #submit-button button'
];
let submitButton = null;
// Try selectors on the comment container first
for (const selector of submitSelectors) {
submitButton = commentContainer.querySelector(selector);
if (submitButton && !submitButton.disabled) {
log('Found enabled submit button with selector:', selector);
submitButton.click();
log('Clicked submit button');
return;
}
}
// If not found, try document-wide
for (const selector of submitSelectors) {
submitButton = document.querySelector(selector);
if (submitButton && !submitButton.disabled) {
log('Found enabled submit button in document with selector:', selector);
submitButton.click();
log('Clicked submit button');
return;
}
}
// Not found or disabled, try again
log(`Submit button not found or disabled, attempt ${attempts + 1}/10`);
setTimeout(() => trySubmit(attempts + 1), 300);
};
// Start trying to submit
setTimeout(() => trySubmit(), 500);
}, 500); // Wait for reply box to appear
});
// Append the button to the toolbar
toolbar.appendChild(damnIsButton);
log('Added button to toolbar');
}
// Function to process all comments
function processComments() {
const toolbars = document.querySelectorAll('ytd-comment-engagement-bar > #toolbar');
log(`Found ${toolbars.length} comment toolbars`);
toolbars.forEach(toolbar => {
processToolbar(toolbar);
});
}
// Set up MutationObserver to detect new comments
function setupObserver() {
const commentsSection = document.querySelector('ytd-comments, #comments');
if (!commentsSection) {
log('Comments section not found, will retry in 1s');
setTimeout(setupObserver, 1000);
return;
}
log('Comments section found, setting up observer');
// Process existing comments
processComments();
// Create observer for new comments
const observer = new MutationObserver(mutations => {
let shouldProcess = false;
for (const mutation of mutations) {
if (mutation.addedNodes.length) {
shouldProcess = true;
break;
}
}
if (shouldProcess) {
if (observer.timeout) {
clearTimeout(observer.timeout);
}
observer.timeout = setTimeout(() => {
log('New content detected, processing comments');
processComments();
observer.timeout = null;
}, 500);
}
});
// Start observing
observer.observe(commentsSection, {
childList: true,
subtree: true
});
// Also run periodically
setInterval(processComments, 5000);
log('Observer set up successfully');
}
// Initialize when DOM is ready
log('Script loaded, waiting for page to fully load');
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', setupObserver);
} else {
setupObserver();
}
// Also run on page navigation (for SPA behavior)
let lastUrl = location.href;
const urlObserver = new MutationObserver(() => {
if (location.href !== lastUrl) {
lastUrl = location.href;
log('URL changed, reinitializing');
setTimeout(setupObserver, 2000);
}
});
if (document.querySelector('head > title')) {
urlObserver.observe(document.querySelector('head > title'), { subtree: true, childList: true });
}
})();