// ==UserScript==
// @name Desuarchive Quick Reply + ACK spam
// @namespace -ACK!
// @version 1.2
// @description Toggle Quick Reply with Q, auto-focus textarea, disable Submit for 120s on successful submit, Word filter ACK!
// @author NiggersTongueMyAnus
// @match https://desuarchive.org/*
// @grant none
// ==/UserScript==
(function() {
'use strict';
const style = document.createElement('style');
style.textContent = `
.quick-reply-modal {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: #1d1f21;
border: 2px solid #333;
padding: 15px;
z-index: 10000;
width: 600px;
max-width: 90%;
box-shadow: 0 4px 8px rgba(0,0,0,0.2);
border-radius: 8px;
display: none;
flex-direction: column;
font-family: Arial, sans-serif;
color: #e0e0e0;
box-sizing: border-box;
}
.quick-reply-modal.dragging {
cursor: move;
}
.quick-reply-header {
position: relative;
font-size: 16px;
font-weight: bold;
margin-bottom: 10px;
text-align: center;
background: #2f3136;
padding: 8px;
border-radius: 4px;
color: #e0e0e0;
}
.quick-reply-close {
position: absolute;
right: 8px;
top: 50%;
transform: translateY(-50%);
font-size: 16px;
color: #e0e0e0;
cursor: pointer;
padding: 4px 8px;
border-radius: 4px;
}
.quick-reply-close:hover {
background: #444;
}
.quick-reply-modal .form-row {
display: flex;
gap: 8px;
margin-bottom: 8px;
width: 100%;
box-sizing: border-box;
}
.quick-reply-modal .form-group {
display: flex;
align-items: center;
width: 270px;
max-width: 270px;
box-sizing: border-box;
}
.quick-reply-modal .form-group .input-prepend {
flex: 1;
box-sizing: border-box;
width: 100%;
max-width: 100%;
display: flex;
align-items: center;
background: transparent !important;
}
.quick-reply-modal .form-group label.add-on {
width: 60px;
text-align: right;
margin-right: 8px;
font-weight: bold;
font-size: 14px;
background: #2f3136 !important;
color: #e0e0e0 !important;
vertical-align: top;
border-radius: 0;
padding: 4px;
}
.quick-reply-modal .form-group input.custom-input {
width: 190px !important;
max-width: 190px !important;
min-width: 190px !important;
padding: 4px;
margin-bottom: 0;
font-size: 13px;
border: 1px solid #444;
border-radius: 0 2px 2px 0;
background: #2f3136 !important;
color: #e0e0e0 !important;
box-sizing: border-box;
vertical-align: top;
}
/* Extra specific overrides for each input */
.quick-reply-modal .form-group input#reply_bokunonome.custom-input,
.quick-reply-modal .form-group input#reply_talkingde.custom-input,
.quick-reply-modal .form-group input#reply_elitterae.custom-input,
.quick-reply-modal .form-group input#reply_nymphassword.custom-input {
width: 190px !important;
max-width: 190px !important;
min-width: 190px !important;
background: #2f3136 !important;
color: #e0e0e0 !important;
}
.quick-reply-modal textarea {
width: 100%;
height: 120px;
resize: both;
margin-bottom: 8px;
min-width: 100%;
max-width: 100%;
padding: 4px;
border: 1px solid #444;
border-radius: 4px;
font-size: 13px;
background: #2f3136 !important;
color: #e0e0e0 !important;
box-sizing: border-box;
}
.quick-reply-modal .btn-group {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 8px;
}
.quick-reply-modal .btn {
padding: 6px 12px;
border-radius: 4px;
cursor: pointer;
font-size: 14px;
}
.quick-reply-modal .btn-primary {
background: #007bff;
color: #fff;
border: none;
order: 2;
transition: background 0.2s;
}
.quick-reply-modal .btn-primary:hover {
background: #0056b3;
}
.quick-reply-modal .btn-primary:disabled {
background: #4a4a4a;
cursor: not-allowed;
}
.quick-reply-modal .btn-reset {
background: #6c757d;
color: #fff;
border: none;
order: 1;
}
.quick-reply-modal .btn-reset:hover {
background: #5a6268;
}
.quick-reply-modal .rules {
margin-top: 8px;
font-size: 12px;
color: #e0e0e0;
}
.quick-reply-modal .recaptcha_widget {
margin: 8px auto;
text-align: center;
width: fit-content;
}
.quick-reply-modal #reply_ajax_notices {
margin-top: 8px;
text-align: center;
font-size: 14px;
color: #e0e0e0;
}
#reply.thread_form_wrap.clearfix {
display: none !important;
}
/* Override site styles */
.input-prepend input,
.input-prepend .custom-input,
.input-prepend .add-on {
background: #2f3136 !important;
color: #e0e0e0 !important;
border: 1px solid #444 !important;
}
`;
document.head.appendChild(style);
// Find the Quick Reply form
const form = document.querySelector('form fieldset');
if (!form) {
console.log('Quick Reply form not found');
return;
}
const textarea = form.querySelector('#reply_chennodiscursus').parentElement;
const btnGroup = form.querySelector('.btn-group');
const rules = form.querySelector('.rules');
const recaptcha = form.querySelector('.recaptcha_widget');
const notices = form.querySelector('#reply_ajax_notices');
// Replace original elements
function replaceInput(originalInput, id, name, type, value = '') {
const newInput = document.createElement('input');
newInput.className = 'custom-input';
newInput.id = id;
newInput.name = name;
newInput.type = type;
newInput.value = value;
if (id === 'reply_nymphassword') {
newInput.required = true;
}
originalInput.replaceWith(newInput);
return newInput.parentElement;
}
const newNameInput = replaceInput(
form.querySelector('#reply_bokunonome'),
'reply_bokunonome',
'reply_bokunonome',
'text',
''
);
const newSubjectInput = replaceInput(
form.querySelector('#reply_talkingde'),
'reply_talkingde',
'reply_talkingde',
'text',
''
);
const newEmailInput = replaceInput(
form.querySelector('#reply_elitterae'),
'reply_elitterae',
'reply_elitterae',
'text',
''
);
const newPasswordInput = replaceInput(
form.querySelector('#reply_nymphassword'),
'reply_nymphassword',
'reply_nymphassword',
'password',
'DDkr0BH'
);
// Create modal
const modal = document.createElement('div');
modal.className = 'quick-reply-modal';
const header = document.createElement('div');
header.className = 'quick-reply-header';
header.textContent = 'Quick Reply';
// Close button
const closeButton = document.createElement('span');
closeButton.className = 'quick-reply-close';
closeButton.textContent = '×';
closeButton.addEventListener('click', () => {
modal.style.display = 'none';
});
header.appendChild(closeButton);
modal.appendChild(header);
const row1 = document.createElement('div');
row1.className = 'form-row';
row1.appendChild(newNameInput);
row1.appendChild(newSubjectInput);
modal.appendChild(row1);
const row2 = document.createElement('div');
row2.className = 'form-row';
row2.appendChild(newEmailInput);
row2.appendChild(newPasswordInput);
modal.appendChild(row2);
modal.appendChild(textarea);
const resetBtn = btnGroup.querySelector('input[name="reset"]');
const submitBtn = btnGroup.querySelector('input[name="reply_gattai"]');
resetBtn.classList.add('btn', 'btn-reset');
submitBtn.classList.add('btn', 'btn-primary');
modal.appendChild(btnGroup);
if (rules) modal.appendChild(rules);
if (recaptcha) modal.appendChild(recaptcha);
if (notices) modal.appendChild(notices);
document.body.appendChild(modal);
// Auto-focus main input
function toggleModal() {
const isDisplayed = modal.style.display === 'flex';
modal.style.display = isDisplayed ? 'none' : 'flex';
if (!isDisplayed) {
const textareaElement = modal.querySelector('#reply_chennodiscursus');
if (textareaElement) {
textareaElement.focus();
textareaElement.setSelectionRange(0, 0);
}
}
}
function isFocusInModal() {
const activeElement = document.activeElement;
return modal.contains(activeElement);
}
// "q" key to bring the modal
document.addEventListener('keydown', (e) => {
if (e.key.toLowerCase() === 'q') {
if (!isFocusInModal() || modal.style.display !== 'flex') {
e.preventDefault(); // Prevent 'q' when opening modal to autofocus
toggleModal();
}
// Allow 'q'
}
});
// Disable Submit button for 120 seconds
function disableSubmit() {
//console.log('Disabling Submit button for 120 seconds');
submitBtn.disabled = true;
setTimeout(() => {
submitBtn.disabled = false;
//console.log('Submit button re-enabled');
}, 120000); // 120 seconds
}
// Detect successful submission via notices
const observer = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
if (notices) {
console.log('Notices changed:', notices.textContent);
if (notices.textContent.includes('Message sent.')) {
//console.log('Detected "Message sent."');
disableSubmit();
}
}
});
});
if (notices) {
observer.observe(notices, { childList: true, characterData: true, subtree: true });
//console.log('MutationObserver set up for #reply_ajax_notices');
} else {
//console.log('Notices element not found');
}
// Word filter for posts and their replacement
const targetWords = ['Akemi', 'Ack', '!Akemi', 'ack', 'ACK'];
const replacementPhrases = ['I love you', 'Marry me', 'Steamy night!', 'Have SEX with me', '*kisses you*', 'I KNEEL'];
function getRandomReplacement() {
return replacementPhrases[Math.floor(Math.random() * replacementPhrases.length)];
}
function filterTextNode(textNode) {
if (!textNode.nodeValue || !textNode.parentNode) return;
let text = textNode.nodeValue;
let modified = false;
targetWords.forEach((word) => {
// Regex to match whole words/phrases, escaping special characters
const regex = new RegExp(`\\b${word.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&')}\\b`, 'g');
if (regex.test(text)) {
text = text.replace(regex, getRandomReplacement());
modified = true;
}
});
if (modified) {
textNode.nodeValue = text;
//console.log('Filtered text node:', text);
}
}
function filterPostText(postTextElement) {
if (!postTextElement) return;
function processNode(node) {
// Skip greentext spans with backlinks
if (node.nodeType === Node.ELEMENT_NODE && node.classList.contains('greentext')) {
if (node.querySelector('a.backlink')) {
return; // Skip backlink greentext
}
}
if (node.nodeType === Node.TEXT_NODE) {
filterTextNode(node);
} else if (node.nodeType === Node.ELEMENT_NODE || node.nodeType === Node.DOCUMENT_FRAGMENT_NODE) {
node.childNodes.forEach(processNode);
}
}
processNode(postTextElement);
}
// Apply filter to existing posts
function applyFilterToPosts() {
const postWrappers = document.querySelectorAll('.post_wrapper');
postWrappers.forEach((wrapper) => {
const postText = wrapper.querySelector('.text');
if (postText) {
filterPostText(postText);
}
});
//console.log(`Applied filter to ${postWrappers.length} posts`);
}
// Initial filter application
applyFilterToPosts();
// Observe for new posts to filter
const postObserver = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
if (mutation.addedNodes.length) {
mutation.addedNodes.forEach((node) => {
if (node.nodeType === Node.ELEMENT_NODE) {
if (node.matches('.post_wrapper')) {
const postText = node.querySelector('.text');
if (postText) {
filterPostText(postText);
}
} else {
const wrappers = node.querySelectorAll('.post_wrapper');
wrappers.forEach((wrapper) => {
const postText = wrapper.querySelector('.text');
if (postText) {
filterPostText(postText);
}
});
}
}
});
}
});
});
postObserver.observe(document.body, {
childList: true,
subtree: true
});
//console.log('Post MutationObserver set up for .post_wrapper');
// Draggable
let isDragging = false;
let currentX;
let currentY;
let initialX;
let initialY;
header.addEventListener('mousedown', (e) => {
if (e.target === closeButton) return;
initialX = e.clientX - currentX;
initialY = e.clientY - currentY;
isDragging = true;
modal.classList.add('dragging');
});
document.addEventListener('mousemove', (e) => {
if (isDragging) {
e.preventDefault();
currentX = e.clientX - initialX;
currentY = e.clientY - initialY;
modal.style.left = `calc(50% + ${currentX}px)`;
modal.style.top = `calc(50% + ${currentY}px)`;
modal.style.transform = 'none';
}
});
document.addEventListener('mouseup', () => {
isDragging = false;
modal.classList.remove('dragging');
});
currentX = 0;
currentY = 0;
})();